Basic moving
This commit is contained in:
parent
1dbd235826
commit
93e6f92e42
|
@ -1,10 +1,10 @@
|
|||
use rayon::prelude::*;
|
||||
use slint::{ComponentHandle, Model, ModelRc, VecModel};
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
use slint::{ComponentHandle, ModelRc, VecModel};
|
||||
|
||||
use czkawka_core::common::remove_folder_if_contains_only_empty_folders;
|
||||
|
||||
use crate::common::{get_is_header_mode, get_str_name_idx, get_str_path_idx, get_tool_model, set_tool_model};
|
||||
use crate::common::{get_is_header_mode, get_tool_model, set_tool_model};
|
||||
use crate::model_operations::{collect_full_path_from_model, deselect_all_items, filter_out_checked_items};
|
||||
use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow};
|
||||
|
||||
pub fn connect_delete_button(app: &MainWindow) {
|
||||
|
@ -30,7 +30,8 @@ fn handle_delete_items(items: &ModelRc<MainListModel>, active_tab: CurrentTab) -
|
|||
let (entries_to_delete, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab));
|
||||
|
||||
if !entries_to_delete.is_empty() {
|
||||
remove_selected_items(entries_to_delete, active_tab);
|
||||
let vec_items_to_remove = collect_full_path_from_model(&entries_to_delete, active_tab);
|
||||
remove_selected_items(vec_items_to_remove, active_tab);
|
||||
deselect_all_items(&mut entries_left);
|
||||
|
||||
let r = ModelRc::new(VecModel::from(entries_left)); // TODO here maybe should also stay old model if entries cannot be removed
|
||||
|
@ -43,18 +44,7 @@ fn handle_delete_items(items: &ModelRc<MainListModel>, active_tab: CurrentTab) -
|
|||
// For empty folders double check if folders are really empty - this function probably should be run in thread
|
||||
// and at the end should be send signal to main thread to update model
|
||||
// TODO handle also situations where cannot delete file/folder
|
||||
fn remove_selected_items(items: Vec<MainListModel>, active_tab: CurrentTab) {
|
||||
let path_idx = get_str_path_idx(active_tab);
|
||||
let name_idx = get_str_name_idx(active_tab);
|
||||
let items_to_remove = items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
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::<Vec<_>>();
|
||||
|
||||
fn remove_selected_items(items_to_remove: Vec<String>, active_tab: CurrentTab) {
|
||||
// Iterate over empty folders and not delete them if they are not empty
|
||||
if active_tab == CurrentTab::EmptyFolders {
|
||||
items_to_remove.into_par_iter().for_each(|item| {
|
||||
|
@ -66,202 +56,3 @@ fn remove_selected_items(items: Vec<MainListModel>, active_tab: CurrentTab) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn deselect_all_items(items: &mut [MainListModel]) {
|
||||
for item in items {
|
||||
item.selected_row = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_out_checked_items(items: &ModelRc<MainListModel>, have_header: bool) -> (Vec<MainListModel>, Vec<MainListModel>) {
|
||||
if cfg!(debug_assertions) {
|
||||
check_if_header_is_checked(items);
|
||||
check_if_header_is_selected_but_should_not_be(items, have_header);
|
||||
}
|
||||
|
||||
let (entries_to_delete, mut entries_left): (Vec<_>, Vec<_>) = items.iter().partition(|item| item.checked);
|
||||
|
||||
// When have header, we must also throw out orphaned items - this needs to be
|
||||
if have_header && !entries_left.is_empty() {
|
||||
// First row must be header
|
||||
assert!(entries_left[0].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();
|
||||
}
|
||||
} else if entries_left.len() < 3 {
|
||||
// Not have enough items to fill model
|
||||
entries_left = Vec::new();
|
||||
} else {
|
||||
let mut last_header = 0;
|
||||
let mut new_items: Vec<MainListModel> = Vec::new();
|
||||
for i in 1..entries_left.len() {
|
||||
if entries_left[i].header_row {
|
||||
if i - last_header > 2 {
|
||||
new_items.extend(entries_left[last_header..i].iter().cloned());
|
||||
}
|
||||
last_header = i;
|
||||
}
|
||||
}
|
||||
if entries_left.len() - last_header > 2 {
|
||||
new_items.extend(entries_left[last_header..].iter().cloned());
|
||||
}
|
||||
|
||||
entries_left = new_items;
|
||||
}
|
||||
}
|
||||
|
||||
(entries_to_delete, entries_left)
|
||||
}
|
||||
|
||||
// Function to verify if really headers are not checked
|
||||
// Checked header is big bug
|
||||
fn check_if_header_is_checked(items: &ModelRc<MainListModel>) {
|
||||
if cfg!(debug_assertions) {
|
||||
for item in items.iter() {
|
||||
if item.header_row {
|
||||
assert!(!item.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In some modes header should not be visible, but if are, then it is a bug
|
||||
fn check_if_header_is_selected_but_should_not_be(items: &ModelRc<MainListModel>, can_have_header: bool) {
|
||||
if cfg!(debug_assertions) {
|
||||
if !can_have_header {
|
||||
for item in items.iter() {
|
||||
assert!(!item.header_row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use slint::{Model, ModelRc, SharedString, VecModel};
|
||||
|
||||
use crate::connect_delete::filter_out_checked_items;
|
||||
use crate::MainListModel;
|
||||
|
||||
#[test]
|
||||
fn test_filter_out_checked_items_empty() {
|
||||
let items: ModelRc<MainListModel> = create_new_model(vec![]);
|
||||
|
||||
let (to_delete, left) = filter_out_checked_items(&items, false);
|
||||
assert!(to_delete.is_empty());
|
||||
assert!(left.is_empty());
|
||||
let (to_delete, left) = filter_out_checked_items(&items, true);
|
||||
assert!(to_delete.is_empty());
|
||||
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![])]);
|
||||
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![])]);
|
||||
let (to_delete, left) = filter_out_checked_items(&items, true);
|
||||
assert!(to_delete.is_empty());
|
||||
assert!(left.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_filter_out_checked_items_one_element_invalid_normal() {
|
||||
let items = create_new_model(vec![(false, 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![])]);
|
||||
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);
|
||||
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"]);
|
||||
}
|
||||
|
||||
#[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"]),
|
||||
]);
|
||||
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"]);
|
||||
}
|
||||
|
||||
#[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"]),
|
||||
]);
|
||||
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"]);
|
||||
}
|
||||
|
||||
fn get_single_data_str_from_model(model: &[MainListModel]) -> Vec<String> {
|
||||
let mut d = model.iter().map(|item| item.val_str.iter().next().unwrap().to_string()).collect::<Vec<_>>();
|
||||
d.sort();
|
||||
d
|
||||
}
|
||||
|
||||
fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc<MainListModel> {
|
||||
let model = VecModel::default();
|
||||
for item in items {
|
||||
let all_items: Vec<SharedString> = item.3.iter().map(|item| (*item).into()).collect::<Vec<_>>();
|
||||
let all_items = VecModel::from(all_items);
|
||||
model.push(MainListModel {
|
||||
checked: item.0,
|
||||
header_row: item.1,
|
||||
selected_row: item.2,
|
||||
val_str: ModelRc::new(all_items),
|
||||
val_int: ModelRc::new(VecModel::default()),
|
||||
});
|
||||
}
|
||||
ModelRc::new(model)
|
||||
}
|
||||
}
|
||||
|
|
106
krokiet/src/connect_move.rs
Normal file
106
krokiet/src/connect_move.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use crate::common::{get_is_header_mode, get_tool_model};
|
||||
use crate::model_operations::{collect_path_name_from_model, deselect_all_items, filter_out_checked_items};
|
||||
use crate::CurrentTab;
|
||||
use crate::{Callabler, GuiState, MainListModel, MainWindow};
|
||||
|
||||
use rayon::prelude::*;
|
||||
use slint::{ComponentHandle, ModelRc, VecModel};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fs, path};
|
||||
|
||||
pub fn connect_move(app: &MainWindow) {
|
||||
let a = app.as_weak();
|
||||
// app.global::<Callabler>().on_move_items(move |select_mode| {
|
||||
// let app = a.upgrade().unwrap();
|
||||
// let active_tab = app.global::<GuiState>().get_active_tab();
|
||||
// let current_model = get_tool_model(&app, active_tab);
|
||||
//
|
||||
// // If tree structure will be
|
||||
// let preserve_structure = false;
|
||||
// let copy_mode = true;
|
||||
// let output_folder = "/home/rafal/Downloads/AAAAAAAA";
|
||||
// move_operation(¤t_model, preserve_structure, copy_mode, output_folder, active_tab);
|
||||
// });
|
||||
// move_selected_items(vec![("/home/rafal/".to_string(), "Other.png".to_string())], true, true, "/home/rafal/Downloads/AAAAAAAA");
|
||||
}
|
||||
|
||||
fn move_operation(items: &ModelRc<MainListModel>, preserve_structure: bool, copy_mode: bool, output_folder: &str, active_tab: CurrentTab) -> Option<ModelRc<MainListModel>> {
|
||||
let (entries_to_move, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab));
|
||||
|
||||
if !entries_to_move.is_empty() {
|
||||
let vec_items_to_move = collect_path_name_from_model(&entries_to_move, active_tab);
|
||||
move_selected_items(vec_items_to_move, preserve_structure, copy_mode, output_folder);
|
||||
deselect_all_items(&mut entries_left);
|
||||
|
||||
let r = ModelRc::new(VecModel::from(entries_left));
|
||||
return Some(r);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn move_selected_items(items_to_move: Vec<(String, String)>, preserve_structure: bool, copy_mode: bool, output_folder: &str) -> Vec<String> {
|
||||
if let Err(err) = fs::create_dir_all(output_folder) {
|
||||
return vec![format!("Error while creating folder: {err}")];
|
||||
}
|
||||
|
||||
if copy_mode {
|
||||
items_to_move
|
||||
.into_par_iter()
|
||||
.filter_map(|(path, name)| {
|
||||
let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure);
|
||||
|
||||
if let Err(e) = fs::copy(&input_file, &output_file) {
|
||||
return Some(format!("Error while copying file {input_file:?} to {output_file:?}, reason {e}"));
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
items_to_move
|
||||
.into_par_iter()
|
||||
.filter_map(|(path, name)| {
|
||||
let (input_file, output_file) = collect_path_and_create_folders(&path, &name, output_folder, preserve_structure);
|
||||
|
||||
if output_file.exists() {
|
||||
return Some(format!("File {output_file:?} already exists"));
|
||||
}
|
||||
|
||||
// Try to rename file, may fail due various reasons
|
||||
if fs::rename(&input_file, &output_file).is_ok() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// It is possible that this failed, because file is on different partition, so
|
||||
// we need to copy file and then remove old
|
||||
if let Err(e) = fs::copy(&input_file, &output_file) {
|
||||
return Some(format!(
|
||||
"Error while copying file {input_file:?} to {output_file:?}(moving into different partition), reason {e}"
|
||||
));
|
||||
}
|
||||
if let Err(e) = fs::remove_file(&input_file) {
|
||||
return Some(format!("Error while removing file {input_file:?}, reason {e}"));
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Create input/output paths, and create output folder
|
||||
fn collect_path_and_create_folders(input_path: &str, input_file: &str, output_path: &str, preserve_structure: bool) -> (PathBuf, PathBuf) {
|
||||
let mut input_full_path = PathBuf::from(input_path);
|
||||
input_full_path.push(input_file);
|
||||
|
||||
let mut output_full_path = PathBuf::from(output_path);
|
||||
if preserve_structure {
|
||||
output_full_path.extend(Path::new(input_path).components().filter(|c| match c {
|
||||
path::Component::Normal(_) => true,
|
||||
_ => false,
|
||||
}));
|
||||
};
|
||||
let _ = fs::create_dir_all(&output_full_path);
|
||||
output_full_path.push(input_file);
|
||||
|
||||
(input_full_path, output_full_path)
|
||||
}
|
|
@ -27,6 +27,7 @@ use czkawka_core::common_dir_traversal::ProgressData;
|
|||
|
||||
use crate::connect_delete::connect_delete_button;
|
||||
use crate::connect_directories_changes::connect_add_remove_directories;
|
||||
use crate::connect_move::connect_move;
|
||||
use crate::connect_open::connect_open_items;
|
||||
use crate::connect_progress_receiver::connect_progress_gathering;
|
||||
use crate::connect_scan::connect_scan_button;
|
||||
|
@ -40,6 +41,7 @@ use crate::settings::{connect_changing_settings_preset, create_default_settings_
|
|||
mod common;
|
||||
mod connect_delete;
|
||||
mod connect_directories_changes;
|
||||
mod connect_move;
|
||||
mod connect_open;
|
||||
mod connect_progress_receiver;
|
||||
mod connect_scan;
|
||||
|
@ -48,13 +50,10 @@ mod connect_show_preview;
|
|||
mod connect_stop;
|
||||
mod connect_translation;
|
||||
mod localizer_krokiet;
|
||||
mod model_operations;
|
||||
mod set_initial_gui_info;
|
||||
mod settings;
|
||||
|
||||
// use std::rc::Rc;
|
||||
|
||||
// use slint::{ModelRc, VecModel};
|
||||
|
||||
slint::include_modules!();
|
||||
fn main() {
|
||||
setup_logger(false);
|
||||
|
@ -87,6 +86,7 @@ fn main() {
|
|||
connect_changing_settings_preset(&app);
|
||||
connect_select(&app);
|
||||
connect_showing_proper_select_buttons(&app);
|
||||
connect_move(&app);
|
||||
|
||||
app.run().unwrap();
|
||||
|
||||
|
|
236
krokiet/src/model_operations.rs
Normal file
236
krokiet/src/model_operations.rs
Normal file
|
@ -0,0 +1,236 @@
|
|||
use crate::common::{get_str_name_idx, get_str_path_idx};
|
||||
use crate::{CurrentTab, MainListModel};
|
||||
use slint::{Model, ModelRc};
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
|
||||
pub fn deselect_all_items(items: &mut [MainListModel]) {
|
||||
for item in items {
|
||||
item.selected_row = false;
|
||||
}
|
||||
}
|
||||
pub fn select_all_items(items: &mut [MainListModel]) {
|
||||
for item in items {
|
||||
item.selected_row = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_full_path_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec<String> {
|
||||
let path_idx = get_str_path_idx(active_tab);
|
||||
let name_idx = get_str_name_idx(active_tab);
|
||||
items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
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::<Vec<_>>()
|
||||
}
|
||||
pub fn collect_path_name_from_model(items: &[MainListModel], active_tab: CurrentTab) -> Vec<(String, String)> {
|
||||
let path_idx = get_str_path_idx(active_tab);
|
||||
let name_idx = get_str_name_idx(active_tab);
|
||||
items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
dbg!(item.val_str.iter().nth(path_idx).unwrap().to_string());
|
||||
dbg!(item.val_str.iter().nth(name_idx).unwrap().to_string());
|
||||
(
|
||||
item.val_str.iter().nth(path_idx).unwrap().to_string(),
|
||||
item.val_str.iter().nth(name_idx).unwrap().to_string(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub fn filter_out_checked_items(items: &ModelRc<MainListModel>, have_header: bool) -> (Vec<MainListModel>, Vec<MainListModel>) {
|
||||
if cfg!(debug_assertions) {
|
||||
check_if_header_is_checked(items);
|
||||
check_if_header_is_selected_but_should_not_be(items, have_header);
|
||||
}
|
||||
|
||||
let (entries_to_delete, mut entries_left): (Vec<_>, Vec<_>) = items.iter().partition(|item| item.checked);
|
||||
|
||||
// When have header, we must also throw out orphaned items - this needs to be
|
||||
if have_header && !entries_left.is_empty() {
|
||||
// First row must be header
|
||||
assert!(entries_left[0].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();
|
||||
}
|
||||
} else if entries_left.len() < 3 {
|
||||
// Not have enough items to fill model
|
||||
entries_left = Vec::new();
|
||||
} else {
|
||||
let mut last_header = 0;
|
||||
let mut new_items: Vec<MainListModel> = Vec::new();
|
||||
for i in 1..entries_left.len() {
|
||||
if entries_left[i].header_row {
|
||||
if i - last_header > 2 {
|
||||
new_items.extend(entries_left[last_header..i].iter().cloned());
|
||||
}
|
||||
last_header = i;
|
||||
}
|
||||
}
|
||||
if entries_left.len() - last_header > 2 {
|
||||
new_items.extend(entries_left[last_header..].iter().cloned());
|
||||
}
|
||||
|
||||
entries_left = new_items;
|
||||
}
|
||||
}
|
||||
|
||||
(entries_to_delete, entries_left)
|
||||
}
|
||||
|
||||
// Function to verify if really headers are not checked
|
||||
// Checked header is big bug
|
||||
fn check_if_header_is_checked(items: &ModelRc<MainListModel>) {
|
||||
if cfg!(debug_assertions) {
|
||||
for item in items.iter() {
|
||||
if item.header_row {
|
||||
assert!(!item.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In some modes header should not be visible, but if are, then it is a bug
|
||||
fn check_if_header_is_selected_but_should_not_be(items: &ModelRc<MainListModel>, can_have_header: bool) {
|
||||
if cfg!(debug_assertions) {
|
||||
if !can_have_header {
|
||||
for item in items.iter() {
|
||||
assert!(!item.header_row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use slint::{Model, ModelRc, SharedString, VecModel};
|
||||
|
||||
use crate::model_operations::filter_out_checked_items;
|
||||
use crate::MainListModel;
|
||||
|
||||
#[test]
|
||||
fn test_filter_out_checked_items_empty() {
|
||||
let items: ModelRc<MainListModel> = create_new_model(vec![]);
|
||||
|
||||
let (to_delete, left) = filter_out_checked_items(&items, false);
|
||||
assert!(to_delete.is_empty());
|
||||
assert!(left.is_empty());
|
||||
let (to_delete, left) = filter_out_checked_items(&items, true);
|
||||
assert!(to_delete.is_empty());
|
||||
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![])]);
|
||||
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![])]);
|
||||
let (to_delete, left) = filter_out_checked_items(&items, true);
|
||||
assert!(to_delete.is_empty());
|
||||
assert!(left.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_filter_out_checked_items_one_element_invalid_normal() {
|
||||
let items = create_new_model(vec![(false, 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![])]);
|
||||
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);
|
||||
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"]);
|
||||
}
|
||||
|
||||
#[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"]),
|
||||
]);
|
||||
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"]);
|
||||
}
|
||||
|
||||
#[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"]),
|
||||
]);
|
||||
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"]);
|
||||
}
|
||||
|
||||
fn get_single_data_str_from_model(model: &[MainListModel]) -> Vec<String> {
|
||||
let mut d = model.iter().map(|item| item.val_str.iter().next().unwrap().to_string()).collect::<Vec<_>>();
|
||||
d.sort();
|
||||
d
|
||||
}
|
||||
|
||||
fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc<MainListModel> {
|
||||
let model = VecModel::default();
|
||||
for item in items {
|
||||
let all_items: Vec<SharedString> = item.3.iter().map(|item| (*item).into()).collect::<Vec<_>>();
|
||||
let all_items = VecModel::from(all_items);
|
||||
model.push(MainListModel {
|
||||
checked: item.0,
|
||||
header_row: item.1,
|
||||
selected_row: item.2,
|
||||
val_str: ModelRc::new(all_items),
|
||||
val_int: ModelRc::new(VecModel::default()),
|
||||
});
|
||||
}
|
||||
ModelRc::new(model)
|
||||
}
|
||||
}
|
|
@ -58,6 +58,15 @@ export component ActionButtons inherits HorizontalLayout {
|
|||
horizontal-stretch: 0.5;
|
||||
}
|
||||
|
||||
move_button := Button {
|
||||
height: parent.height;
|
||||
enabled: !scanning && lists_enabled;
|
||||
text: "Move";
|
||||
clicked => {
|
||||
// show_move_popup(self.x + self.width / 2, self.y + parent.y);
|
||||
}
|
||||
}
|
||||
|
||||
select_button := Button {
|
||||
height: parent.height;
|
||||
enabled: !scanning && lists_enabled;
|
||||
|
|
|
@ -21,7 +21,6 @@ export component PopupSelectResults inherits Rectangle {
|
|||
out property <length> all_items_height: item_height * model.length;
|
||||
|
||||
popup_window := PopupWindow {
|
||||
|
||||
width: item_width;
|
||||
height: all_items_height;
|
||||
|
||||
|
|
Loading…
Reference in a new issue