Make
This commit is contained in:
parent
e859c777a4
commit
6d8fe135ce
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1328,6 +1328,7 @@ name = "czkawka_slint"
|
||||||
version = "6.1.0"
|
version = "6.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"crossbeam-channel",
|
||||||
"czkawka_core",
|
"czkawka_core",
|
||||||
"open",
|
"open",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
@ -17,6 +17,7 @@ rand = "0.8.5"
|
||||||
czkawka_core = { version = "6.1.0", path = "../czkawka_core" }
|
czkawka_core = { version = "6.1.0", path = "../czkawka_core" }
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
open="5.0.0"
|
open="5.0.0"
|
||||||
|
crossbeam-channel = "0.5.8"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
#slint-build = "1.2.2"
|
#slint-build = "1.2.2"
|
||||||
|
|
39
czkawka_slint_gui/src/connect_progress_receiver.rs
Normal file
39
czkawka_slint_gui/src/connect_progress_receiver.rs
Normal file
|
@ -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<ProgressData>) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::{split_path, CurrentTab, MainWindow};
|
use crate::{split_path, CurrentTab, MainWindow, ProgressToSend};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use czkawka_core::common_dir_traversal::ProgressData;
|
||||||
use czkawka_core::common_tool::CommonData;
|
use czkawka_core::common_tool::CommonData;
|
||||||
use czkawka_core::empty_folder::EmptyFolder;
|
use czkawka_core::empty_folder::EmptyFolder;
|
||||||
use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak};
|
use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak};
|
||||||
|
@ -7,27 +9,32 @@ use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
pub fn connect_scan_button(app: &MainWindow) {
|
pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender<ProgressData>) {
|
||||||
let a = app.as_weak();
|
let a = app.as_weak();
|
||||||
app.on_scanned(move |active_tab| {
|
app.on_scanned(move |active_tab| {
|
||||||
let app = a.upgrade().unwrap();
|
let app = a.upgrade().unwrap();
|
||||||
app.set_scanning(true);
|
app.set_scanning(true);
|
||||||
|
app.set_progress_datas(ProgressToSend {
|
||||||
|
all_progress: 0,
|
||||||
|
current_progress: 0,
|
||||||
|
step_name: SharedString::from(""),
|
||||||
|
});
|
||||||
|
|
||||||
let a = app.as_weak();
|
let a = app.as_weak();
|
||||||
match active_tab {
|
match active_tab {
|
||||||
CurrentTab::EmptyFolders => {
|
CurrentTab::EmptyFolders => {
|
||||||
scan_empty_folders(a);
|
scan_empty_folders(a, &progress_sender);
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_empty_folders(a: Weak<MainWindow>) {
|
fn scan_empty_folders(a: Weak<MainWindow>, progress_sender: &Sender<ProgressData>) {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut ef = EmptyFolder::new();
|
let mut ef = EmptyFolder::new();
|
||||||
ef.set_included_directory(vec![PathBuf::from("/home/rafal/Desktop")]);
|
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();
|
ef.get_empty_folder_list();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
mod connect_delete;
|
mod connect_delete;
|
||||||
mod connect_open;
|
mod connect_open;
|
||||||
|
mod connect_progress_receiver;
|
||||||
mod connect_scan;
|
mod connect_scan;
|
||||||
|
|
||||||
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
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_open::connect_open_items;
|
||||||
use crate::connect_scan::connect_scan_button;
|
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};
|
use slint::{ModelRc, SharedString, VecModel};
|
||||||
|
|
||||||
slint::include_modules!();
|
slint::include_modules!();
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = MainWindow::new().unwrap(); //.run().unwrap();
|
let app = MainWindow::new().unwrap(); //.run().unwrap();
|
||||||
|
|
||||||
|
let (progress_sender, progress_receiver): (Sender<ProgressData>, Receiver<ProgressData>) = unbounded();
|
||||||
|
// Fills model at start, don't really needed too much after testing
|
||||||
to_remove_debug(&app);
|
to_remove_debug(&app);
|
||||||
|
|
||||||
connect_delete_button(&app);
|
connect_delete_button(&app);
|
||||||
connect_scan_button(&app);
|
connect_scan_button(&app, progress_sender);
|
||||||
connect_open_items(&app);
|
connect_open_items(&app);
|
||||||
|
connect_progress_gathering(&app, progress_receiver);
|
||||||
|
|
||||||
app.run().unwrap();
|
app.run().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -29,7 +36,7 @@ type ModelType = VecModel<(bool, bool, bool, ModelRc<SharedString>)>;
|
||||||
pub fn to_remove_debug(app: &MainWindow) {
|
pub fn to_remove_debug(app: &MainWindow) {
|
||||||
let row_data: Rc<ModelType> = Rc::new(VecModel::default());
|
let row_data: Rc<ModelType> = Rc::new(VecModel::default());
|
||||||
|
|
||||||
for r in 0..100_000 {
|
for r in 0..1_000_000 {
|
||||||
let items = VecModel::default();
|
let items = VecModel::default();
|
||||||
|
|
||||||
for c in 0..3 {
|
for c in 0..3 {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox} from "std-widgets.slint";
|
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 {LeftSidePanel} from "left_side_panel.slint";
|
||||||
import {MainList} from "main_lists.slint";
|
import {MainList} from "main_lists.slint";
|
||||||
import {CurrentTab} from "common.slint";
|
import {CurrentTab} from "common.slint";
|
||||||
|
|
|
@ -7,4 +7,10 @@ export enum CurrentTab {
|
||||||
export enum TypeOfOpenedItem {
|
export enum TypeOfOpenedItem {
|
||||||
CurrentItem,
|
CurrentItem,
|
||||||
ParentItem,
|
ParentItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
export struct ProgressToSend {
|
||||||
|
current_progress: int,
|
||||||
|
all_progress: int,
|
||||||
|
step_name: string,
|
||||||
}
|
}
|
|
@ -2,19 +2,26 @@ import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListV
|
||||||
import {SelectableTableView} from "selectable_tree_view.slint";
|
import {SelectableTableView} from "selectable_tree_view.slint";
|
||||||
import {LeftSidePanel} from "left_side_panel.slint";
|
import {LeftSidePanel} from "left_side_panel.slint";
|
||||||
import {MainList} from "main_lists.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 { ActionButtons } from "action_buttons.slint";
|
||||||
|
import { Progress } from "progress.slint";
|
||||||
|
|
||||||
export component MainWindow inherits Window {
|
export component MainWindow inherits Window {
|
||||||
callback deleted;
|
callback deleted;
|
||||||
callback scanned(CurrentTab);
|
callback scanned(CurrentTab);
|
||||||
callback item_opened(string);
|
callback item_opened(string);
|
||||||
|
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
preferred-width: 1024px;
|
preferred-width: 1024px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
preferred-height: 600px;
|
preferred-height: 600px;
|
||||||
|
|
||||||
in-out property <bool> scanning: false;
|
in-out property <bool> scanning: false;
|
||||||
|
in-out property <ProgressToSend> progress_datas : {
|
||||||
|
current_progress: 15,
|
||||||
|
all_progress: 20,
|
||||||
|
step_name: "Cache",
|
||||||
|
};
|
||||||
|
|
||||||
in-out property <CurrentTab> active-tab: CurrentTab.EmptyFolders;
|
in-out property <CurrentTab> active-tab: CurrentTab.EmptyFolders;
|
||||||
in-out property <[{checked: bool, header_row: bool, selected_row: bool, val:[string]}]> empty_folder_model: [
|
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;
|
active-tab <=> root.active-tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainList {
|
VerticalLayout {
|
||||||
horizontal-stretch: 1.0;
|
horizontal-stretch: 1.0;
|
||||||
active-tab <=> root.active-tab;
|
MainList {
|
||||||
empty_folder_model <=> root.empty_folder_model;
|
vertical-stretch: 1.0;
|
||||||
empty_files_model <=> root.empty_files_model;
|
active-tab <=> root.active-tab;
|
||||||
similar_images_model <=> root.similar_images_model;
|
empty_folder_model <=> root.empty_folder_model;
|
||||||
|
empty_files_model <=> root.empty_files_model;
|
||||||
item_opened(item) => {item_opened(item)}
|
similar_images_model <=> root.similar_images_model;
|
||||||
|
|
||||||
|
item_opened(item) => {item_opened(item)}
|
||||||
|
}
|
||||||
|
Progress {
|
||||||
|
horizontal-stretch: 0.0;
|
||||||
|
progress_datas <=> root.progress_datas;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionButtons {
|
ActionButtons {
|
||||||
|
|
|
@ -18,9 +18,18 @@ export component SelectableTableView inherits Rectangle {
|
||||||
|
|
||||||
in-out property <int> selected_item: -1;
|
in-out property <int> selected_item: -1;
|
||||||
|
|
||||||
|
forward-focus: focus_item;
|
||||||
|
|
||||||
|
focus_item := FocusScope {
|
||||||
|
key-released(event) => {
|
||||||
|
debug(event);
|
||||||
|
accept
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VerticalBox {
|
VerticalBox {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
// Widgets
|
forward-focus: focus-item;
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
spacing: 5px;
|
spacing: 5px;
|
||||||
|
@ -32,11 +41,13 @@ export component SelectableTableView inherits Rectangle {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
background: gray;
|
background: gray;
|
||||||
|
forward-focus: focus-item;
|
||||||
|
|
||||||
TouchArea {
|
TouchArea {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
x: (parent.width - self.width) / 2;
|
x: (parent.width - self.width) / 2;
|
||||||
property <length> cached;
|
property <length> cached;
|
||||||
|
forward-focus: focus-item;
|
||||||
pointer-event(event) => {
|
pointer-event(event) => {
|
||||||
if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) {
|
if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) {
|
||||||
self.cached = root.column_sizes[idx];
|
self.cached = root.column_sizes[idx];
|
||||||
|
@ -58,14 +69,17 @@ export component SelectableTableView inherits Rectangle {
|
||||||
}
|
}
|
||||||
list_view := ListView {
|
list_view := ListView {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
forward-focus: focus-item;
|
||||||
|
|
||||||
for r[idx] in root.values : Rectangle {
|
for r[idx] in root.values : Rectangle {
|
||||||
|
forward-focus: focus-item;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
// TODO move this into singleton
|
// 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: 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);
|
// background: touch-area.has-hover ? (selected ? #333333 : #222222) : (selected ? #333333: #222222);
|
||||||
|
|
||||||
touch_area:= TouchArea {
|
touch_area:= TouchArea {
|
||||||
|
forward-focus: focus-item;
|
||||||
clicked => {
|
clicked => {
|
||||||
if (!r.header_row) {
|
if (!r.header_row) {
|
||||||
r.selected_row = !r.selected_row;
|
r.selected_row = !r.selected_row;
|
||||||
|
@ -82,6 +96,7 @@ export component SelectableTableView inherits Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pointer-event(event) => {
|
pointer-event(event) => {
|
||||||
|
debug(event);
|
||||||
// TODO this should be clicked by double-click
|
// TODO this should be clicked by double-click
|
||||||
if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) {
|
if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) {
|
||||||
root.item_opened(r.val[root.parentPathIdx - 1])
|
root.item_opened(r.val[root.parentPathIdx - 1])
|
||||||
|
@ -94,16 +109,16 @@ export component SelectableTableView inherits Rectangle {
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
spacing: 5px;
|
spacing: 5px;
|
||||||
|
forward-focus: focus-item;
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
//min-width: 200px;
|
|
||||||
visible: !r.header-row;
|
visible: !r.header-row;
|
||||||
checked: r.checked && !r.header-row;
|
checked: r.checked && !r.header-row;
|
||||||
width: root.column-sizes[0];
|
width: root.column-sizes[0];
|
||||||
|
forward-focus: focus-item;
|
||||||
toggled => {
|
toggled => {
|
||||||
r.checked = self.checked;
|
r.checked = self.checked;
|
||||||
}
|
}
|
||||||
// preferred-width: root.column-sizes[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
|
@ -112,6 +127,7 @@ export component SelectableTableView inherits Rectangle {
|
||||||
width: root.column-sizes[idx + 1];
|
width: root.column-sizes[idx + 1];
|
||||||
text: f;
|
text: f;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
forward-focus: focus-item;
|
||||||
|
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue