From b3ff7e3f008f2daf1b7038705a9b9f9a0280fc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Sun, 6 Oct 2024 20:07:26 +0200 Subject: [PATCH] Fixed crash in similar images, added icons to krokiet, changed a litte gui, modified logo (#1359) --- Changelog.md | 20 +++++- czkawka_core/src/common_image.rs | 16 ++++- czkawka_core/src/similar_images.rs | 8 ++- krokiet/README.md | 9 +++ krokiet/icons/krokiet_delete.svg | 1 + krokiet/icons/krokiet_move.svg | 1 + krokiet/icons/krokiet_search.svg | 1 + krokiet/icons/krokiet_select.svg | 1 + krokiet/icons/krokiet_stop.svg | 1 + krokiet/icons/logo.png | Bin 11659 -> 18867 bytes krokiet/icons/logo_small.png | Bin 0 -> 9410 bytes krokiet/icons/settings.svg | 6 +- krokiet/icons/subsettings.svg | 18 +---- krokiet/src/common.rs | 5 ++ krokiet/src/connect_scan.rs | 7 +- krokiet/src/connect_show_preview.rs | 7 +- krokiet/ui/action_buttons.slint | 92 ++++++++++++++++++++----- krokiet/ui/bottom_panel.slint | 16 ++--- krokiet/ui/color_palette.slint | 27 +++++++- krokiet/ui/gui_state.slint | 2 +- krokiet/ui/included_directories.slint | 4 +- krokiet/ui/left_side_panel.slint | 24 ++++--- krokiet/ui/main_lists.slint | 59 +++++++++++----- krokiet/ui/main_window.slint | 20 ++++-- krokiet/ui/popup_new_directories.slint | 6 +- krokiet/ui/progress.slint | 14 ++-- krokiet/ui/selectable_tree_view.slint | 60 ++++++++++------ krokiet/ui/settings_list.slint | 4 +- 28 files changed, 301 insertions(+), 128 deletions(-) create mode 100644 krokiet/icons/krokiet_delete.svg create mode 100644 krokiet/icons/krokiet_move.svg create mode 100644 krokiet/icons/krokiet_search.svg create mode 100644 krokiet/icons/krokiet_select.svg create mode 100644 krokiet/icons/krokiet_stop.svg create mode 100644 krokiet/icons/logo_small.png diff --git a/Changelog.md b/Changelog.md index c8a698589..094902e77 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,9 +2,15 @@ ### Breaking changes -- Due removing image_type from image struct, old cache files are incompatible with new version and should be regenerated +- Due to the removal image_type from image struct, old cache files are incompatible with new version and should be + regenerated from scratch(it uses new name) +### Known regressions + +- Slint 1.8 which Krokiet uses requires femtovg 0.9.2 which broke font + rendering - https://github.com/slint-ui/slint/issues/6298 + ### Core - Removed some unnecessary panics - [#1354](https://github.com/qarmin/czkawka/pull/1354) @@ -26,7 +32,7 @@ - Added avif support(via external C library, not enabled by default) - [#1358](https://github.com/qarmin/czkawka/pull/1358) - Integer overflow are enabled by default(prepare for reporting bugs, slower performance and - unstability) - [#1358](https://github.com/qarmin/czkawka/pull/1358) + general unstability) - [#1358](https://github.com/qarmin/czkawka/pull/1358) - Fixed crash when loading invalid image cache - [#1230](https://github.com/qarmin/czkawka/pull/1230) ### Krokiet @@ -34,6 +40,14 @@ - Fixed invalid default hash size in similar images - [#1354](https://github.com/qarmin/czkawka/pull/1354) - Fixed and added more input parameters to the application - [#1354](https://github.com/qarmin/czkawka/pull/1354) - Fixed problem with loading invalid preset - [#1226](https://github.com/qarmin/czkawka/pull/1226) +- Fixed crash when using 8 hash size with small similarity - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Disabling buttons when no files were found - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Changed way to close/open panel at bottom - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Modify logo a little - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Avoid errors when trying to load preview of not supported file - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Added ability to show preview of referenced folders - [#1359](https://github.com/qarmin/czkawka/pull/1359) +- Enable selecting with space and jumping over entries with + arrows and opening with enter - [#1359](https://github.com/qarmin/czkawka/pull/1359) ### GTK GUI @@ -47,7 +61,7 @@ - Added options to find/remove images by size - [#1255](https://github.com/qarmin/czkawka/pull/1255) - Fixed and added more input parameters to the application - [#1354](https://github.com/qarmin/czkawka/pull/1354) -- Fixed crash when stopping scan mutliple times - [#1355](https://github.com/qarmin/czkawka/pull/1355) +- Fixed crash when stopping scan multiple times - [#1355](https://github.com/qarmin/czkawka/pull/1355) - Print results also in debug build - [#1355](https://github.com/qarmin/czkawka/pull/1355) ## Version 7.0.0 - 19.02.2024r diff --git a/czkawka_core/src/common_image.rs b/czkawka_core/src/common_image.rs index d0fdbc482..73f7be417 100644 --- a/czkawka_core/src/common_image.rs +++ b/czkawka_core/src/common_image.rs @@ -29,7 +29,7 @@ use rawloader::RawLoader; use symphonia::core::conv::IntoSample; use crate::common; -use crate::common::{create_crash_message, HEIC_EXTENSIONS, JXL_IMAGE_EXTENSIONS, RAW_IMAGE_EXTENSIONS}; +use crate::common::{create_crash_message, HEIC_EXTENSIONS, IMAGE_RS_EXTENSIONS, IMAGE_RS_SIMILAR_IMAGES_EXTENSIONS, JXL_IMAGE_EXTENSIONS, RAW_IMAGE_EXTENSIONS}; // #[cfg(feature = "heif")] // use libheif_rs::LibHeif; @@ -171,3 +171,17 @@ pub fn get_raw_image(path: impl AsRef + std::fmt::Debug) -> Result bool { + let Some(extension) = Path::new(path).extension() else { + return false; + }; + let extension_str = extension.to_string_lossy().to_lowercase(); + #[cfg(feature = "heif")] + let allowed_extensions = &[IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS, JXL_IMAGE_EXTENSIONS, HEIC_EXTENSIONS].concat(); + + #[cfg(not(feature = "heif"))] + let allowed_extensions = &[IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS, JXL_IMAGE_EXTENSIONS].concat(); + + allowed_extensions.iter().any(|ext| extension_str.ends_with(ext)) +} diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index 77c8a1143..e77a6dd19 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -29,8 +29,11 @@ use crate::progress_data::{CurrentStage, ProgressData}; type ImHash = Vec; +// 40 is, similar like previous 20 in 8 hash size is useless +// But since Krowka have problems with proper changing max value in fly +// hardcode 40 as max value pub const SIMILAR_VALUES: [[u32; 6]; 4] = [ - [1, 2, 5, 7, 14, 20], // 8 + [1, 2, 5, 7, 14, 40], // 8 [2, 5, 15, 30, 40, 40], // 16 [4, 10, 20, 40, 40, 40], // 32 [6, 20, 40, 40, 40, 40], // 64 @@ -357,7 +360,6 @@ impl SimilarImages { .hash_alg(self.get_params().hash_alg) .resize_filter(self.get_params().image_filter); let hasher = hasher_config.to_hasher(); - let hash = hasher.hash_image(&img); file_entry.hash = hash.as_bytes().to_vec(); @@ -818,7 +820,7 @@ pub fn get_string_from_similarity(similarity: &u32, hash_size: u8) -> String { } else if *similarity <= SIMILAR_VALUES[index_preset][5] { flc!("core_similarity_minimal") } else { - panic!(); + panic!("Invalid similarity value {similarity} for hash size {hash_size} (index {index_preset})"); } } diff --git a/krokiet/README.md b/krokiet/README.md index de73d6f81..523a796d9 100644 --- a/krokiet/README.md +++ b/krokiet/README.md @@ -81,6 +81,15 @@ should print something like Slint: Build config: debug; Backend: software ``` +## Scaling application + +If you have high DPI monitor, you may want to scale application. You can do it by setting `SLINT_SCALE_FACTOR` +environment + +``` +SLINT_SCALE_FACTOR=2 cargo run +``` + ## Different theme App was created with dark fluent theme in mind, but is possible to use light theme by setting `SLINT_STYLE` environment diff --git a/krokiet/icons/krokiet_delete.svg b/krokiet/icons/krokiet_delete.svg new file mode 100644 index 000000000..0a420d816 --- /dev/null +++ b/krokiet/icons/krokiet_delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/icons/krokiet_move.svg b/krokiet/icons/krokiet_move.svg new file mode 100644 index 000000000..02d395b60 --- /dev/null +++ b/krokiet/icons/krokiet_move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/icons/krokiet_search.svg b/krokiet/icons/krokiet_search.svg new file mode 100644 index 000000000..32d11dbb2 --- /dev/null +++ b/krokiet/icons/krokiet_search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/icons/krokiet_select.svg b/krokiet/icons/krokiet_select.svg new file mode 100644 index 000000000..e9be3889b --- /dev/null +++ b/krokiet/icons/krokiet_select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/icons/krokiet_stop.svg b/krokiet/icons/krokiet_stop.svg new file mode 100644 index 000000000..1a22aeb34 --- /dev/null +++ b/krokiet/icons/krokiet_stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/icons/logo.png b/krokiet/icons/logo.png index 20230dc8b74b165ed3fb9aa75a638e4fa2b269ed..d016c53920b235f4f5f5b517ecc3200aaac6aa1e 100644 GIT binary patch delta 15033 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 delta 7922 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 yCy%+(88_gILBcSjO@> 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 literal 0 HcmV?d00001 diff --git a/krokiet/icons/settings.svg b/krokiet/icons/settings.svg index 64597bb03..ee20ffbea 100644 --- a/krokiet/icons/settings.svg +++ b/krokiet/icons/settings.svg @@ -1,5 +1 @@ - - - - - + \ No newline at end of file diff --git a/krokiet/icons/subsettings.svg b/krokiet/icons/subsettings.svg index 62007e647..45e04f91c 100644 --- a/krokiet/icons/subsettings.svg +++ b/krokiet/icons/subsettings.svg @@ -1,17 +1 @@ - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/krokiet/src/common.rs b/krokiet/src/common.rs index d6ece412d..09c9c34b8 100644 --- a/krokiet/src/common.rs +++ b/krokiet/src/common.rs @@ -374,6 +374,11 @@ pub fn create_excluded_directories_model_from_pathbuf(items: &[PathBuf]) -> Mode ModelRc::new(VecModel::from(converted)) } +pub fn check_if_there_are_any_included_folders(app: &MainWindow) -> bool { + let included = app.global::().get_included_directories_model(); + included.iter().count() > 0 +} + pub fn check_if_all_included_dirs_are_referenced(app: &MainWindow) -> bool { let included = app.global::().get_included_directories_model(); included.iter().all(|x| x.referenced_folder) diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs index 05b63cefd..a618b743a 100644 --- a/krokiet/src/connect_scan.rs +++ b/krokiet/src/connect_scan.rs @@ -24,7 +24,7 @@ use humansize::{format_size, BINARY}; use rayon::prelude::*; use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak}; -use crate::common::{check_if_all_included_dirs_are_referenced, split_u64_into_i32s}; +use crate::common::{check_if_all_included_dirs_are_referenced, check_if_there_are_any_included_folders, split_u64_into_i32s}; use crate::settings::{ collect_settings, get_audio_check_type_idx, get_biggest_item_idx, get_duplicates_check_method_idx, get_duplicates_hash_type_idx, get_image_hash_alg_idx, get_resize_algorithm_idx, SettingsCustom, ALLOWED_AUDIO_CHECK_TYPE_VALUES, ALLOWED_BIG_FILE_SIZE_VALUES, ALLOWED_DUPLICATES_CHECK_METHOD_VALUES, @@ -37,6 +37,11 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender button_visibility; in-out property bottom_panel_visibility; - enabled: bottom_panel_visibility != button-visibility; + checked: bottom_panel_visibility == button_visibility; height: 30px; width: 70px; clicked => { - bottom-panel-visibility = button_visibility; + if (bottom_panel_visibility == button_visibility) { + bottom_panel_visibility = BottomPanelVisibility.NotVisible; + } else { + bottom_panel_visibility = button_visibility; + } } } @@ -23,20 +28,78 @@ export component ActionButtons inherits HorizontalLayout { callback show_select_popup(length, length); callback show_remove_popup(); callback request_folder_to_move(); + + in-out property <[MainListModel]> duplicate_files_model: []; + in-out property <[MainListModel]> empty_folder_model: []; + in-out property <[MainListModel]> big_files_model: []; + in-out property <[MainListModel]> empty_files_model: []; + in-out property <[MainListModel]> temporary_files_model: []; + in-out property <[MainListModel]> similar_images_model: []; + in-out property <[MainListModel]> similar_videos_model: []; + in-out property <[MainListModel]> similar_music_model: []; + in-out property <[MainListModel]> invalid_symlinks_model: []; + in-out property <[MainListModel]> broken_files_model: []; + in-out property <[MainListModel]> bad_extensions_model: []; + + property active_tab: GuiState.active_tab; + in-out property bottom_panel_visibility <=> GuiState.bottom_panel_visibility; in-out property stop_requested: false; in-out property scanning; in-out property lists_enabled: GuiState.is_tool_tab_active; + in-out property results_available: false; out property name; height: 30px; spacing: 4px; + changed duplicate_files_model => {check_if_enable_buttons();} + changed empty_folder_model => {check_if_enable_buttons();} + changed big_files_model => {check_if_enable_buttons();} + changed empty_files_model => {check_if_enable_buttons();} + changed temporary_files_model => {check_if_enable_buttons();} + changed similar_images_model => {check_if_enable_buttons();} + changed similar_videos_model => {check_if_enable_buttons();} + changed similar_music_model => {check_if_enable_buttons();} + changed invalid_symlinks_model => {check_if_enable_buttons();} + changed broken_files_model => {check_if_enable_buttons();} + changed bad_extensions_model => {check_if_enable_buttons();} + changed active_tab => {check_if_enable_buttons();} + + function check_if_enable_buttons() { + if (active_tab == CurrentTab.DuplicateFiles && duplicate_files_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.EmptyFolders && empty_folder_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.BigFiles && big_files_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.EmptyFiles && empty_files_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.TemporaryFiles && temporary_files_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.SimilarImages && similar_images_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.SimilarVideos && similar_videos_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.SimilarMusic && similar_music_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.InvalidSymlinks && invalid_symlinks_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.BrokenFiles && broken_files_model.length > 0) { + results_available = true; + } else if (active_tab == CurrentTab.BadExtensions && bad_extensions_model.length > 0) { + results_available = true; + } else { + results_available = false; + } + } + Rectangle { scan_button := Button { height: parent.height; enabled: !scanning && lists_enabled; visible: !scanning && lists_enabled; text: "Scan"; + icon: @image-url("../icons/krokiet_search.svg"); clicked => { root.scanning = true; root.scan_starting(GuiState.active_tab); @@ -48,6 +111,7 @@ export component ActionButtons inherits HorizontalLayout { visible: scanning; enabled: scanning && !stop_requested && root.lists_enabled; text: "Stop"; + icon: @image-url("../icons/krokiet_stop.svg"); clicked => { root.scan_stopping(); root.stop_requested = true; @@ -56,14 +120,15 @@ export component ActionButtons inherits HorizontalLayout { } Rectangle { - horizontal-stretch: 0.5; + max-width: 5px; } select_button := Button { visible: lists_enabled; height: parent.height; - enabled: !scanning && lists_enabled; + enabled: !scanning && lists_enabled && results_available; text: "Select"; + icon: @image-url("../icons/krokiet_select.svg"); clicked => { show_select_popup(self.x + self.width / 2, self.y + parent.y); } @@ -72,8 +137,9 @@ export component ActionButtons inherits HorizontalLayout { move_button := Button { visible: lists_enabled; height: parent.height; - enabled: !scanning && lists_enabled; + enabled: !scanning && lists_enabled && results_available; text: "Move"; + icon: @image-url("../icons/krokiet_move.svg"); clicked => { request_folder_to_move(); } @@ -82,8 +148,9 @@ export component ActionButtons inherits HorizontalLayout { delete_button := Button { visible: lists_enabled; height: parent.height; - enabled: !scanning && lists_enabled; + enabled: !scanning && lists_enabled && results_available; text: "Delete"; + icon: @image-url("../icons/krokiet_delete.svg"); clicked => { show_remove_popup(); } @@ -98,23 +165,16 @@ export component ActionButtons inherits HorizontalLayout { spacing: 0px; VisibilityButton { height: parent.height; - button-visibility: BottomPanelVisibility.Directories; + button_visibility: BottomPanelVisibility.Directories; bottom_panel_visibility <=> bottom_panel_visibility; text: "Dirs"; } VisibilityButton { height: parent.height; - button-visibility: BottomPanelVisibility.TextErrors; - bottom_panel_visibility <=> bottom_panel_visibility; - text: "Text"; - } - - VisibilityButton { - height: parent.height; - button-visibility: BottomPanelVisibility.NotVisible; + button_visibility: BottomPanelVisibility.TextErrors; bottom_panel_visibility <=> bottom_panel_visibility; - text: "None"; + text: "Output"; } } } diff --git a/krokiet/ui/bottom_panel.slint b/krokiet/ui/bottom_panel.slint index 3e0bd6acb..bdfd7fa86 100644 --- a/krokiet/ui/bottom_panel.slint +++ b/krokiet/ui/bottom_panel.slint @@ -105,20 +105,20 @@ export component BottomPanel { in-out property bottom_panel_visibility: BottomPanelVisibility.Directories; callback folder_choose_requested(bool); callback show_manual_add_dialog(bool); - min-height: bottom-panel-visibility == BottomPanelVisibility.NotVisible ? 0px : 150px; - min-width: bottom-panel-visibility == BottomPanelVisibility.NotVisible ? 0px : 400px; - if bottom-panel-visibility == BottomPanelVisibility.Directories: DirectoriesPanel { + min-height: bottom_panel_visibility == BottomPanelVisibility.NotVisible ? 0px : 150px; + min-width: bottom_panel_visibility == BottomPanelVisibility.NotVisible ? 0px : 400px; + if bottom_panel_visibility == BottomPanelVisibility.Directories: DirectoriesPanel { width: parent.width; height: parent.height; - folder_choose_requested(included-directories) => { - root.folder_choose_requested(included-directories) + folder_choose_requested(included_directories) => { + root.folder_choose_requested(included_directories) } - show_manual_add_dialog(included-directories) => { - root.show_manual_add_dialog(included-directories) + show_manual_add_dialog(included_directories) => { + root.show_manual_add_dialog(included_directories) } } - if bottom-panel-visibility == BottomPanelVisibility.TextErrors: TextErrorsPanel { + if bottom_panel_visibility == BottomPanelVisibility.TextErrors: TextErrorsPanel { width: parent.width; height: parent.height; } diff --git a/krokiet/ui/color_palette.slint b/krokiet/ui/color_palette.slint index 4a0bada3f..35d7bc64a 100644 --- a/krokiet/ui/color_palette.slint +++ b/krokiet/ui/color_palette.slint @@ -6,9 +6,30 @@ export global ColorPalette { in-out property tab_selected_color: dark_scheme ? #353535 : #5e5e5e; in-out property tab_hovered_color: dark_scheme ? #49494926 : #80808014; // ListView - in-out property list_view_normal_color: dark_scheme ? #222222 : #dddddd; - in-out property list_view_normal_header_color: dark_scheme ? #111111 : #888888; - in-out property list_view_normal_selected_header: dark_scheme ? #444444 : #cccccc; + in-out property list_view_item_color: dark_scheme ? #222222 : #dddddd; + in-out property list_view_item_hovered_color: dark_scheme ? #333333 : #d2d2d2; + in-out property list_view_item_selected_color: dark_scheme ? #444444 : #cccccc; + in-out property list_view_item_selected_hovered_color: dark_scheme ? #555555 : #bbbbbb; + + in-out property list_view_header_color: dark_scheme ? #111111 : #888888; + in-out property list_view_clicked_header_color: dark_scheme ? #1a1a1a : #808080; + // Popup in-out property popup_background: dark_scheme ? #353535 : #5e5e5e; + + + public pure function get_listview_color(selected: bool, hovered: bool) -> color { + if (selected) { + return hovered ? self.list_view_item_selected_hovered_color : self.list_view_item_selected_color; + } else { + return hovered ? self.list_view_item_hovered_color : self.list_view_item_color; + } + } + public pure function get_listview_color_with_header(selected: bool, hovered: bool, header: bool) -> color { + if (header) { + return selected ? self.list_view_clicked_header_color : self.list_view_header_color; + } else { + return self.get_listview_color(selected, hovered); + } + } } diff --git a/krokiet/ui/gui_state.slint b/krokiet/ui/gui_state.slint index 59dc4a469..9c9952fb7 100644 --- a/krokiet/ui/gui_state.slint +++ b/krokiet/ui/gui_state.slint @@ -18,7 +18,7 @@ export global GuiState { in-out property visible_tool_settings; in-out property available_subsettings: active_tab == CurrentTab.SimilarImages || active_tab == CurrentTab.DuplicateFiles || active_tab == CurrentTab.SimilarVideos || active_tab == CurrentTab.SimilarMusic || active_tab == CurrentTab.BigFiles || active_tab == CurrentTab.BrokenFiles; - in-out property active_tab: CurrentTab.DuplicateFiles; + in-out property active_tab: CurrentTab.SimilarImages; in-out property is_tool_tab_active: active_tab != CurrentTab.Settings && active_tab != CurrentTab.About; 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"}]; diff --git a/krokiet/ui/included_directories.slint b/krokiet/ui/included_directories.slint index 5ee28819c..e55ba227e 100644 --- a/krokiet/ui/included_directories.slint +++ b/krokiet/ui/included_directories.slint @@ -31,7 +31,7 @@ export component IncludedDirectories { border_radius: 5px; width: parent.width; - background: touch-area.has-hover ? (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color) : (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color); + background: ColorPalette.get_listview_color(r.selected_row, touch-area.has-hover); touch_area := TouchArea { clicked => { if (current_index == -1) { @@ -90,7 +90,7 @@ export component ExcludedDirectories { border_radius: 5px; width: parent.width; - background: touch-area.has-hover ? (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color) : (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color); + background: ColorPalette.get_listview_color(r.selected_row, touch-area.has-hover); touch_area := TouchArea { clicked => { diff --git a/krokiet/ui/left_side_panel.slint b/krokiet/ui/left_side_panel.slint index 2587758e0..778feab5c 100644 --- a/krokiet/ui/left_side_panel.slint +++ b/krokiet/ui/left_side_panel.slint @@ -13,13 +13,13 @@ component TabItem { Rectangle { width: parent.width; horizontal-stretch: 1.0; - background: touch-area.has-hover ? ColorPalette.tab-hovered-color : transparent; + background: touch-area.has-hover ? ColorPalette.tab_hovered_color : transparent; touch_area := TouchArea { clicked => { - if (GuiState.active_tab == root.curr-tab) { + if (GuiState.active_tab == root.curr_tab) { return; } - GuiState.active_tab = root.curr-tab; + GuiState.active_tab = root.curr_tab; Callabler.tab_changed(); changed_current_tab(); } @@ -28,12 +28,12 @@ component TabItem { HorizontalLayout { width: parent.width; - alignment: LayoutAlignment.end; + alignment: LayoutAlignment.start; layout_rectangle := VerticalLayout { empty_rectangle := Rectangle { } current_rectangle := Rectangle { - visible: (GuiState.active_tab == root.curr-tab); + visible: (GuiState.active_tab == root.curr_tab); border-radius: 2px; width: 5px; height: 0px; @@ -54,10 +54,10 @@ component TabItem { } states [ - is-selected when GuiState.active_tab == root.curr-tab: { + is-selected when GuiState.active_tab == root.curr_tab: { current_rectangle.height: layout_rectangle.height; } - is-not-selected when GuiState.active_tab != root.curr-tab: { + is-not-selected when GuiState.active_tab != root.curr_tab: { current_rectangle.height: 0px; } ] @@ -74,7 +74,7 @@ export component LeftSidePanel { height: 80px; Image { width: parent.height; - source: @image-url("../icons/logo.png"); + source: @image-url("../icons/logo_small.png"); image-fit: ImageFit.contain; } touch_area := TouchArea { @@ -88,7 +88,7 @@ export component LeftSidePanel { } ListView { - out property element-size: 25px; + out property element_size: 25px; out property <[{name: string, tab: CurrentTab}]> speed_model: [ {name: "Duplicate Files", tab: CurrentTab.DuplicateFiles}, {name: "Empty Folders", tab: CurrentTab.EmptyFolders}, @@ -104,7 +104,7 @@ export component LeftSidePanel { ]; for r[idx] in speed_model: TabItem { - height: parent.element-size; + height: parent.element_size; scanning: scanning; text: r.name; curr_tab: r.tab; @@ -132,6 +132,8 @@ export component LeftSidePanel { HorizontalLayout { alignment: end; Button { + checkable: true; + checked: GuiState.visible_tool_settings; visible: GuiState.available_subsettings; min-width: 20px; min-height: 20px; @@ -139,7 +141,7 @@ export component LeftSidePanel { preferred-height: self.width; icon: @image-url("../icons/subsettings.svg"); clicked => { - GuiState.visible_tool_settings = !GuiState.visible-tool-settings; + GuiState.visible_tool_settings = !GuiState.visible_tool_settings; } } } diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint index cce13fdc6..ed433509d 100644 --- a/krokiet/ui/main_lists.slint +++ b/krokiet/ui/main_lists.slint @@ -45,7 +45,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Size", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, path_px, mod_px]; + column_sizes: [35px, size_px, name_px, path_px, mod_px]; values <=> duplicate_files_model; parentPathIdx: 3; fileNameIdx: 2; @@ -56,8 +56,8 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Folder Name", "Path", "Modification Date"]; - column-sizes: [35px, name_px, path_px, mod_px]; - values <=> empty-folder-model; + column_sizes: [35px, name_px, path_px, mod_px]; + values <=> empty_folder_model; parentPathIdx: 2; fileNameIdx: 1; } @@ -67,7 +67,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Size", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, path_px, mod_px]; + column_sizes: [35px, size_px, name_px, path_px, mod_px]; values <=> big_files_model; parentPathIdx: 3; fileNameIdx: 2; @@ -78,8 +78,8 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, name_px, path_px, mod_px]; - values <=> empty-files-model; + column_sizes: [35px, name_px, path_px, mod_px]; + values <=> empty_files_model; parentPathIdx: 2; fileNameIdx: 1; } @@ -89,7 +89,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, name_px, path_px, mod_px]; + column_sizes: [35px, name_px, path_px, mod_px]; values <=> temporary_files_model; parentPathIdx: 2; fileNameIdx: 1; @@ -100,8 +100,8 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Similarity", "Size", "Dimensions", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, 80px, 80px, 80px, name_px, path_px, mod_px]; - values <=> similar-images-model; + column_sizes: [35px, 80px, 80px, 80px, name_px, path_px, mod_px]; + values <=> similar_images_model; parentPathIdx: 5; fileNameIdx: 4; } @@ -111,7 +111,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Size", "File Name", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, path_px, mod_px]; + column_sizes: [35px, size_px, name_px, path_px, mod_px]; values <=> similar_videos_model; parentPathIdx: 3; fileNameIdx: 2; @@ -122,7 +122,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Size", "File Name", "Title","Artist", "Year", "Bitrate", "Length", "Genre", "Path", "Modification Date"]; - column-sizes: [35px, size_px, name_px, 80px, 80px, 80px, 80px, 80px, 80px, path_px, mod_px]; + column_sizes: [35px, size_px, name_px, 80px, 80px, 80px, 80px, 80px, 80px, path_px, mod_px]; values <=> similar_music_model; parentPathIdx: 9; fileNameIdx: 2; @@ -133,7 +133,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "Symlink Name", "Symlink Folder", "Destination Path", "Modification Date"]; - column-sizes: [35px, name_px, path_px, path_px, mod_px]; + column_sizes: [35px, name_px, path_px, path_px, mod_px]; values <=> invalid_symlinks_model; parentPathIdx: 2; fileNameIdx: 1; @@ -144,7 +144,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "File Name", "Path", "Type of Error", "Size", "Modification Date"]; - column-sizes: [35px, name_px, path_px, 200px, size_px, mod_px]; + column_sizes: [35px, name_px, path_px, 200px, size_px, mod_px]; values <=> broken_files_model; parentPathIdx: 2; fileNameIdx: 1; @@ -155,7 +155,7 @@ export component MainList { min-width: 200px; height: parent.height; columns: ["Selection", "File Name", "Path", "Current Extension", "Proper Extension"]; - column-sizes: [35px, name_px, path_px, 40px, 200px]; + column_sizes: [35px, name_px, path_px, 40px, 200px]; values <=> bad_extensions_model; parentPathIdx: 2; fileNameIdx: 1; @@ -170,7 +170,8 @@ export component MainList { } focus_item := FocusScope { - width: 0px; // Hack to not steal first click from other components - https://github.com/slint-ui/slint/issues/3503 + // TODO consider to remove that - looks that hack not works and is unnecessary + // width: 0px; // Hack to not steal first click from other components - https://github.com/slint-ui/slint/issues/3503 // Hack not works https://github.com/slint-ui/slint/issues/3503#issuecomment-1817809834 because disables key-released event key-released(event) => { @@ -180,12 +181,34 @@ export component MainList { if (GuiState.active_tab == CurrentTab.EmptyFiles) { empty_files.released_key(event); } else if (GuiState.active_tab == CurrentTab.EmptyFolders) { - empty-folders.released_key(event); + empty_folders.released_key(event); } else if (GuiState.active_tab == CurrentTab.SimilarImages) { - similar-images.released_key(event); + similar_images.released_key(event); + } else if (GuiState.active_tab == CurrentTab.BadExtensions) { + bad_extensions.released_key(event); + } else if (GuiState.active_tab == CurrentTab.BigFiles) { + big_files.released_key(event); + } else if (GuiState.active_tab == CurrentTab.DuplicateFiles) { + duplicates.released_key(event); + } else if (GuiState.active_tab == CurrentTab.TemporaryFiles) { + temporary_files.released_key(event); + } else if (GuiState.active_tab == CurrentTab.SimilarVideos) { + similar_videos.released_key(event); + } else if (GuiState.active_tab == CurrentTab.SimilarMusic) { + similar_music.released_key(event); + } else if (GuiState.active_tab == CurrentTab.InvalidSymlinks) { + invalid_symlink.released_key(event); + } else if (GuiState.active_tab == CurrentTab.BrokenFiles) { + broken_files.released_key(event); } else { - debug("Non handled key in main_lists.slint"); + debug("Non handled key in main_lists.slint", event, GuiState.active_tab); } + + + + // else { + // debug("Non handled key in main_lists.slint", event, GuiState.active_tab); + // } accept } } diff --git a/krokiet/ui/main_window.slint b/krokiet/ui/main_window.slint index d0f28fb6a..55fabdfd2 100644 --- a/krokiet/ui/main_window.slint +++ b/krokiet/ui/main_window.slint @@ -47,7 +47,7 @@ export component MainWindow inherits Window { in-out property <[MainListModel]> empty_folder_model: []; in-out property <[MainListModel]> big_files_model: []; in-out property <[MainListModel]> empty_files_model: []; - in-out property <[MainListModel]> temporary-files_model: []; + in-out property <[MainListModel]> temporary_files_model: []; in-out property <[MainListModel]> similar_images_model: []; in-out property <[MainListModel]> similar_videos_model: []; in-out property <[MainListModel]> similar_music_model: []; @@ -82,7 +82,7 @@ export component MainWindow inherits Window { empty_folder_model <=> root.empty_folder_model; big_files_model <=> root.big_files_model; empty_files_model <=> root.empty_files_model; - temporary-files_model <=> root.temporary-files_model; + temporary_files_model <=> root.temporary_files_model; similar_images_model <=> root.similar_images_model; similar_videos_model <=> root.similar_videos_model; similar_music_model <=> root.similar_music_model; @@ -118,9 +118,21 @@ export component MainWindow inherits Window { } action_buttons := ActionButtons { + duplicate_files_model <=> root.duplicate_files_model; + empty_folder_model <=> root.empty_folder_model; + big_files_model <=> root.big_files_model; + empty_files_model <=> root.empty_files_model; + temporary_files_model <=> root.temporary_files_model; + similar_images_model <=> root.similar_images_model; + similar_videos_model <=> root.similar_videos_model; + similar_music_model <=> root.similar_music_model; + invalid_symlinks_model <=> root.invalid_symlinks_model; + broken_files_model <=> root.broken_files_model; + bad_extensions_model <=> root.bad_extensions_model; + vertical-stretch: 0.0; scanning <=> root.scanning; - stop_requested <=> root.stop-requested; + stop_requested <=> root.stop_requested; scan_stopping => { text_summary_text = "Stopping scan, please wait..."; root.scan_stopping(); @@ -156,7 +168,7 @@ export component MainWindow inherits Window { } bottom_panel := BottomPanel { - bottom-panel-visibility <=> action_buttons.bottom_panel_visibility; + bottom_panel_visibility <=> action_buttons.bottom_panel_visibility; vertical-stretch: 0.0; folder_choose_requested(included_directories) => { root.folder_choose_requested(included_directories) diff --git a/krokiet/ui/popup_new_directories.slint b/krokiet/ui/popup_new_directories.slint index 1de76e5f4..95a094897 100644 --- a/krokiet/ui/popup_new_directories.slint +++ b/krokiet/ui/popup_new_directories.slint @@ -44,17 +44,16 @@ export component PopupNewDirectories inherits Rectangle { TextEdit { vertical-stretch: 1.0; - text <=> text-data; + text <=> text_data; } HorizontalLayout { min-height: 20px; Button { - enabled: text-data != ""; + enabled: text_data != ""; text: "OK"; clicked => { Callabler.added_manual_directories(GuiState.choosing_include_directories, text_data); - debug("OK"); popup_window.close(); } } @@ -62,7 +61,6 @@ export component PopupNewDirectories inherits Rectangle { Button { text: "Cancel"; clicked => { - debug("Cancel"); popup_window.close(); } } diff --git a/krokiet/ui/progress.slint b/krokiet/ui/progress.slint index 2c874f574..5f632e333 100644 --- a/krokiet/ui/progress.slint +++ b/krokiet/ui/progress.slint @@ -11,7 +11,7 @@ export component Progress { preferred-height: 40px; VerticalLayout { Text { - text: progress-datas.step-name; + text: progress-datas.step_name; horizontal-alignment: TextHorizontalAlignment.center; } @@ -35,9 +35,9 @@ export component Progress { VerticalLayout { alignment: LayoutAlignment.center; ProgressIndicator { - visible: progress_datas.current-progress >= -0.001; + visible: progress_datas.current_progress >= -0.001; height: 8px; - progress: progress_datas.current-progress / 100.0; + progress: progress_datas.current_progress / 100.0; } } @@ -45,7 +45,7 @@ export component Progress { alignment: LayoutAlignment.center; ProgressIndicator { height: 8px; - progress: progress_datas.all-progress / 100.0; + progress: progress_datas.all_progress / 100.0; } } } @@ -53,14 +53,14 @@ export component Progress { VerticalLayout { spacing: 5px; Text { - visible: progress_datas.current-progress >= -0.001; + visible: progress_datas.current_progress >= -0.001; vertical-alignment: TextVerticalAlignment.center; - text: progress_datas.current-progress + "%"; + text: progress_datas.current_progress + "%"; } Text { vertical-alignment: TextVerticalAlignment.center; - text: progress_datas.all-progress + "%"; + text: progress_datas.all_progress + "%"; } } } diff --git a/krokiet/ui/selectable_tree_view.slint b/krokiet/ui/selectable_tree_view.slint index b72edbd1d..e96327040 100644 --- a/krokiet/ui/selectable_tree_view.slint +++ b/krokiet/ui/selectable_tree_view.slint @@ -15,7 +15,7 @@ export component SelectableTableView inherits Rectangle { {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; + private property column_number: column_sizes.length + 1; // This idx, starts from zero, but since first is always a checkbox, and is not in model.val values, remove 1 from idx in-out property parentPathIdx; in-out property fileNameIdx; @@ -32,7 +32,7 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { spacing: 5px; for title [idx] in root.columns: HorizontalLayout { - width: root.column-sizes[idx]; + width: root.column_sizes[idx]; Text { overflow: elide; text: title; @@ -73,33 +73,35 @@ export component SelectableTableView inherits Rectangle { border-radius: 5px; height: 20px; - background: r.header-row ? ColorPalette.list_view_normal_header_color : (touch-area.has-hover ? (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color) : (r.selected_row ? ColorPalette.list-view-normal-selected-header : ColorPalette.list_view_normal_color)); + background: ColorPalette.get_listview_color_with_header(r.selected_row, touch-area.has-hover, r.header_row); touch_area := TouchArea { function clicked_manual() { - if (!r.header_row) { - if (root.selected-item == -1) { + // We don't allow to select header row + // unless it contains data, which is true only for for modes with reference folders + if (!r.header_row || r.val_str.length > 0) { + if (root.selected_item == -1) { r.selected_row = !r.selected_row; - root.selected-item = idx; + root.selected_item = idx; } else { - if (!r.selected_row && root.selected-item != idx) { + if (!r.selected_row && root.selected_item != idx) { r.selected_row = !r.selected_row; - root.values[root.selected-item].selected_row = false; - root.selected-item = idx; + root.values[root.selected_item].selected_row = false; + root.selected_item = idx; } } if (root.selected_item != -1) { - Callabler.load_image_preview(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]); + showPreview(); } else { GuiState.preview_visible = false; } } } double-clicked => { - if (r.header_row && !r.filled_header_row) { - return; - } - Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) + if (r.header_row && !r.filled_header_row) { + return; + } + openSelectedItem(); } pointer-event(event) => { // TODO this should be clicked by double-click - https://github.com/slint-ui/slint/issues/4235 @@ -119,9 +121,9 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { CheckBox { - visible: !r.header-row; - checked: r.checked && !r.header-row; - width: root.column-sizes[0]; + visible: !r.header_row; + checked: r.checked && !r.header_row; + width: root.column_sizes[0]; toggled => { r.checked = self.checked; } @@ -130,7 +132,7 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { spacing: 5px; for f [idx] in r.val_str: Text { - width: root.column-sizes[idx + 1]; + width: root.column_sizes[idx + 1]; text: f; font-size: 12px; vertical-alignment: center; @@ -144,17 +146,29 @@ export component SelectableTableView inherits Rectangle { public function deselect_selected_item() { if (root.selected_item != -1) { - root.values[root.selected-item].selected_row = false; - root.selected-item = -1; + root.values[root.selected_item].selected_row = false; + root.selected_item = -1; } } + function showPreview() { + Callabler.load_image_preview(root.values[root.selected_item].val_str[root.parentPathIdx - 1] + "/" + root.values[root.selected_item].val_str[root.fileNameIdx - 1]); + } + + function openSelectedItem() { + Callabler.item_opened(root.values[root.selected_item].val_str[root.parentPathIdx - 1] + "/" + root.values[root.selected_item].val_str[root.fileNameIdx - 1]); + } + // TODO this should work with multiple selection and shift and control key - problably logic will need to be set in global state public function released_key(event: KeyEvent) { if (event.text == " ") { - if (root.selected_item != -1) { + if (root.selected_item != -1 && !root.values[root.selected_item].header_row) { root.values[root.selected_item].checked = !root.values[root.selected_item].checked; } + } else if (event.text == "\n" ) { + if (root.selected_item != -1) { + openSelectedItem(); + } } else if (event.text == Key.DownArrow) { if (root.selected_item != -1) { if (root.values.length - 1 == root.selected_item) { @@ -171,6 +185,7 @@ export component SelectableTableView inherits Rectangle { root.selected_item += 1; } root.values[root.selected_item].selected_row = true; + showPreview(); } } else { // Select last item if nothing is selected @@ -181,6 +196,7 @@ export component SelectableTableView inherits Rectangle { root.selected_item = 0; } root.values[root.selected_item].selected_row = true; + showPreview(); } } } else if (event.text == Key.UpArrow) { @@ -202,6 +218,7 @@ export component SelectableTableView inherits Rectangle { } if (root.selected_item != -1) { root.values[root.selected_item].selected_row = true; + showPreview(); } } } else { @@ -209,6 +226,7 @@ export component SelectableTableView inherits Rectangle { if (root.values.length > 0) { root.selected_item = root.values.length - 1; root.values[root.selected_item].selected_row = true; + showPreview(); } } } diff --git a/krokiet/ui/settings_list.slint b/krokiet/ui/settings_list.slint index e0c44e5d8..cfb48a4ce 100644 --- a/krokiet/ui/settings_list.slint +++ b/krokiet/ui/settings_list.slint @@ -213,11 +213,11 @@ export component SettingsList inherits VerticalLayout { } CheckBoxComponent { - name: "Recursive"; + name: "Recursive search"; model <=> Settings.recursive_search; } CheckBoxComponent { - name: "Use Cache"; + name: "Use cache"; model <=> Settings.use_cache; } CheckBoxComponent {