Actions
This commit is contained in:
parent
473375ad5f
commit
dfb89b2f5e
|
@ -26,6 +26,12 @@ pub struct FolderEntry {
|
||||||
pub modified_date: u64,
|
pub modified_date: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FolderEntry {
|
||||||
|
pub fn get_modified_date(&self) -> u64 {
|
||||||
|
self.modified_date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EmptyFolder {
|
pub struct EmptyFolder {
|
||||||
common_data: CommonToolData,
|
common_data: CommonToolData,
|
||||||
information: Info,
|
information: Info,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{CurrentTab, ExcludedDirectoriesModel, IncludedDirectoriesModel, Main
|
||||||
use slint::{ModelRc, SharedString, VecModel};
|
use slint::{ModelRc, SharedString, VecModel};
|
||||||
|
|
||||||
// Remember to match updated this according to ui/main_lists.slint and connect_scan.rs files
|
// Remember to match updated this according to ui/main_lists.slint and connect_scan.rs files
|
||||||
pub fn get_path_idx(active_tab: CurrentTab) -> usize {
|
pub fn get_str_path_idx(active_tab: CurrentTab) -> usize {
|
||||||
match active_tab {
|
match active_tab {
|
||||||
CurrentTab::EmptyFolders => 1,
|
CurrentTab::EmptyFolders => 1,
|
||||||
CurrentTab::EmptyFiles => 1,
|
CurrentTab::EmptyFiles => 1,
|
||||||
|
@ -12,7 +12,7 @@ pub fn get_path_idx(active_tab: CurrentTab) -> usize {
|
||||||
CurrentTab::Settings => panic!("Button should be disabled"),
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_name_idx(active_tab: CurrentTab) -> usize {
|
pub fn get_str_name_idx(active_tab: CurrentTab) -> usize {
|
||||||
match active_tab {
|
match active_tab {
|
||||||
CurrentTab::EmptyFolders => 0,
|
CurrentTab::EmptyFolders => 0,
|
||||||
CurrentTab::EmptyFiles => 0,
|
CurrentTab::EmptyFiles => 0,
|
||||||
|
@ -20,6 +20,39 @@ pub fn get_name_idx(active_tab: CurrentTab) -> usize {
|
||||||
CurrentTab::Settings => panic!("Button should be disabled"),
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_int_modification_date_idx(active_tab: CurrentTab) -> usize {
|
||||||
|
match active_tab {
|
||||||
|
CurrentTab::EmptyFiles => 0,
|
||||||
|
CurrentTab::SimilarImages => 0,
|
||||||
|
CurrentTab::EmptyFolders => 0,
|
||||||
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_int_size_idx(active_tab: CurrentTab) -> usize {
|
||||||
|
match active_tab {
|
||||||
|
CurrentTab::EmptyFiles => 2,
|
||||||
|
CurrentTab::SimilarImages => 2,
|
||||||
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
|
CurrentTab::EmptyFolders => panic!("Unable to get size from this tab"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_width_idx(active_tab: CurrentTab) -> usize {
|
||||||
|
match active_tab {
|
||||||
|
CurrentTab::SimilarImages => 4,
|
||||||
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
|
_ => panic!("Unable to get height from this tab"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_height_idx(active_tab: CurrentTab) -> usize {
|
||||||
|
match active_tab {
|
||||||
|
CurrentTab::SimilarImages => 5,
|
||||||
|
CurrentTab::Settings => panic!("Button should be disabled"),
|
||||||
|
_ => panic!("Unable to get height from this tab"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_is_header_mode(active_tab: CurrentTab) -> bool {
|
pub fn get_is_header_mode(active_tab: CurrentTab) -> bool {
|
||||||
match active_tab {
|
match active_tab {
|
||||||
CurrentTab::EmptyFolders | CurrentTab::EmptyFiles => false,
|
CurrentTab::EmptyFolders | CurrentTab::EmptyFiles => false,
|
||||||
|
@ -99,3 +132,56 @@ pub fn create_excluded_directories_model_from_pathbuf(items: &[PathBuf]) -> Mode
|
||||||
pub fn create_vec_model_from_vec_string(items: Vec<String>) -> VecModel<SharedString> {
|
pub fn create_vec_model_from_vec_string(items: Vec<String>) -> VecModel<SharedString> {
|
||||||
VecModel::from(items.into_iter().map(SharedString::from).collect::<Vec<_>>())
|
VecModel::from(items.into_iter().map(SharedString::from).collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/slint-ui/slint/discussions/4596
|
||||||
|
// Currently there is no way to save u64 in slint, so we need to split it into two i32
|
||||||
|
pub fn split_u64_into_i32s(value: u64) -> (i32, i32) {
|
||||||
|
let part1: i32 = (value >> 32) as i32;
|
||||||
|
let part2: i32 = value as i32;
|
||||||
|
(part1, part2)
|
||||||
|
}
|
||||||
|
pub fn connect_i32_into_u64(part1: i32, part2: i32) -> u64 {
|
||||||
|
((part1 as u64) << 32) | (part2 as u64 & 0xFFFFFFFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::common::split_u64_into_i32s;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_u64_into_i32s_small() {
|
||||||
|
let value = 1;
|
||||||
|
let (part1, part2) = split_u64_into_i32s(value);
|
||||||
|
assert_eq!(part1, 0);
|
||||||
|
assert_eq!(part2, 1);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_split_u64_into_i32s_big() {
|
||||||
|
let value = u64::MAX;
|
||||||
|
let (part1, part2) = split_u64_into_i32s(value);
|
||||||
|
assert_eq!(part1, -1);
|
||||||
|
assert_eq!(part2, -1);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_connect_i32_into_u64_small() {
|
||||||
|
let part1 = 0;
|
||||||
|
let part2 = 1;
|
||||||
|
let value = super::connect_i32_into_u64(part1, part2);
|
||||||
|
assert_eq!(value, 1);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_connect_i32_into_u64_big() {
|
||||||
|
let part1 = -1;
|
||||||
|
let part2 = -1;
|
||||||
|
let value = super::connect_i32_into_u64(part1, part2);
|
||||||
|
assert_eq!(value, u64::MAX);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_connect_split_zero() {
|
||||||
|
for start_value in [0, 1, 10, u32::MAX as u64, i32::MAX as u64, u64::MAX] {
|
||||||
|
let (part1, part2) = split_u64_into_i32s(start_value);
|
||||||
|
let end_value = super::connect_i32_into_u64(part1, part2);
|
||||||
|
assert_eq!(start_value, end_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
use czkawka_core::common::remove_folder_if_contains_only_empty_folders;
|
use czkawka_core::common::remove_folder_if_contains_only_empty_folders;
|
||||||
|
|
||||||
use crate::common::{get_is_header_mode, get_name_idx, get_path_idx, get_tool_model, set_tool_model};
|
use crate::common::{get_is_header_mode, get_str_name_idx, get_str_path_idx, get_tool_model, set_tool_model};
|
||||||
use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow};
|
use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow};
|
||||||
|
|
||||||
pub fn connect_delete_button(app: &MainWindow) {
|
pub fn connect_delete_button(app: &MainWindow) {
|
||||||
|
@ -44,8 +44,8 @@ fn handle_delete_items(items: &ModelRc<MainListModel>, active_tab: CurrentTab) -
|
||||||
// and at the end should be send signal to main thread to update model
|
// and at the end should be send signal to main thread to update model
|
||||||
// TODO handle also situations where cannot delete file/folder
|
// TODO handle also situations where cannot delete file/folder
|
||||||
fn remove_selected_items(items: Vec<MainListModel>, active_tab: CurrentTab) {
|
fn remove_selected_items(items: Vec<MainListModel>, active_tab: CurrentTab) {
|
||||||
let path_idx = get_path_idx(active_tab);
|
let path_idx = get_str_path_idx(active_tab);
|
||||||
let name_idx = get_name_idx(active_tab);
|
let name_idx = get_str_name_idx(active_tab);
|
||||||
let items_to_remove = items
|
let items_to_remove = items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
|
|
|
@ -16,6 +16,7 @@ use czkawka_core::empty_folder::{EmptyFolder, FolderEntry};
|
||||||
use czkawka_core::similar_images;
|
use czkawka_core::similar_images;
|
||||||
use czkawka_core::similar_images::{ImagesEntry, SimilarImages};
|
use czkawka_core::similar_images::{ImagesEntry, SimilarImages};
|
||||||
|
|
||||||
|
use crate::common::split_u64_into_i32s;
|
||||||
use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES};
|
use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES};
|
||||||
use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend};
|
use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend};
|
||||||
|
|
||||||
|
@ -50,7 +51,6 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender<ProgressDat
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle referenced folders
|
|
||||||
fn scan_similar_images(a: Weak<MainWindow>, progress_sender: Sender<ProgressData>, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) {
|
fn scan_similar_images(a: Weak<MainWindow>, progress_sender: Sender<ProgressData>, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.stack_size(DEFAULT_THREAD_SIZE)
|
.stack_size(DEFAULT_THREAD_SIZE)
|
||||||
|
@ -70,6 +70,7 @@ fn scan_similar_images(a: Weak<MainWindow>, progress_sender: Sender<ProgressData
|
||||||
.expect("Hash type not found")
|
.expect("Hash type not found")
|
||||||
.2;
|
.2;
|
||||||
finder.set_hash_alg(hash_type);
|
finder.set_hash_alg(hash_type);
|
||||||
|
dbg!(&custom_settings.similar_images_sub_ignore_same_size);
|
||||||
finder.set_exclude_images_with_same_size(custom_settings.similar_images_sub_ignore_same_size);
|
finder.set_exclude_images_with_same_size(custom_settings.similar_images_sub_ignore_same_size);
|
||||||
finder.set_similarity(custom_settings.similar_images_sub_similarity as u32);
|
finder.set_similarity(custom_settings.similar_images_sub_similarity as u32);
|
||||||
finder.find_similar_images(Some(&stop_receiver), Some(&progress_sender));
|
finder.find_similar_images(Some(&stop_receiver), Some(&progress_sender));
|
||||||
|
@ -144,7 +145,9 @@ fn prepare_data_model_similar_images(fe: &ImagesEntry, hash_size: u8) -> (ModelR
|
||||||
directory.into(),
|
directory.into(),
|
||||||
NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().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]);
|
let modification_split = split_u64_into_i32s(fe.get_modified_date());
|
||||||
|
let size_split = split_u64_into_i32s(fe.size);
|
||||||
|
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1, fe.width as i32, fe.height as i32]);
|
||||||
(data_model_str, data_model_int)
|
(data_model_str, data_model_int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +190,9 @@ fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc<SharedString>, Mod
|
||||||
directory.into(),
|
directory.into(),
|
||||||
NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(),
|
NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(),
|
||||||
]);
|
]);
|
||||||
let data_model_int = VecModel::from_slice(&[]);
|
let modification_split = split_u64_into_i32s(fe.get_modified_date());
|
||||||
|
let size_split = split_u64_into_i32s(fe.size);
|
||||||
|
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]);
|
||||||
(data_model_str, data_model_int)
|
(data_model_str, data_model_int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +235,8 @@ fn prepare_data_model_empty_folders(fe: &FolderEntry) -> (ModelRc<SharedString>,
|
||||||
directory.into(),
|
directory.into(),
|
||||||
NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(),
|
NaiveDateTime::from_timestamp_opt(fe.modified_date as i64, 0).unwrap().to_string().into(),
|
||||||
]);
|
]);
|
||||||
let data_model_int = VecModel::from_slice(&[]);
|
let modification_split = split_u64_into_i32s(fe.get_modified_date());
|
||||||
|
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1]);
|
||||||
(data_model_str, data_model_int)
|
(data_model_str, data_model_int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +247,7 @@ fn insert_data_to_model(items: &Rc<VecModel<MainListModel>>, data_model_str: Mod
|
||||||
header_row,
|
header_row,
|
||||||
selected_row: false,
|
selected_row: false,
|
||||||
val_str: ModelRc::new(data_model_str),
|
val_str: ModelRc::new(data_model_str),
|
||||||
val_int: ModelRc::new(data_model_int), // TODO fill
|
val_int: ModelRc::new(data_model_int),
|
||||||
};
|
};
|
||||||
items.push(main);
|
items.push(main);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::common::{get_tool_model, set_tool_model};
|
use crate::common::{connect_i32_into_u64, get_int_size_idx, get_is_header_mode, get_tool_model, set_tool_model};
|
||||||
use crate::SelectModel;
|
|
||||||
use crate::{Callabler, GuiState, MainListModel, MainWindow, SelectMode};
|
use crate::{Callabler, GuiState, MainListModel, MainWindow, SelectMode};
|
||||||
|
use crate::{CurrentTab, SelectModel};
|
||||||
use slint::{ComponentHandle, Model, ModelRc, VecModel};
|
use slint::{ComponentHandle, Model, ModelRc, VecModel};
|
||||||
|
|
||||||
// TODO optimize this, not sure if it is possible to not copy entire model to just select item
|
// TODO optimize this, not sure if it is possible to not copy entire model to just select item
|
||||||
|
@ -16,6 +16,9 @@ pub fn connect_select(app: &MainWindow) {
|
||||||
SelectMode::SelectAll => select_all(current_model),
|
SelectMode::SelectAll => select_all(current_model),
|
||||||
SelectMode::UnselectAll => deselect_all(current_model),
|
SelectMode::UnselectAll => deselect_all(current_model),
|
||||||
SelectMode::InvertSelection => invert_selection(current_model),
|
SelectMode::InvertSelection => invert_selection(current_model),
|
||||||
|
SelectMode::SelectTheBiggestSize => select_the_biggest_size(current_model, active_tab),
|
||||||
|
SelectMode::SelectTheSmallestSize => select_the_small_size(current_model, active_tab),
|
||||||
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
set_tool_model(&app, active_tab, new_model);
|
set_tool_model(&app, active_tab, new_model);
|
||||||
});
|
});
|
||||||
|
@ -31,10 +34,23 @@ pub fn connect_showing_proper_select_buttons(app: &MainWindow) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_select_buttons(app: &MainWindow) {
|
fn set_select_buttons(app: &MainWindow) {
|
||||||
// let active_tab = app.global::<GuiState>().get_active_tab();
|
let active_tab = app.global::<GuiState>().get_active_tab();
|
||||||
let base_buttons = vec![SelectMode::SelectAll, SelectMode::UnselectAll, SelectMode::InvertSelection];
|
let mut base_buttons = vec![SelectMode::SelectAll, SelectMode::UnselectAll, SelectMode::InvertSelection];
|
||||||
|
|
||||||
// TODO Here needs to be put logic to set custom buttons depending on tab
|
let additional_buttons = match active_tab {
|
||||||
|
CurrentTab::SimilarImages => vec![
|
||||||
|
SelectMode::SelectOldest,
|
||||||
|
SelectMode::SelectNewest,
|
||||||
|
SelectMode::SelectTheSmallestSize,
|
||||||
|
SelectMode::SelectTheBiggestSize,
|
||||||
|
SelectMode::SelectTheSmallestResolution,
|
||||||
|
SelectMode::SelectTheBiggestResolution,
|
||||||
|
],
|
||||||
|
_ => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
base_buttons.extend(additional_buttons);
|
||||||
|
base_buttons.reverse();
|
||||||
|
|
||||||
let new_select_model = base_buttons
|
let new_select_model = base_buttons
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -52,9 +68,65 @@ fn translate_select_mode(select_mode: SelectMode) -> String {
|
||||||
SelectMode::SelectAll => "Select all".into(),
|
SelectMode::SelectAll => "Select all".into(),
|
||||||
SelectMode::UnselectAll => "Unselect all".into(),
|
SelectMode::UnselectAll => "Unselect all".into(),
|
||||||
SelectMode::InvertSelection => "Invert selection".into(),
|
SelectMode::InvertSelection => "Invert selection".into(),
|
||||||
|
SelectMode::SelectTheBiggestSize => "Select the biggest size".into(),
|
||||||
|
SelectMode::SelectTheBiggestResolution => "Select the biggest resolution".into(),
|
||||||
|
SelectMode::SelectTheSmallestSize => "Select the smallest size".into(),
|
||||||
|
SelectMode::SelectTheSmallestResolution => "Select the smallest resolution".into(),
|
||||||
|
SelectMode::SelectNewest => "Select newest".into(),
|
||||||
|
SelectMode::SelectOldest => "Select oldest".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_the_biggest_size(model: ModelRc<MainListModel>, active_tab: CurrentTab) -> ModelRc<MainListModel> {
|
||||||
|
let is_header_mode = get_is_header_mode(active_tab);
|
||||||
|
assert!(is_header_mode); // non header modes not really have reasont to use this function
|
||||||
|
|
||||||
|
let mut old_data = model.iter().collect::<Vec<_>>();
|
||||||
|
let headers_idx = find_header_idx_and_deselect_all(&mut old_data);
|
||||||
|
let size_idx = get_int_size_idx(active_tab);
|
||||||
|
|
||||||
|
for i in 0..(headers_idx.len() - 1) {
|
||||||
|
let mut max_size = 0;
|
||||||
|
let mut max_size_idx = 0;
|
||||||
|
for j in (headers_idx[i] + 1)..headers_idx[i + 1] {
|
||||||
|
let int_data = old_data[j].val_int.iter().collect::<Vec<_>>();
|
||||||
|
let size = connect_i32_into_u64(int_data[size_idx], int_data[size_idx + 1]);
|
||||||
|
if size > max_size {
|
||||||
|
max_size = size;
|
||||||
|
max_size_idx = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old_data[max_size_idx].checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelRc::new(VecModel::from(old_data))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_the_small_size(model: ModelRc<MainListModel>, active_tab: CurrentTab) -> ModelRc<MainListModel> {
|
||||||
|
let is_header_mode = get_is_header_mode(active_tab);
|
||||||
|
assert!(is_header_mode); // non header modes not really have reasont to use this function
|
||||||
|
|
||||||
|
let mut old_data = model.iter().collect::<Vec<_>>();
|
||||||
|
let headers_idx = find_header_idx_and_deselect_all(&mut old_data);
|
||||||
|
let size_idx = get_int_size_idx(active_tab);
|
||||||
|
|
||||||
|
for i in 0..(headers_idx.len() - 1) {
|
||||||
|
let mut min_size = u64::MAX;
|
||||||
|
let mut min_size_idx = 0;
|
||||||
|
for j in (headers_idx[i] + 1)..headers_idx[i + 1] {
|
||||||
|
let int_data = old_data[j].val_int.iter().collect::<Vec<_>>();
|
||||||
|
let size = connect_i32_into_u64(int_data[size_idx], int_data[size_idx + 1]);
|
||||||
|
if size < min_size {
|
||||||
|
min_size = size;
|
||||||
|
min_size_idx = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old_data[min_size_idx].checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelRc::new(VecModel::from(old_data))
|
||||||
|
}
|
||||||
|
|
||||||
fn select_all(model: ModelRc<MainListModel>) -> ModelRc<MainListModel> {
|
fn select_all(model: ModelRc<MainListModel>) -> ModelRc<MainListModel> {
|
||||||
let mut old_data = model.iter().collect::<Vec<_>>();
|
let mut old_data = model.iter().collect::<Vec<_>>();
|
||||||
old_data.iter_mut().for_each(|x| {
|
old_data.iter_mut().for_each(|x| {
|
||||||
|
@ -80,3 +152,64 @@ fn invert_selection(model: ModelRc<MainListModel>) -> ModelRc<MainListModel> {
|
||||||
});
|
});
|
||||||
ModelRc::new(VecModel::from(old_data))
|
ModelRc::new(VecModel::from(old_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_header_idx_and_deselect_all(old_data: &mut Vec<MainListModel>) -> Vec<usize> {
|
||||||
|
let mut header_idx = old_data
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(idx, m)| if m.header_row { Some(idx) } else { None })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
header_idx.push(old_data.len());
|
||||||
|
|
||||||
|
old_data.iter_mut().for_each(|x| {
|
||||||
|
if !x.header_row {
|
||||||
|
x.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
header_idx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{MainListModel, SelectMode};
|
||||||
|
use slint::ModelRc;
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// pub fn test_select_all() {
|
||||||
|
// let model = ModelRc::new(VecModel::from(vec![SelectModel {
|
||||||
|
// name: "test".into(),
|
||||||
|
// data: SelectMode::SelectAll,
|
||||||
|
// }]));
|
||||||
|
// let new_model = select_all(model);
|
||||||
|
// let new_data = new_model.iter().collect::<Vec<_>>();
|
||||||
|
// assert_eq!(new_data[0].checked, true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn prepare_simple_model() -> ModelRc<MainListModel> {
|
||||||
|
// ModelRc::new(VecModel::from(vec![
|
||||||
|
// MainListModel {
|
||||||
|
// header_row: false,
|
||||||
|
// checked: false,
|
||||||
|
// selected_row: false,
|
||||||
|
// val_str: [],
|
||||||
|
// val_int: [0, 0, 0, 0, 0, 0],
|
||||||
|
// },
|
||||||
|
// MainListModel {
|
||||||
|
// header_row: false,
|
||||||
|
// checked: true,
|
||||||
|
// text: "test".into(),
|
||||||
|
// size: 0,
|
||||||
|
// resolution: (0, 0),
|
||||||
|
// date: 0,
|
||||||
|
// },
|
||||||
|
// MainListModel {
|
||||||
|
// header_row: false,
|
||||||
|
// checked: false,
|
||||||
|
// text: "test".into(),
|
||||||
|
// size: 0,
|
||||||
|
// resolution: (0, 0),
|
||||||
|
// date: 0,
|
||||||
|
// },
|
||||||
|
// ]))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ export component ActionButtons inherits HorizontalLayout {
|
||||||
enabled: !scanning && lists_enabled;
|
enabled: !scanning && lists_enabled;
|
||||||
text: "Select";
|
text: "Select";
|
||||||
clicked => {
|
clicked => {
|
||||||
show_select_popup(self.x - self.width / 2, self.y + parent.y);
|
show_select_popup(self.x + self.width / 2, self.y + parent.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,13 @@ export struct ExcludedDirectoriesModel {
|
||||||
export enum SelectMode {
|
export enum SelectMode {
|
||||||
SelectAll,
|
SelectAll,
|
||||||
UnselectAll,
|
UnselectAll,
|
||||||
InvertSelection
|
InvertSelection,
|
||||||
|
SelectTheBiggestSize,
|
||||||
|
SelectTheBiggestResolution,
|
||||||
|
SelectTheSmallestSize,
|
||||||
|
SelectTheSmallestResolution,
|
||||||
|
SelectNewest,
|
||||||
|
SelectOldest,
|
||||||
}
|
}
|
||||||
|
|
||||||
export struct SelectModel {
|
export struct SelectModel {
|
||||||
|
|
|
@ -19,5 +19,5 @@ export global GuiState {
|
||||||
|
|
||||||
in-out property <bool> available_subsettings: active_tab == CurrentTab.SimilarImages;
|
in-out property <bool> available_subsettings: active_tab == CurrentTab.SimilarImages;
|
||||||
in-out property <CurrentTab> active_tab: CurrentTab.EmptyFiles;
|
in-out property <CurrentTab> active_tab: CurrentTab.EmptyFiles;
|
||||||
in-out property <[SelectModel]> select_results_list: [{data: SelectMode.SelectAll, name: "Select All"}, {data: SelectMode.UnselectAll, name: "Deselect All"}];
|
in-out property <[SelectModel]> select_results_list: [{data: SelectMode.SelectAll, name: "Select All"}, {data: SelectMode.UnselectAll, name: "Deselect All"}, {data: SelectMode.SelectTheSmallestResolution, name: "Select the smallest resolution"}];
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ export component MainWindow inherits Window {
|
||||||
}
|
}
|
||||||
show_select_popup(x_offset, y_offset) => {
|
show_select_popup(x_offset, y_offset) => {
|
||||||
select_popup_window.x_offset = x_offset;
|
select_popup_window.x_offset = x_offset;
|
||||||
select_popup_window.y_offset = y_offset - select-popup-window.all_items_height - 5px;
|
select_popup_window.y_offset = y_offset;
|
||||||
select_popup_window.show_popup();
|
select_popup_window.show_popup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,8 +151,8 @@ export component MainWindow inherits Window {
|
||||||
property <length> x_offset: 0;
|
property <length> x_offset: 0;
|
||||||
property <length> y_offset: 0;
|
property <length> y_offset: 0;
|
||||||
|
|
||||||
x: parent.x + x_offset;
|
x: parent.x + x_offset - self.item_width / 2.0;
|
||||||
y: parent.y + y_offset;
|
y: parent.y + y_offset - self.all_items_height - 5px;
|
||||||
|
|
||||||
height: root.height;
|
height: root.height;
|
||||||
width: root.width;
|
width: root.width;
|
||||||
|
|
|
@ -17,7 +17,7 @@ export component PopupSelectResults inherits Rectangle {
|
||||||
callback show_popup();
|
callback show_popup();
|
||||||
property <[SelectModel]> model: GuiState.select_results_list;
|
property <[SelectModel]> model: GuiState.select_results_list;
|
||||||
property <length> item_height: 30px;
|
property <length> item_height: 30px;
|
||||||
property <length> item_width: 140px;
|
out property <length> item_width: 200px;
|
||||||
out property <length> all_items_height: item_height * model.length;
|
out property <length> all_items_height: item_height * model.length;
|
||||||
|
|
||||||
popup_window := PopupWindow {
|
popup_window := PopupWindow {
|
||||||
|
|
|
@ -69,21 +69,21 @@ export component ToolSettings {
|
||||||
ComboBoxWrapper {
|
ComboBoxWrapper {
|
||||||
text: "Hash size";
|
text: "Hash size";
|
||||||
model: Settings.similar_images_sub_available_hash_size;
|
model: Settings.similar_images_sub_available_hash_size;
|
||||||
current_index: Settings.similar_images_sub_hash_size_index;
|
current_index <=> Settings.similar_images_sub_hash_size_index;
|
||||||
}
|
}
|
||||||
ComboBoxWrapper {
|
ComboBoxWrapper {
|
||||||
text: "Resize Algorithm";
|
text: "Resize Algorithm";
|
||||||
model: Settings.similar_images_sub_available_resize_algorithm;
|
model: Settings.similar_images_sub_available_resize_algorithm;
|
||||||
current_index: Settings.similar_images_sub_resize_algorithm_index;
|
current_index <=> Settings.similar_images_sub_resize_algorithm_index;
|
||||||
}
|
}
|
||||||
ComboBoxWrapper {
|
ComboBoxWrapper {
|
||||||
text: "Hash type";
|
text: "Hash type";
|
||||||
model: Settings.similar_images_sub_available_hash_type;
|
model: Settings.similar_images_sub_available_hash_type;
|
||||||
current_index: Settings.similar_images_sub_hash_type_index;
|
current_index <=> Settings.similar_images_sub_hash_type_index;
|
||||||
}
|
}
|
||||||
CheckBoxWrapper {
|
CheckBoxWrapper {
|
||||||
text: "Ignore same size";
|
text: "Ignore same size";
|
||||||
checked: Settings.similar_images_sub_ignore_same_size;
|
checked <=> Settings.similar_images_sub_ignore_same_size;
|
||||||
}
|
}
|
||||||
SliderWrapper {
|
SliderWrapper {
|
||||||
text: "Max difference";
|
text: "Max difference";
|
||||||
|
|
Loading…
Reference in a new issue