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 20230dc8b..d016c5392 100644 Binary files a/krokiet/icons/logo.png and b/krokiet/icons/logo.png differ diff --git a/krokiet/icons/logo_small.png b/krokiet/icons/logo_small.png new file mode 100644 index 000000000..b1d1e0632 Binary files /dev/null and b/krokiet/icons/logo_small.png differ 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 {