Actions
This commit is contained in:
parent
473375ad5f
commit
dfb89b2f5e
|
@ -26,6 +26,12 @@ pub struct FolderEntry {
|
|||
pub modified_date: u64,
|
||||
}
|
||||
|
||||
impl FolderEntry {
|
||||
pub fn get_modified_date(&self) -> u64 {
|
||||
self.modified_date
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptyFolder {
|
||||
common_data: CommonToolData,
|
||||
information: Info,
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{CurrentTab, ExcludedDirectoriesModel, IncludedDirectoriesModel, Main
|
|||
use slint::{ModelRc, SharedString, VecModel};
|
||||
|
||||
// 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 {
|
||||
CurrentTab::EmptyFolders => 1,
|
||||
CurrentTab::EmptyFiles => 1,
|
||||
|
@ -12,7 +12,7 @@ pub fn get_path_idx(active_tab: CurrentTab) -> usize {
|
|||
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 {
|
||||
CurrentTab::EmptyFolders => 0,
|
||||
CurrentTab::EmptyFiles => 0,
|
||||
|
@ -20,6 +20,39 @@ pub fn get_name_idx(active_tab: CurrentTab) -> usize {
|
|||
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 {
|
||||
match active_tab {
|
||||
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> {
|
||||
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 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};
|
||||
|
||||
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
|
||||
// TODO handle also situations where cannot delete file/folder
|
||||
fn remove_selected_items(items: Vec<MainListModel>, active_tab: CurrentTab) {
|
||||
let path_idx = get_path_idx(active_tab);
|
||||
let name_idx = get_name_idx(active_tab);
|
||||
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| {
|
||||
|
|
|
@ -16,6 +16,7 @@ use czkawka_core::empty_folder::{EmptyFolder, FolderEntry};
|
|||
use czkawka_core::similar_images;
|
||||
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::{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) {
|
||||
thread::Builder::new()
|
||||
.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")
|
||||
.2;
|
||||
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_similarity(custom_settings.similar_images_sub_similarity as u32);
|
||||
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(),
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -187,7 +190,9 @@ fn prepare_data_model_empty_files(fe: &FileEntry) -> (ModelRc<SharedString>, Mod
|
|||
directory.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)
|
||||
}
|
||||
|
||||
|
@ -230,7 +235,8 @@ fn prepare_data_model_empty_folders(fe: &FolderEntry) -> (ModelRc<SharedString>,
|
|||
directory.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)
|
||||
}
|
||||
|
||||
|
@ -241,7 +247,7 @@ fn insert_data_to_model(items: &Rc<VecModel<MainListModel>>, data_model_str: Mod
|
|||
header_row,
|
||||
selected_row: false,
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::common::{get_tool_model, set_tool_model};
|
||||
use crate::SelectModel;
|
||||
use crate::common::{connect_i32_into_u64, get_int_size_idx, get_is_header_mode, get_tool_model, set_tool_model};
|
||||
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
|
||||
|
@ -16,6 +16,9 @@ pub fn connect_select(app: &MainWindow) {
|
|||
SelectMode::SelectAll => select_all(current_model),
|
||||
SelectMode::UnselectAll => deselect_all(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);
|
||||
});
|
||||
|
@ -31,10 +34,23 @@ pub fn connect_showing_proper_select_buttons(app: &MainWindow) {
|
|||
}
|
||||
|
||||
fn set_select_buttons(app: &MainWindow) {
|
||||
// let active_tab = app.global::<GuiState>().get_active_tab();
|
||||
let base_buttons = vec![SelectMode::SelectAll, SelectMode::UnselectAll, SelectMode::InvertSelection];
|
||||
let active_tab = app.global::<GuiState>().get_active_tab();
|
||||
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
|
||||
.into_iter()
|
||||
|
@ -52,9 +68,65 @@ fn translate_select_mode(select_mode: SelectMode) -> String {
|
|||
SelectMode::SelectAll => "Select all".into(),
|
||||
SelectMode::UnselectAll => "Unselect all".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> {
|
||||
let mut old_data = model.iter().collect::<Vec<_>>();
|
||||
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))
|
||||
}
|
||||
|
||||
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;
|
||||
text: "Select";
|
||||
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 {
|
||||
SelectAll,
|
||||
UnselectAll,
|
||||
InvertSelection
|
||||
InvertSelection,
|
||||
SelectTheBiggestSize,
|
||||
SelectTheBiggestResolution,
|
||||
SelectTheSmallestSize,
|
||||
SelectTheSmallestResolution,
|
||||
SelectNewest,
|
||||
SelectOldest,
|
||||
}
|
||||
|
||||
export struct SelectModel {
|
||||
|
|
|
@ -19,5 +19,5 @@ export global GuiState {
|
|||
|
||||
in-out property <bool> available_subsettings: active_tab == CurrentTab.SimilarImages;
|
||||
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) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -151,8 +151,8 @@ export component MainWindow inherits Window {
|
|||
property <length> x_offset: 0;
|
||||
property <length> y_offset: 0;
|
||||
|
||||
x: parent.x + x_offset;
|
||||
y: parent.y + y_offset;
|
||||
x: parent.x + x_offset - self.item_width / 2.0;
|
||||
y: parent.y + y_offset - self.all_items_height - 5px;
|
||||
|
||||
height: root.height;
|
||||
width: root.width;
|
||||
|
|
|
@ -17,7 +17,7 @@ export component PopupSelectResults inherits Rectangle {
|
|||
callback show_popup();
|
||||
property <[SelectModel]> model: GuiState.select_results_list;
|
||||
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;
|
||||
|
||||
popup_window := PopupWindow {
|
||||
|
|
|
@ -69,21 +69,21 @@ export component ToolSettings {
|
|||
ComboBoxWrapper {
|
||||
text: "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 {
|
||||
text: "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 {
|
||||
text: "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 {
|
||||
text: "Ignore same size";
|
||||
checked: Settings.similar_images_sub_ignore_same_size;
|
||||
checked <=> Settings.similar_images_sub_ignore_same_size;
|
||||
}
|
||||
SliderWrapper {
|
||||
text: "Max difference";
|
||||
|
|
Loading…
Reference in a new issue