Windows taskbar progress support (#264)
* Initial Windows taskbar progress support * Changes to COM (un)init It turns out winapi exposes IIDs through a `uuidof()` function of interfaces, so the copied one can be removed. * Don't return error codes Now the `TaskbarProgress` functions fail silently. The `TaskbarProgress` struct now will always be created (even in case of errors in initialisation), but it won't do anything. * Fix builds for other systems * Formatted code * Fix progress shown after the operation finished A progress update was received after the stop event. Also `as_ref()` was removed in many places (I don't even know why it was there). * Remove redundant call to hide It's already called by the `glib_stop_receiver` receiver. * Release the ITaskbarList3 and call CoUninitialize at exit Because objects moved to closures used as fallbacks in GTK have [static lifetimes](https://gtk-rs.org/docs-src/tutorial/closures#closures), the `TaskbarProgress` will never be dropped. To workaround this problem a `release` function is called when the main window is closed. This function behaves like `drop`, but sets the struct in a valid "empty" state, so that calling `release`/`drop` again won't cause problems. * Don't set the NORMAL state manually Because only NOPROGRESS and INDETERMINATE states are used, there is no need to set the NORMAL state when changing the progress value. Now `set_progress_value` will also change the `TaskbarProgress::current_state` if such situation occurs. > Unless [SetProgressState](https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate) > has set a blocking state (TBPF_ERROR or TBPF_PAUSED) for the window, a call to **SetProgressValue** assumes the TBPF_NORMAL > state even if it is not explicitly set. A call to **SetProgressValue** overrides and clears the TBPF_INDETERMINATE state. See the [SetProgressValue documentation](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressvalue#how-the-taskbar-button-chooses-the-progress-indicator-for-a-group)
This commit is contained in:
parent
db255a87d4
commit
4c205ce098
10 changed files with 275 additions and 0 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -600,6 +600,7 @@ dependencies = [
|
||||||
"humansize",
|
"humansize",
|
||||||
"image",
|
"image",
|
||||||
"open",
|
"open",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -31,6 +31,9 @@ open = "1.4.0"
|
||||||
# To get image preview
|
# To get image preview
|
||||||
image = "0.23.12"
|
image = "0.23.12"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = { version = "0.3.9", features = ["combaseapi", "objbase", "shobjidl_core", "windef", "winerror", "wtypesbase", "winuser"] }
|
||||||
|
|
||||||
[dependencies.gtk]
|
[dependencies.gtk]
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
default-features = false # just in case
|
default-features = false # just in case
|
||||||
|
|
|
@ -21,6 +21,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
use crate::taskbar_progress::tbp_flags::TBPF_NOPROGRESS;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn connect_button_search(
|
pub fn connect_button_search(
|
||||||
gui_data: &GuiData,
|
gui_data: &GuiData,
|
||||||
|
@ -83,6 +85,7 @@ pub fn connect_button_search(
|
||||||
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let image_preview_similar_images = gui_data.main_notebook.image_preview_similar_images.clone();
|
let image_preview_similar_images = gui_data.main_notebook.image_preview_similar_images.clone();
|
||||||
let radio_button_hash_type_blake3 = gui_data.main_notebook.radio_button_hash_type_blake3.clone();
|
let radio_button_hash_type_blake3 = gui_data.main_notebook.radio_button_hash_type_blake3.clone();
|
||||||
let radio_button_hash_type_crc32 = gui_data.main_notebook.radio_button_hash_type_crc32.clone();
|
let radio_button_hash_type_crc32 = gui_data.main_notebook.radio_button_hash_type_crc32.clone();
|
||||||
|
@ -405,6 +408,8 @@ pub fn connect_button_search(
|
||||||
// Show progress dialog
|
// Show progress dialog
|
||||||
if show_dialog.load(Ordering::Relaxed) {
|
if show_dialog.load(Ordering::Relaxed) {
|
||||||
window_progress.show();
|
window_progress.show();
|
||||||
|
taskbar_state.borrow().show();
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_NOPROGRESS);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,15 @@ pub fn connect_compute_results(gui_data: &GuiData, glib_stop_receiver: Receiver<
|
||||||
let shared_same_music_state = gui_data.shared_same_music_state.clone();
|
let shared_same_music_state = gui_data.shared_same_music_state.clone();
|
||||||
let buttons_names = gui_data.bottom_buttons.buttons_names.clone();
|
let buttons_names = gui_data.bottom_buttons.buttons_names.clone();
|
||||||
let window_progress = gui_data.progress_window.window_progress.clone();
|
let window_progress = gui_data.progress_window.window_progress.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
|
|
||||||
glib_stop_receiver.attach(None, move |msg| {
|
glib_stop_receiver.attach(None, move |msg| {
|
||||||
buttons_search.show();
|
buttons_search.show();
|
||||||
|
|
||||||
window_progress.hide();
|
window_progress.hide();
|
||||||
|
|
||||||
|
taskbar_state.borrow().hide();
|
||||||
|
|
||||||
// Restore clickability to main notebook
|
// Restore clickability to main notebook
|
||||||
notebook_main.set_sensitive(true);
|
notebook_main.set_sensitive(true);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::gui_data::GuiData;
|
use crate::gui_data::GuiData;
|
||||||
|
use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE;
|
||||||
|
|
||||||
use czkawka_core::{big_file, broken_files, duplicate, empty_files, empty_folder, invalid_symlinks, same_music, similar_images, temporary, zeroed};
|
use czkawka_core::{big_file, broken_files, duplicate, empty_files, empty_folder, invalid_symlinks, same_music, similar_images, temporary, zeroed};
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ pub fn connect_progress_window(
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_duplicate_files.next().await {
|
while let Some(item) = futures_receiver_duplicate_files.next().await {
|
||||||
match item.checking_method {
|
match item.checking_method {
|
||||||
|
@ -39,6 +41,7 @@ pub fn connect_progress_window(
|
||||||
// progress_bar_all_stages.hide();
|
// progress_bar_all_stages.hide();
|
||||||
progress_bar_all_stages.set_fraction(0 as f64);
|
progress_bar_all_stages.set_fraction(0 as f64);
|
||||||
label_stage.set_text(format!("Scanned size of {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned size of {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
// Hash - first 1KB file
|
// Hash - first 1KB file
|
||||||
1 => {
|
1 => {
|
||||||
|
@ -47,9 +50,11 @@ pub fn connect_progress_window(
|
||||||
if item.files_to_check != 0 {
|
if item.files_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
||||||
|
taskbar_state.borrow().set_progress_value((item.files_to_check + item.files_checked) as u64, item.files_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(1, 1 + item.max_stage as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Analyzed partial hash of {}/{} files", item.files_checked, item.files_to_check).as_str());
|
label_stage.set_text(format!("Analyzed partial hash of {}/{} files", item.files_checked, item.files_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
@ -58,9 +63,13 @@ pub fn connect_progress_window(
|
||||||
if item.files_to_check != 0 {
|
if item.files_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((2f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((2f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
||||||
|
taskbar_state
|
||||||
|
.borrow()
|
||||||
|
.set_progress_value((2 * item.files_to_check + item.files_checked) as u64, item.files_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(2, 1 + item.max_stage as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.checking_method == duplicate::CheckingMethod::Hash {
|
if item.checking_method == duplicate::CheckingMethod::Hash {
|
||||||
|
@ -79,12 +88,14 @@ pub fn connect_progress_window(
|
||||||
grid_progress_stages.hide();
|
grid_progress_stages.hide();
|
||||||
|
|
||||||
label_stage.set_text(format!("Scanned name of {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned name of {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
duplicate::CheckingMethod::Size => {
|
duplicate::CheckingMethod::Size => {
|
||||||
label_stage.show();
|
label_stage.show();
|
||||||
grid_progress_stages.hide();
|
grid_progress_stages.hide();
|
||||||
|
|
||||||
label_stage.set_text(format!("Scanned size {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned size {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
duplicate::CheckingMethod::None => {
|
duplicate::CheckingMethod::None => {
|
||||||
panic!();
|
panic!();
|
||||||
|
@ -97,9 +108,11 @@ pub fn connect_progress_window(
|
||||||
{
|
{
|
||||||
// Empty Files
|
// Empty Files
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_empty_files.next().await {
|
while let Some(item) = futures_receiver_empty_files.next().await {
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
@ -107,9 +120,11 @@ pub fn connect_progress_window(
|
||||||
{
|
{
|
||||||
// Empty Folder
|
// Empty Folder
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_empty_folder.next().await {
|
while let Some(item) = futures_receiver_empty_folder.next().await {
|
||||||
label_stage.set_text(format!("Scanned {} folders", item.folders_checked).as_str());
|
label_stage.set_text(format!("Scanned {} folders", item.folders_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
@ -117,9 +132,11 @@ pub fn connect_progress_window(
|
||||||
{
|
{
|
||||||
// Big Files
|
// Big Files
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_big_files.next().await {
|
while let Some(item) = futures_receiver_big_files.next().await {
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
@ -129,21 +146,25 @@ pub fn connect_progress_window(
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_same_music.next().await {
|
while let Some(item) = futures_receiver_same_music.next().await {
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(format!("Scanned {} files", item.music_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.music_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.music_to_check != 0 {
|
if item.music_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.music_checked) as f64 / item.music_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64 + (item.music_checked) as f64 / item.music_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.music_checked) as f64 / item.music_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.music_checked) as f64 / item.music_to_check as f64);
|
||||||
|
taskbar_state.borrow().set_progress_value((item.music_to_check + item.music_checked) as u64, item.music_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Reading tags of {}/{} music files", item.music_checked, item.music_to_check).as_str());
|
label_stage.set_text(format!("Reading tags of {}/{} music files", item.music_checked, item.music_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
@ -151,9 +172,13 @@ pub fn connect_progress_window(
|
||||||
if item.music_to_check != 0 {
|
if item.music_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((2f64 + (item.music_checked) as f64 / item.music_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((2f64 + (item.music_checked) as f64 / item.music_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.music_checked) as f64 / item.music_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.music_checked) as f64 / item.music_to_check as f64);
|
||||||
|
taskbar_state
|
||||||
|
.borrow()
|
||||||
|
.set_progress_value((2 * item.music_to_check + item.music_checked) as u64, item.music_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((2f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(2, (item.max_stage + 1) as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Checking for duplicates of {}/{} music files", item.music_checked, item.music_to_check).as_str());
|
label_stage.set_text(format!("Checking for duplicates of {}/{} music files", item.music_checked, item.music_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
@ -170,21 +195,27 @@ pub fn connect_progress_window(
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_similar_images.next().await {
|
while let Some(item) = futures_receiver_similar_images.next().await {
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(format!("Scanned {} files", item.images_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.images_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.images_to_check != 0 {
|
if item.images_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.images_checked) as f64 / item.images_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64 + (item.images_checked) as f64 / item.images_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.images_checked) as f64 / item.images_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.images_checked) as f64 / item.images_to_check as f64);
|
||||||
|
taskbar_state
|
||||||
|
.borrow()
|
||||||
|
.set_progress_value((item.images_to_check + item.images_checked) as u64, item.images_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Hashing {}/{} image", item.images_checked, item.images_to_check).as_str());
|
label_stage.set_text(format!("Hashing {}/{} image", item.images_checked, item.images_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
@ -199,9 +230,11 @@ pub fn connect_progress_window(
|
||||||
{
|
{
|
||||||
// Temporary
|
// Temporary
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_temporary.next().await {
|
while let Some(item) = futures_receiver_temporary.next().await {
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
@ -211,21 +244,25 @@ pub fn connect_progress_window(
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_zeroed.next().await {
|
while let Some(item) = futures_receiver_zeroed.next().await {
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.files_to_check != 0 {
|
if item.files_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
||||||
|
taskbar_state.borrow().set_progress_value((item.files_to_check + item.files_checked) as u64, item.files_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Checking {}/{} file", item.files_checked, item.files_to_check).as_str());
|
label_stage.set_text(format!("Checking {}/{} file", item.files_checked, item.files_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
@ -240,9 +277,11 @@ pub fn connect_progress_window(
|
||||||
{
|
{
|
||||||
// Invalid Symlinks
|
// Invalid Symlinks
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_invalid_symlinks.next().await {
|
while let Some(item) = futures_receiver_invalid_symlinks.next().await {
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main_context.spawn_local(future);
|
main_context.spawn_local(future);
|
||||||
|
@ -252,21 +291,25 @@ pub fn connect_progress_window(
|
||||||
let label_stage = gui_data.progress_window.label_stage.clone();
|
let label_stage = gui_data.progress_window.label_stage.clone();
|
||||||
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
let progress_bar_current_stage = gui_data.progress_window.progress_bar_current_stage.clone();
|
||||||
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
let progress_bar_all_stages = gui_data.progress_window.progress_bar_all_stages.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
let future = async move {
|
let future = async move {
|
||||||
while let Some(item) = futures_receiver_broken_files.next().await {
|
while let Some(item) = futures_receiver_broken_files.next().await {
|
||||||
match item.current_stage {
|
match item.current_stage {
|
||||||
0 => {
|
0 => {
|
||||||
progress_bar_current_stage.hide();
|
progress_bar_current_stage.hide();
|
||||||
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
label_stage.set_text(format!("Scanned {} files", item.files_checked).as_str());
|
||||||
|
taskbar_state.borrow().set_progress_state(TBPF_INDETERMINATE);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
progress_bar_current_stage.show();
|
progress_bar_current_stage.show();
|
||||||
if item.files_to_check != 0 {
|
if item.files_to_check != 0 {
|
||||||
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64 + (item.files_checked) as f64 / item.files_to_check as f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
progress_bar_current_stage.set_fraction((item.files_checked) as f64 / item.files_to_check as f64);
|
||||||
|
taskbar_state.borrow().set_progress_value((item.files_to_check + item.files_checked) as u64, item.files_to_check as u64 * (item.max_stage + 1) as u64);
|
||||||
} else {
|
} else {
|
||||||
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
progress_bar_all_stages.set_fraction((1f64) / (item.max_stage + 1) as f64);
|
||||||
progress_bar_current_stage.set_fraction(0f64);
|
progress_bar_current_stage.set_fraction(0f64);
|
||||||
|
taskbar_state.borrow().set_progress_value(1, (item.max_stage + 1) as u64);
|
||||||
}
|
}
|
||||||
label_stage.set_text(format!("Checking {}/{} files", item.files_checked, item.files_to_check).as_str());
|
label_stage.set_text(format!("Checking {}/{} files", item.files_checked, item.files_to_check).as_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::gui_popovers::GUIPopovers;
|
||||||
use crate::gui_progress_dialog::GUIProgressDialog;
|
use crate::gui_progress_dialog::GUIProgressDialog;
|
||||||
use crate::gui_upper_notepad::GUIUpperNotebook;
|
use crate::gui_upper_notepad::GUIUpperNotebook;
|
||||||
use crate::notebook_enums::*;
|
use crate::notebook_enums::*;
|
||||||
|
use crate::taskbar_progress::TaskbarProgress;
|
||||||
use crossbeam_channel::unbounded;
|
use crossbeam_channel::unbounded;
|
||||||
use czkawka_core::big_file::BigFile;
|
use czkawka_core::big_file::BigFile;
|
||||||
use czkawka_core::broken_files::BrokenFiles;
|
use czkawka_core::broken_files::BrokenFiles;
|
||||||
|
@ -43,6 +44,9 @@ pub struct GuiData {
|
||||||
pub options: GUIOptions,
|
pub options: GUIOptions,
|
||||||
pub header: GUIHeader,
|
pub header: GUIHeader,
|
||||||
|
|
||||||
|
// Taskbar state
|
||||||
|
pub taskbar_state: Rc<RefCell<TaskbarProgress>>,
|
||||||
|
|
||||||
// Buttons state
|
// Buttons state
|
||||||
pub shared_buttons: Rc<RefCell<HashMap<NotebookMainEnum, HashMap<String, bool>>>>,
|
pub shared_buttons: Rc<RefCell<HashMap<NotebookMainEnum, HashMap<String, bool>>>>,
|
||||||
|
|
||||||
|
@ -95,6 +99,9 @@ impl GuiData {
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Taskbar state
|
||||||
|
let taskbar_state = Rc::new(RefCell::new(TaskbarProgress::new()));
|
||||||
|
|
||||||
// Buttons State - to remember existence of different buttons on pages
|
// Buttons State - to remember existence of different buttons on pages
|
||||||
let shared_buttons: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::<NotebookMainEnum, HashMap<String, bool>>::new()));
|
let shared_buttons: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::<NotebookMainEnum, HashMap<String, bool>>::new()));
|
||||||
|
|
||||||
|
@ -160,6 +167,7 @@ impl GuiData {
|
||||||
about,
|
about,
|
||||||
options,
|
options,
|
||||||
header,
|
header,
|
||||||
|
taskbar_state,
|
||||||
shared_buttons,
|
shared_buttons,
|
||||||
shared_upper_notebooks,
|
shared_upper_notebooks,
|
||||||
shared_duplication_state,
|
shared_duplication_state,
|
||||||
|
|
|
@ -31,6 +31,11 @@ mod help_functions;
|
||||||
mod initialize_gui;
|
mod initialize_gui;
|
||||||
mod notebook_enums;
|
mod notebook_enums;
|
||||||
mod saving_loading;
|
mod saving_loading;
|
||||||
|
mod taskbar_progress;
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
mod taskbar_progress_dummy;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod taskbar_progress_win;
|
||||||
|
|
||||||
use czkawka_core::*;
|
use czkawka_core::*;
|
||||||
|
|
||||||
|
@ -140,9 +145,11 @@ fn main() {
|
||||||
// Quit the program when X in main window was clicked
|
// Quit the program when X in main window was clicked
|
||||||
{
|
{
|
||||||
let window_main = gui_data.window_main.clone();
|
let window_main = gui_data.window_main.clone();
|
||||||
|
let taskbar_state = gui_data.taskbar_state.clone();
|
||||||
window_main.connect_delete_event(move |_, _| {
|
window_main.connect_delete_event(move |_, _| {
|
||||||
save_configuration(&gui_data, false); // Save configuration at exit
|
save_configuration(&gui_data, false); // Save configuration at exit
|
||||||
gtk::main_quit();
|
gtk::main_quit();
|
||||||
|
taskbar_state.borrow_mut().release();
|
||||||
Inhibit(false)
|
Inhibit(false)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
5
czkawka_gui/src/taskbar_progress.rs
Normal file
5
czkawka_gui/src/taskbar_progress.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use crate::taskbar_progress_win::{tbp_flags, TaskbarProgress};
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub use crate::taskbar_progress_dummy::{tbp_flags, TaskbarProgress};
|
46
czkawka_gui/src/taskbar_progress_dummy.rs
Normal file
46
czkawka_gui/src/taskbar_progress_dummy.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#![cfg(not(target_os = "windows"))]
|
||||||
|
use std::convert::From;
|
||||||
|
|
||||||
|
enum HWND__ {}
|
||||||
|
type HWND = *mut HWND__;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
|
pub enum TBPFLAG {
|
||||||
|
TBPF_NOPROGRESS = 0,
|
||||||
|
TBPF_INDETERMINATE = 0x1,
|
||||||
|
TBPF_NORMAL = 0x2,
|
||||||
|
TBPF_ERROR = 0x4,
|
||||||
|
TBPF_PAUSED = 0x8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod tbp_flags {
|
||||||
|
pub use super::TBPFLAG::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TaskbarProgress {}
|
||||||
|
|
||||||
|
impl TaskbarProgress {
|
||||||
|
pub fn new() -> TaskbarProgress {
|
||||||
|
TaskbarProgress {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_progress_state(&self, _tbp_flags: TBPFLAG) {}
|
||||||
|
|
||||||
|
pub fn set_progress_value(&self, _completed: u64, _total: u64) {}
|
||||||
|
|
||||||
|
pub fn hide(&self) {}
|
||||||
|
|
||||||
|
pub fn show(&self) {}
|
||||||
|
|
||||||
|
pub fn release(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HWND> for TaskbarProgress {
|
||||||
|
fn from(_hwnd: HWND) -> Self {
|
||||||
|
TaskbarProgress {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TaskbarProgress {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
154
czkawka_gui/src/taskbar_progress_win.rs
Normal file
154
czkawka_gui/src/taskbar_progress_win.rs
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#![cfg(target_os = "windows")]
|
||||||
|
extern crate winapi;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::convert::From;
|
||||||
|
use std::ptr;
|
||||||
|
use winapi::ctypes::c_void;
|
||||||
|
use winapi::shared::windef::HWND;
|
||||||
|
use winapi::shared::winerror::{E_POINTER, S_OK};
|
||||||
|
use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;
|
||||||
|
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList3, TBPFLAG};
|
||||||
|
use winapi::um::{combaseapi, objbase, winuser};
|
||||||
|
use winapi::Interface;
|
||||||
|
|
||||||
|
pub mod tbp_flags {
|
||||||
|
pub use winapi::um::shobjidl_core::{TBPF_ERROR, TBPF_INDETERMINATE, TBPF_NOPROGRESS, TBPF_NORMAL, TBPF_PAUSED};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TaskbarProgress {
|
||||||
|
hwnd: HWND,
|
||||||
|
taskbar_list: *mut ITaskbarList3,
|
||||||
|
current_state: RefCell<TBPFLAG>,
|
||||||
|
current_progress: RefCell<(u64, u64)>,
|
||||||
|
must_uninit_com: bool,
|
||||||
|
is_active: RefCell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskbarProgress {
|
||||||
|
pub fn new() -> TaskbarProgress {
|
||||||
|
let hwnd = unsafe { winuser::GetActiveWindow() };
|
||||||
|
TaskbarProgress::from(hwnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_progress_state(&self, tbp_flags: TBPFLAG) {
|
||||||
|
if tbp_flags == *self.current_state.borrow() || !*self.is_active.borrow() {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
let result = unsafe {
|
||||||
|
if let Some(list) = self.taskbar_list.as_ref() {
|
||||||
|
list.SetProgressState(self.hwnd, tbp_flags)
|
||||||
|
} else {
|
||||||
|
E_POINTER
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if result == S_OK {
|
||||||
|
self.current_state.replace(tbp_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_progress_value(&self, completed: u64, total: u64) {
|
||||||
|
// Don't change the value if the is_active flag is false or the value has not changed.
|
||||||
|
// If is_active is true and the value has not changed, but the progress indicator was in NOPROGRESS or INDETERMINATE state, set the value (and NORMAL state).
|
||||||
|
if ((completed, total) == *self.current_progress.borrow() && *self.current_state.borrow() != tbp_flags::TBPF_NOPROGRESS && *self.current_state.borrow() != tbp_flags::TBPF_INDETERMINATE) || !*self.is_active.borrow() {
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
let result = unsafe {
|
||||||
|
if let Some(list) = self.taskbar_list.as_ref() {
|
||||||
|
list.SetProgressValue(self.hwnd, completed, total)
|
||||||
|
} else {
|
||||||
|
E_POINTER
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if result == S_OK {
|
||||||
|
self.current_progress.replace((completed, total));
|
||||||
|
if *self.current_state.borrow() == tbp_flags::TBPF_NOPROGRESS || *self.current_state.borrow() == tbp_flags::TBPF_INDETERMINATE {
|
||||||
|
self.current_state.replace(tbp_flags::TBPF_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide(&self) {
|
||||||
|
self.set_progress_state(tbp_flags::TBPF_NOPROGRESS);
|
||||||
|
*self.is_active.borrow_mut() = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&self) {
|
||||||
|
*self.is_active.borrow_mut() = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the ITaskbarList3 pointer, uninitialises the COM API and sets the struct to a valid "empty" state.
|
||||||
|
/// It's required for proper use of the COM API, because `drop` is never called (objects moved to GTK closures have `static` lifetime).
|
||||||
|
pub fn release(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(list) = self.taskbar_list.as_ref() {
|
||||||
|
list.Release();
|
||||||
|
self.taskbar_list = ptr::null_mut();
|
||||||
|
self.hwnd = ptr::null_mut();
|
||||||
|
}
|
||||||
|
// A thread must call CoUninitialize once for each successful call it has made to
|
||||||
|
// the CoInitialize or CoInitializeEx function, including any call that returns S_FALSE.
|
||||||
|
if self.must_uninit_com {
|
||||||
|
combaseapi::CoUninitialize();
|
||||||
|
self.must_uninit_com = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HWND> for TaskbarProgress {
|
||||||
|
fn from(hwnd: HWND) -> Self {
|
||||||
|
if hwnd.is_null() {
|
||||||
|
return TaskbarProgress {
|
||||||
|
hwnd,
|
||||||
|
taskbar_list: ptr::null_mut(),
|
||||||
|
current_state: RefCell::new(tbp_flags::TBPF_NOPROGRESS),
|
||||||
|
current_progress: RefCell::new((0, 0)),
|
||||||
|
must_uninit_com: false,
|
||||||
|
is_active: RefCell::new(false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let init_result = unsafe { combaseapi::CoInitializeEx(ptr::null_mut(), objbase::COINIT_APARTMENTTHREADED) };
|
||||||
|
// S_FALSE means that COM library is already initialised for this thread
|
||||||
|
// Success codes are not negative, RPC_E_CHANGED_MODE should not be possible and is treated as an error
|
||||||
|
if init_result < 0 {
|
||||||
|
return TaskbarProgress {
|
||||||
|
hwnd: ptr::null_mut(),
|
||||||
|
taskbar_list: ptr::null_mut(),
|
||||||
|
current_state: RefCell::new(tbp_flags::TBPF_NOPROGRESS),
|
||||||
|
current_progress: RefCell::new((0, 0)),
|
||||||
|
must_uninit_com: false,
|
||||||
|
is_active: RefCell::new(false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut taskbar_list: *mut ITaskbarList3 = ptr::null_mut();
|
||||||
|
let taskbar_list_ptr: *mut *mut ITaskbarList3 = &mut taskbar_list;
|
||||||
|
|
||||||
|
unsafe { combaseapi::CoCreateInstance(&CLSID_TaskbarList, ptr::null_mut(), CLSCTX_INPROC_SERVER, &ITaskbarList3::uuidof(), taskbar_list_ptr as *mut *mut c_void) };
|
||||||
|
|
||||||
|
TaskbarProgress {
|
||||||
|
hwnd: if taskbar_list.is_null() { ptr::null_mut() } else { hwnd },
|
||||||
|
taskbar_list,
|
||||||
|
current_state: RefCell::new(tbp_flags::TBPF_NOPROGRESS), // Assume no progress
|
||||||
|
current_progress: RefCell::new((0, 0)),
|
||||||
|
must_uninit_com: true,
|
||||||
|
is_active: RefCell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TaskbarProgress {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(list) = self.taskbar_list.as_ref() {
|
||||||
|
list.Release();
|
||||||
|
}
|
||||||
|
// A thread must call CoUninitialize once for each successful call it has made to
|
||||||
|
// the CoInitialize or CoInitializeEx function, including any call that returns S_FALSE.
|
||||||
|
if self.must_uninit_com {
|
||||||
|
combaseapi::CoUninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue