diff --git a/Cargo.lock b/Cargo.lock index 1cff485..4d4986e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1328,6 +1328,7 @@ name = "czkawka_slint" version = "6.1.0" dependencies = [ "chrono", + "crossbeam-channel", "czkawka_core", "open", "rand", diff --git a/czkawka_slint_gui/Cargo.toml b/czkawka_slint_gui/Cargo.toml index ee1e794..fd1c0eb 100644 --- a/czkawka_slint_gui/Cargo.toml +++ b/czkawka_slint_gui/Cargo.toml @@ -17,6 +17,7 @@ rand = "0.8.5" czkawka_core = { version = "6.1.0", path = "../czkawka_core" } chrono = "0.4.31" open="5.0.0" +crossbeam-channel = "0.5.8" [build-dependencies] #slint-build = "1.2.2" diff --git a/czkawka_slint_gui/src/connect_progress_receiver.rs b/czkawka_slint_gui/src/connect_progress_receiver.rs new file mode 100644 index 0000000..b3ea2a3 --- /dev/null +++ b/czkawka_slint_gui/src/connect_progress_receiver.rs @@ -0,0 +1,39 @@ +use crate::{MainWindow, ProgressToSend}; +use crossbeam_channel::Receiver; +use czkawka_core::common_dir_traversal::ProgressData; +use slint::{ComponentHandle, SharedString}; +use std::thread; + +pub fn connect_progress_gathering(app: &MainWindow, progress_receiver: Receiver) { + let a = app.as_weak(); + + thread::spawn(move || loop { + let progress_data = progress_receiver.recv().unwrap(); + + a.upgrade_in_event_loop(move |app| { + let (all_stages, current_stage) = common_get_data(&progress_data); + let to_send = ProgressToSend { + all_progress: (all_stages * 100.0) as i32, + current_progress: (current_stage * 100.0) as i32, + step_name: SharedString::from(format!("Checked {} folders", progress_data.entries_checked)), + }; + + app.set_progress_datas(to_send); + }) + .unwrap(); + }); +} +fn common_get_data(item: &ProgressData) -> (f64, f64) { + if item.entries_to_check != 0 { + let all_stages = (item.current_stage as f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64; + let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages }; + + let current_stage = (item.entries_checked) as f64 / item.entries_to_check as f64; + let current_stage = if current_stage > 0.99 { 0.99 } else { current_stage }; + (all_stages, current_stage) + } else { + let all_stages = (item.current_stage as f64) / (item.max_stage + 1) as f64; + let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages }; + (all_stages, 0f64) + } +} diff --git a/czkawka_slint_gui/src/connect_scan.rs b/czkawka_slint_gui/src/connect_scan.rs index 8faa706..774c59d 100644 --- a/czkawka_slint_gui/src/connect_scan.rs +++ b/czkawka_slint_gui/src/connect_scan.rs @@ -1,5 +1,7 @@ -use crate::{split_path, CurrentTab, MainWindow}; +use crate::{split_path, CurrentTab, MainWindow, ProgressToSend}; use chrono::NaiveDateTime; +use crossbeam_channel::Sender; +use czkawka_core::common_dir_traversal::ProgressData; use czkawka_core::common_tool::CommonData; use czkawka_core::empty_folder::EmptyFolder; use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak}; @@ -7,27 +9,32 @@ use std::path::PathBuf; use std::rc::Rc; use std::thread; -pub fn connect_scan_button(app: &MainWindow) { +pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender) { let a = app.as_weak(); app.on_scanned(move |active_tab| { let app = a.upgrade().unwrap(); app.set_scanning(true); + app.set_progress_datas(ProgressToSend { + all_progress: 0, + current_progress: 0, + step_name: SharedString::from(""), + }); let a = app.as_weak(); match active_tab { CurrentTab::EmptyFolders => { - scan_empty_folders(a); + scan_empty_folders(a, &progress_sender); } _ => panic!(), } }); } -fn scan_empty_folders(a: Weak) { +fn scan_empty_folders(a: Weak, progress_sender: &Sender) { thread::spawn(move || { let mut ef = EmptyFolder::new(); ef.set_included_directory(vec![PathBuf::from("/home/rafal/Desktop")]); - ef.find_empty_folders(None, None); + ef.find_empty_folders(None, Some(progress_sender)); ef.get_empty_folder_list(); diff --git a/czkawka_slint_gui/src/main.rs b/czkawka_slint_gui/src/main.rs index 8422ef0..095094f 100644 --- a/czkawka_slint_gui/src/main.rs +++ b/czkawka_slint_gui/src/main.rs @@ -1,7 +1,9 @@ mod connect_delete; mod connect_open; +mod connect_progress_receiver; mod connect_scan; +use crossbeam_channel::{unbounded, Receiver, Sender}; use std::path::Path; use std::rc::Rc; @@ -9,17 +11,22 @@ use crate::connect_delete::connect_delete_button; use crate::connect_open::connect_open_items; use crate::connect_scan::connect_scan_button; +use crate::connect_progress_receiver::connect_progress_gathering; +use czkawka_core::common_dir_traversal::ProgressData; use slint::{ModelRc, SharedString, VecModel}; slint::include_modules!(); fn main() { let app = MainWindow::new().unwrap(); //.run().unwrap(); + let (progress_sender, progress_receiver): (Sender, Receiver) = unbounded(); + // Fills model at start, don't really needed too much after testing to_remove_debug(&app); connect_delete_button(&app); - connect_scan_button(&app); + connect_scan_button(&app, progress_sender); connect_open_items(&app); + connect_progress_gathering(&app, progress_receiver); app.run().unwrap(); } @@ -29,7 +36,7 @@ type ModelType = VecModel<(bool, bool, bool, ModelRc)>; pub fn to_remove_debug(app: &MainWindow) { let row_data: Rc = Rc::new(VecModel::default()); - for r in 0..100_000 { + for r in 0..1_000_000 { let items = VecModel::default(); for c in 0..3 { diff --git a/czkawka_slint_gui/ui/action_buttons.slint b/czkawka_slint_gui/ui/action_buttons.slint index 79095d6..e173b31 100644 --- a/czkawka_slint_gui/ui/action_buttons.slint +++ b/czkawka_slint_gui/ui/action_buttons.slint @@ -1,5 +1,4 @@ import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox} from "std-widgets.slint"; -import {SelectableTableView} from "selectable_tree_view.slint"; import {LeftSidePanel} from "left_side_panel.slint"; import {MainList} from "main_lists.slint"; import {CurrentTab} from "common.slint"; diff --git a/czkawka_slint_gui/ui/common.slint b/czkawka_slint_gui/ui/common.slint index b1de472..700cae1 100644 --- a/czkawka_slint_gui/ui/common.slint +++ b/czkawka_slint_gui/ui/common.slint @@ -7,4 +7,10 @@ export enum CurrentTab { export enum TypeOfOpenedItem { CurrentItem, ParentItem, +} + +export struct ProgressToSend { + current_progress: int, + all_progress: int, + step_name: string, } \ No newline at end of file diff --git a/czkawka_slint_gui/ui/main_window.slint b/czkawka_slint_gui/ui/main_window.slint index 21f4f63..2a28e5d 100644 --- a/czkawka_slint_gui/ui/main_window.slint +++ b/czkawka_slint_gui/ui/main_window.slint @@ -2,19 +2,26 @@ import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListV import {SelectableTableView} from "selectable_tree_view.slint"; import {LeftSidePanel} from "left_side_panel.slint"; import {MainList} from "main_lists.slint"; -import {CurrentTab} from "common.slint"; +import {CurrentTab, ProgressToSend} from "common.slint"; import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; export component MainWindow inherits Window { callback deleted; callback scanned(CurrentTab); callback item_opened(string); + min-width: 300px; preferred-width: 1024px; min-height: 300px; preferred-height: 600px; in-out property scanning: false; + in-out property progress_datas : { + current_progress: 15, + all_progress: 20, + step_name: "Cache", + }; in-out property active-tab: CurrentTab.EmptyFolders; in-out property <[{checked: bool, header_row: bool, selected_row: bool, val:[string]}]> empty_folder_model: [ @@ -63,14 +70,21 @@ export component MainWindow inherits Window { active-tab <=> root.active-tab; } - MainList { + VerticalLayout { horizontal-stretch: 1.0; - active-tab <=> root.active-tab; - empty_folder_model <=> root.empty_folder_model; - empty_files_model <=> root.empty_files_model; - similar_images_model <=> root.similar_images_model; - - item_opened(item) => {item_opened(item)} + MainList { + vertical-stretch: 1.0; + active-tab <=> root.active-tab; + empty_folder_model <=> root.empty_folder_model; + empty_files_model <=> root.empty_files_model; + similar_images_model <=> root.similar_images_model; + + item_opened(item) => {item_opened(item)} + } + Progress { + horizontal-stretch: 0.0; + progress_datas <=> root.progress_datas; + } } } ActionButtons { diff --git a/czkawka_slint_gui/ui/selectable_tree_view.slint b/czkawka_slint_gui/ui/selectable_tree_view.slint index 7288be5..019d3dc 100644 --- a/czkawka_slint_gui/ui/selectable_tree_view.slint +++ b/czkawka_slint_gui/ui/selectable_tree_view.slint @@ -18,9 +18,18 @@ export component SelectableTableView inherits Rectangle { in-out property selected_item: -1; + forward-focus: focus_item; + + focus_item := FocusScope { + key-released(event) => { + debug(event); + accept + } + } + VerticalBox { padding: 5px; - // Widgets + forward-focus: focus-item; HorizontalLayout { padding: 5px; spacing: 5px; @@ -32,11 +41,13 @@ export component SelectableTableView inherits Rectangle { Rectangle { width: 1px; background: gray; + forward-focus: focus-item; TouchArea { width: 5px; x: (parent.width - self.width) / 2; property cached; + forward-focus: focus-item; pointer-event(event) => { if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { self.cached = root.column_sizes[idx]; @@ -58,14 +69,17 @@ export component SelectableTableView inherits Rectangle { } list_view := ListView { min-width: 100px; + forward-focus: focus-item; for r[idx] in root.values : Rectangle { + forward-focus: focus-item; height: 30px; // TODO move this into singleton background: r.header-row ? #888888 : (touch-area.has-hover ? (r.selected_row ? #cccccc : #dddddd) : (r.selected_row ? #cccccc: #dddddd)); // background: touch-area.has-hover ? (selected ? #333333 : #222222) : (selected ? #333333: #222222); touch_area:= TouchArea { + forward-focus: focus-item; clicked => { if (!r.header_row) { r.selected_row = !r.selected_row; @@ -82,6 +96,7 @@ export component SelectableTableView inherits Rectangle { } } pointer-event(event) => { + debug(event); // TODO this should be clicked by double-click if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) { root.item_opened(r.val[root.parentPathIdx - 1]) @@ -94,16 +109,16 @@ export component SelectableTableView inherits Rectangle { HorizontalLayout { padding: 5px; spacing: 5px; + forward-focus: focus-item; CheckBox { - //min-width: 200px; visible: !r.header-row; checked: r.checked && !r.header-row; width: root.column-sizes[0]; + forward-focus: focus-item; toggled => { r.checked = self.checked; } - // preferred-width: root.column-sizes[0]; } HorizontalLayout { @@ -112,6 +127,7 @@ export component SelectableTableView inherits Rectangle { width: root.column-sizes[idx + 1]; text: f; font-size: 12px; + forward-focus: focus-item; vertical-alignment: center;