diff --git a/.github/workflows/linux_cli.yml b/.github/workflows/linux_cli.yml index 70f3c9b..053f22f 100644 --- a/.github/workflows/linux_cli.yml +++ b/.github/workflows/linux_cli.yml @@ -12,23 +12,20 @@ jobs: linux-cli: strategy: matrix: - toolchain: [ stable, 1.70.0 ] + toolchain: [ stable, 1.72.1 ] type: [ release ] runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - name: Install basic libraries - run: sudo apt-get update; sudo apt install libheif-dev ffmpeg -y + run: sudo apt update || true; sudo apt install libheif-dev ffmpeg -y - name: Setup rust version run: rustup default ${{ matrix.toolchain }} - name: Build Release run: cargo build --release --bin czkawka_cli - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0" if: ${{ (matrix.type == 'release') }} - name: Store Linux CLI diff --git a/.github/workflows/linux_cli_eyra.yml b/.github/workflows/linux_cli_eyra.yml new file mode 100644 index 0000000..6370205 --- /dev/null +++ b/.github/workflows/linux_cli_eyra.yml @@ -0,0 +1,54 @@ +name: 🐧 Linux CLI Eyra +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 2' + +env: + CARGO_TERM_COLOR: always + +jobs: + linux-cli: + strategy: + matrix: + toolchain: [ nightly ] + type: [ release ] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Install basic libraries + run: sudo apt update || true; sudo apt install -y ffmpeg + + # New versions of nightly rust may call new unimplemented in eyra functions, so use const version + - name: Setup rust version + run: rustup default nightly-2023-11-29 + + - name: Add eyra + run: | + cd czkawka_cli + cargo add eyra --rename=std + echo 'fn main() { println!("cargo:rustc-link-arg=-nostartfiles"); }' > build.rs + cd .. + + - name: Build Release + run: cargo build --release --bin czkawka_cli + if: ${{ (matrix.type == 'release') }} + + - name: Store Linux CLI + uses: actions/upload-artifact@v3 + with: + name: czkawka_cli-${{ runner.os }}-${{ matrix.toolchain }} + path: target/release/czkawka_cli + if: ${{ matrix.type == 'release' }} + + - name: Linux Regression Test + run: | + wget https://github.com/qarmin/czkawka/releases/download/6.0.0/TestFiles.zip + cd ci_tester + cargo build --release + cd .. + + ci_tester/target/release/ci_tester target/release/czkawka_cli + if: ${{ matrix.type == 'release' }} diff --git a/.github/workflows/linux_gui.yml b/.github/workflows/linux_gui.yml index 96c8147..aca39f5 100644 --- a/.github/workflows/linux_gui.yml +++ b/.github/workflows/linux_gui.yml @@ -9,48 +9,92 @@ env: CARGO_TERM_COLOR: always jobs: - linux-gui: + linux-krokiet-gui: strategy: matrix: - toolchain: [ stable, 1.70.0 ] + toolchain: [ stable, 1.72.1 ] + type: [ release ] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Setup rust version + run: rustup default ${{ matrix.toolchain }} + + - name: Build Release Krokiet + run: cargo build --release --bin krokiet + if: ${{ (matrix.type == 'release') }} + + - name: Store Linux GUI Krokiet + uses: actions/upload-artifact@v3 + with: + name: krokiet-${{ runner.os }}-${{ matrix.toolchain }} + path: target/release/krokiet + if: ${{ matrix.type == 'release' }} + + linux-krokiet-gui-heif: + strategy: + matrix: + toolchain: [ stable, 1.72.1 ] type: [ release ] runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install basic libraries - run: sudo apt-get update; sudo apt install libgtk-4-dev libheif-dev -y + run: sudo apt update || true; sudo apt install libheif-dev libraw-dev -y - name: Setup rust version run: rustup default ${{ matrix.toolchain }} - - name: Build Release Heif - run: cargo build --release --features heif - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0" - if: ${{ (matrix.type == 'release') && (matrix.toolchain == '1.70.0') }} + - name: Build Release Krokiet heif + run: cargo build --release --bin krokiet --features "heif,libraw" + if: ${{ (matrix.type == 'release') }} - - name: Store Linux GUI Heif + - name: Store Linux GUI Krokiet heif libraw uses: actions/upload-artifact@v3 with: - name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }}-heif - path: target/release/czkawka_gui + name: krokiet-${{ runner.os }}-${{ matrix.toolchain }}-heif-libraw + path: target/release/krokiet if: ${{ matrix.type == 'release' }} - - name: Build Release - run: cargo build --release - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0" + linux-gui: + strategy: + matrix: + toolchain: [ stable, 1.72.1 ] + type: [ release ] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Install basic libraries + run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev libraw-dev -y + + - name: Setup rust version + run: rustup default ${{ matrix.toolchain }} + + - name: Build Release Heif Libraw + run: cargo build --release --bin czkawka_gui --features "heif,libraw" + if: ${{ (matrix.type == 'release') }} + + - name: Store Linux GUI Heif Libraw + uses: actions/upload-artifact@v3 + with: + name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }}-heif-libraw + path: target/release/czkawka_gui if: ${{ (matrix.type == 'release') && (matrix.toolchain == 'stable') }} + - name: Build Release + run: cargo build --release --bin czkawka_gui + if: ${{ (matrix.type == 'release') }} + + # Only store stable toolchain - name: Store Linux GUI uses: actions/upload-artifact@v3 with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }} path: target/release/czkawka_gui - if: ${{ matrix.type == 'release' }} + if: ${{ (matrix.type == 'release') && (matrix.toolchain == 'stable') }} linux-appimage-gui: strategy: @@ -62,16 +106,13 @@ jobs: - uses: actions/checkout@v3 - name: Install Dependencies - run: sudo apt-get update; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y + run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y - name: Setup rust version run: rustup default ${{ matrix.toolchain }} - name: Build Release - run: cargo build --release - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0" + run: cargo build --release --bin czkawka_gui - name: Download appimage dependencies run: | @@ -118,13 +159,10 @@ jobs: - uses: actions/checkout@v3 - name: Install Dependencies - run: sudo apt-get update; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y xvfb + run: sudo apt update || true; sudo apt install libgtk-4-dev libheif-dev librsvg2-dev wget fuse libfuse2 -y xvfb - name: Setup rust version run: rustup default ${{ matrix.toolchain }} - name: Test run: xvfb-run cargo test - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0" diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 9bf36e5..e746fcc 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -32,8 +32,6 @@ jobs: - name: Build Release run: cargo build --release - env: - CARGO_INCREMENTAL: 0 if: ${{ matrix.type == 'release'}} - name: Store MacOS CLI @@ -50,10 +48,15 @@ jobs: path: target/release/czkawka_gui if: ${{ matrix.type == 'release' }} + - name: Store MacOS Krokiet + uses: actions/upload-artifact@v3 + with: + name: krokiet-${{ runner.os }}-${{ matrix.toolchain }} + path: target/release/krokiet + if: ${{ matrix.type == 'release' }} + - name: Build Release Heif run: cargo build --release --features heif - env: - CARGO_INCREMENTAL: 0 if: ${{ matrix.type == 'release'}} - name: Store MacOS CLI Heif @@ -68,4 +71,11 @@ jobs: with: name: czkawka_gui-${{ runner.os }}-${{ matrix.toolchain }}-heif path: target/release/czkawka_gui + if: ${{ matrix.type == 'release' }} + + - name: Store MacOS Krokiet Heif + uses: actions/upload-artifact@v3 + with: + name: krokiet-${{ runner.os }}-${{ matrix.toolchain }}-heif + path: target/release/krokiet if: ${{ matrix.type == 'release' }} \ No newline at end of file diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a7c0459..60e3bd8 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -15,16 +15,13 @@ jobs: - uses: actions/checkout@v3 - name: Install Gtk 4 - run: sudo apt-get update; sudo apt install -y libgtk-4-dev libheif-dev -y + run: sudo apt update || true; sudo apt install -y libgtk-4-dev libraw-dev libheif-dev -y - name: Check the format run: cargo fmt --all -- --check - # type complexity must be ignored because we use huge templates for queries - name: Run clippy - run: > - cargo clippy - --all-targets - --all-features - -- - -D warnings + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Run clippy + run: cargo clippy -- -D warnings diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b276683..f979715 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -9,47 +9,121 @@ env: CARGO_TERM_COLOR: always jobs: + krokiet-compiled-on-linux: + strategy: + fail-fast: false + matrix: + use_heif: [ normal ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies(mostly sd) + run: | + sudo apt update || true;sudo apt install -y mingw-w64 mingw-w64-x86-64-dev + wget https://github.com/chmln/sd/releases/download/v1.0.0/sd-v1.0.0-x86_64-unknown-linux-gnu.tar.gz -O a.tar.gz + tar -xzf a.tar.gz + cp sd-v1.0.0-x86_64-unknown-linux-gnu/sd . + chmod +x ./sd + + - name: Setup rust version + run: | + rustup target add x86_64-pc-windows-gnu + + - name: Compile Krokiet + run: cargo build --release --target x86_64-pc-windows-gnu --bin krokiet + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: krokiet-windows-c-on-linux-${{ github.sha }}-${{ matrix.use_heif }} + path: | + target/x86_64-pc-windows-gnu/release/krokiet.exe + if-no-files-found: error + + - name: Show console window on windows + run: | + ./sd -s '#![windows_subsystem = "windows"]' '//#![windows_subsystem = "windows"]' krokiet/src/main.rs + cat krokiet/src/main.rs + + - name: Compile Krokiet Console + run: cargo build --release --target x86_64-pc-windows-gnu --bin krokiet + + - name: Upload artifacts Console + uses: actions/upload-artifact@v3 + with: + name: krokiet-windows-c-on-linux-${{ github.sha }}-${{ matrix.use_heif }}-console + path: | + target/x86_64-pc-windows-gnu/release/krokiet.exe + if-no-files-found: error + + krokiet-compiled-on-windows: + strategy: + fail-fast: false + matrix: + use_heif: [ normal ] + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies(mostly sd) + run: | + Invoke-WebRequest -Uri https://github.com/chmln/sd/releases/download/v1.0.0/sd-v1.0.0-x86_64-pc-windows-gnu.zip -OutFile a.zip + Expand-Archive ./a.zip + cp a/sd-v1.0.0-x86_64-pc-windows-gnu/sd.exe . + + - name: Setup rust version + run: rustup default stable-x86_64-pc-windows-gnu + + - name: Compile Krokiet + run: cargo build --release --bin krokiet + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: krokiet-windows-c-on-windows-${{ github.sha }}-${{ matrix.use_heif }} + path: | + target/release/krokiet.exe + if-no-files-found: error + + - name: Show console window on windows + run: | + ./sd.exe -s '#![windows_subsystem = "windows"]' '//#![windows_subsystem = "windows"]' krokiet/src/main.rs + cat krokiet/src/main.rs + + - name: Compile Krokiet Console + run: cargo build --release --bin krokiet + + - name: Upload artifacts Console + uses: actions/upload-artifact@v3 + with: + name: krokiet-windows-c-on-windows-${{ github.sha }}-${{ matrix.use_heif }}-console + path: | + target/release/krokiet.exe + if-no-files-found: error + + container: strategy: + fail-fast: false matrix: - use_heif: [ non_heif ] #, heif ] - heif problems with mingw - runs-on: ubuntu-22.04 + use_heif: [ non_heif ] + runs-on: ubuntu-latest container: image: ghcr.io/piegamesde/gtk4-cross:gtk-4.6 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Install additional dependencies # gio is for the build script - run: dnf install wget2 unzip mingw64-bzip2.noarch mingw64-poppler mingw64-poppler-glib mingw32-python3 rust-gio-devel adwaita-icon-theme -y && dnf clean all -y - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - target: x86_64-pc-windows-gnu - - name: Cache ~/.cargo - uses: actions/cache@v1 - with: - path: ~/.cargo - key: windows-dotcargo - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: windows-build-target - - - name: Cross compile for Windows Heif run: | - #!/bin/bash - set -euo pipefail - export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/share/pkgconfig:$MINGW_PREFIX/lib/pkgconfig/:/usr/x86_64-w64-mingw32/lib/pkgconfig/ - cargo build --target=x86_64-pc-windows-gnu --release --locked --features heif - mkdir -p package - cp target/x86_64-pc-windows-gnu/release/czkawka_gui.exe package/ - cp target/x86_64-pc-windows-gnu/release/czkawka_cli.exe package/ - if: ${{ matrix.use_heif == 'heif' }} + dnf install curl wget2 unzip mingw64-bzip2.noarch mingw64-poppler mingw64-poppler-glib mingw32-python3 rust-gio-devel adwaita-icon-theme -y && dnf clean all -y + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" + rustup target add x86_64-pc-windows-gnu - name: Cross compile for Windows run: | + source "$HOME/.cargo/env" #!/bin/bash set -euo pipefail export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/share/pkgconfig:$MINGW_PREFIX/lib/pkgconfig/:/usr/x86_64-w64-mingw32/lib/pkgconfig/ @@ -57,7 +131,6 @@ jobs: mkdir -p package cp target/x86_64-pc-windows-gnu/release/czkawka_gui.exe package/ cp target/x86_64-pc-windows-gnu/release/czkawka_cli.exe package/ - if: ${{ matrix.use_heif == 'non_heif' }} - name: Package run: | @@ -95,29 +168,27 @@ jobs: container: image: ghcr.io/piegamesde/gtk4-cross:gtk-4.6 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 + - name: Install dependencies(mostly sd) + run: | + dnf install wget -y + wget https://github.com/chmln/sd/releases/download/v1.0.0/sd-v1.0.0-x86_64-unknown-linux-gnu.tar.gz -O a.tar.gz + tar -xzf a.tar.gz + chmod +x sd-v1.0.0-x86_64-unknown-linux-gnu/sd + sudo cp sd-v1.0.0-x86_64-unknown-linux-gnu/sd /usr/bin/sd - name: Install additional dependencies # gio is for the build script - run: dnf install wget2 unzip mingw64-bzip2.noarch mingw64-poppler mingw64-poppler-glib mingw32-python3 rust-gio-devel adwaita-icon-theme -y && dnf clean all -y - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - target: x86_64-pc-windows-gnu - - name: Cache ~/.cargo - uses: actions/cache@v1 - with: - path: ~/.cargo - key: windows-dotcargo - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: windows-build-target + run: | + dnf install curl wget2 unzip mingw64-bzip2.noarch mingw64-poppler mingw64-poppler-glib mingw32-python3 rust-gio-devel adwaita-icon-theme -y && dnf clean all -y + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" + rustup target add x86_64-pc-windows-gnu + - name: Show console window on windows - run: sed -i 's|\#\!\[windows_subsystem|//#![windows_subsystem|' czkawka_gui/src/main.rs + run: sd -s '#![windows_subsystem = "windows"]' '//#![windows_subsystem = "windows"]' krokiet/src/main.rs - name: Cross compile for Windows run: | + source "$HOME/.cargo/env" #!/bin/bash set -euo pipefail export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/share/pkgconfig:$MINGW_PREFIX/lib/pkgconfig/:/usr/x86_64-w64-mingw32/lib/pkgconfig/ diff --git a/.gitignore b/.gitignore index 298e00b..1a09545 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,6 @@ flatpak/ /report ci_tester/target ci_tester/Cargo.lock -czkawka_slint_gui/Cargo.lock -czkawka_slint_gui/target +krokiet/Cargo.lock +krokiet/target *.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3f7f71c..ccfd7cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ab_glyph" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + [[package]] name = "adler" version = "1.0.2" @@ -25,6 +41,19 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -34,6 +63,39 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-activity" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" +dependencies = [ + "android-properties", + "bitflags 2.4.1", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -84,7 +146,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -94,7 +156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -122,16 +184,276 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "async-trait" -version = "0.1.73" +name = "as-raw-xcb-connection" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "ashpd" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c018490e423efb6f032ef575f873ea57b61d44bec763cfe027b8e8852a027cf" +dependencies = [ + "async-std", + "enumflags2", + "futures-channel", + "futures-util", + "once_cell", + "rand", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.2.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 2.1.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-global-executor" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526" +dependencies = [ + "async-channel 2.1.1", + "async-executor", + "async-io 2.2.1", + "async-lock 3.2.0", + "blocking", + "futures-lite 2.1.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" +dependencies = [ + "async-lock 3.2.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.1.0", + "parking", + "polling 3.3.1", + "rustix 0.38.26", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +dependencies = [ + "event-listener 4.0.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.26", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.1", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.26", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "audio_checker" version = "0.1.0" @@ -141,6 +463,18 @@ dependencies = [ "symphonia", ] +[[package]] +name = "auto_enums" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a764179c02b324e33cf71b4180e7dd13572400ff7e5c866da813f6c84e0e4cd3" +dependencies = [ + "derive_utils", + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -149,9 +483,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -168,6 +502,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", + "which", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -182,9 +539,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bk-tree" @@ -233,17 +590,72 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys", + "objc2", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.1.1", + "async-lock 3.2.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.1.0", + "piper", + "tracing", +] + [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "by_address" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf8dba2868114ed769a1f2590fc9ae5eb331175b44313b6c9b922f8f7ca813d0" + [[package]] name = "bytemuck" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] [[package]] name = "byteorder" @@ -251,6 +663,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + [[package]] name = "bzip2" version = "0.4.4" @@ -274,11 +692,11 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0466dfa8c0ee78deef390c274ad756801e0a6dbb86c5ef0924a298c5761c4d" +checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cairo-sys-rs", "glib", "libc", @@ -297,6 +715,32 @@ dependencies = [ "system-deps", ] +[[package]] +name = "calloop" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" +dependencies = [ + "bitflags 2.4.1", + "log", + "polling 3.3.1", + "rustix 0.38.26", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix 0.38.26", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cbc" version = "0.1.2" @@ -312,9 +756,25 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfb" version = "0.7.3" @@ -342,6 +802,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "chrono" version = "0.4.31" @@ -367,10 +842,21 @@ dependencies = [ ] [[package]] -name = "clap" -version = "4.4.6" +name = "clang-sys" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading 0.7.4", +] + +[[package]] +name = "clap" +version = "4.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", "clap_derive", @@ -378,9 +864,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", @@ -390,21 +876,83 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi", +] + +[[package]] +name = "clru" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + +[[package]] +name = "codemap-diagnostic" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc20770be05b566a963bf91505e60412c4a2d016d1ef95c5512823bb085a8122" +dependencies = [ + "codemap", + "termcolor", +] [[package]] name = "color_quant" @@ -418,6 +966,46 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-field-offset" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6304465f16f463cddc572b737c3df93576edd3a6b53f057bd8beeb29f4ef8dfd" +dependencies = [ + "const-field-offset-macro", + "field-offset", +] + +[[package]] +name = "const-field-offset-macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57aaaad9185d3bcb3afe63549d8ba60b2fb0ea8dc2da83f62dd56805edf56fd1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "const_fn" version = "0.4.9" @@ -437,16 +1025,88 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] -name = "core-foundation-sys" -version = "0.8.4" +name = "convert_case" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "copypasta" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d35364349bf9e9e1c3a035ddcb00d188d23a3c40c50244c03c27a99fc6a65ae" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -460,6 +1120,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -490,10 +1156,20 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.9.0", "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -519,6 +1195,31 @@ dependencies = [ "typenum", ] +[[package]] +name = "css-color-parser2" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8ed1639f4b56ec6f31d007ff66ce4a13099dce5a9995d48368a30d62bf04bd" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote", + "syn 2.0.39", +] + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "czkawka_cli" version = "6.1.0" @@ -538,7 +1239,7 @@ dependencies = [ "anyhow", "audio_checker", "bincode", - "bitflags 2.4.0", + "bitflags 2.4.1", "bk-tree", "blake3", "crc32fast", @@ -546,7 +1247,6 @@ dependencies = [ "directories-next", "ffmpeg_cmdline_utils", "fun_time", - "futures", "hamming", "handsome_logger", "humansize", @@ -558,14 +1258,17 @@ dependencies = [ "infer", "libheif-rs", "libheif-sys", + "libraw-rs", "lofty", "log", "mime_guess", "once_cell", + "os_info", "pdf", "rawloader", "rayon", "rust-embed", + "rustc_version", "rusty-chromaprint", "serde", "serde_json", @@ -587,7 +1290,6 @@ dependencies = [ "directories-next", "fs_extra", "fun_time", - "futures", "gdk4", "glib", "gtk4", @@ -648,12 +1350,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.1", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "datasize" version = "0.2.15" @@ -685,13 +1393,48 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "derive_utils" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9abcad25e9720609ccb3dcdb795d845e37d8ce34183330a9f48b03a1a71c8e21" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "digest" version = "0.10.7" @@ -724,6 +1467,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "displaydoc" version = "0.2.4" @@ -732,7 +1481,16 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.1", ] [[package]] @@ -741,6 +1499,93 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "drm" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf9159ef4bcecd0c5e4cbeb573b8d0037493403d542780dba5d840bbf9df56f" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "drm-ffi 0.5.0", + "drm-fourcc", + "nix 0.26.4", +] + +[[package]] +name = "drm" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb1b703ffbc7ebd216eba7900008049a56ace55580ecb2ee7fa801e8d8be87" +dependencies = [ + "bitflags 2.4.1", + "bytemuck", + "drm-ffi 0.6.0", + "drm-fourcc", + "nix 0.27.1", +] + +[[package]] +name = "drm-ffi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1352481b7b90e27a8a1bf8ef6b33cf18b98dba7c410e75c24bb3eef2f0d8d525" +dependencies = [ + "drm-sys 0.4.0", + "nix 0.26.4", +] + +[[package]] +name = "drm-ffi" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7d1c19c4b6270e89d59fb27dc6d02a317c658a8a54e54781e1db9b5947595d" +dependencies = [ + "drm-sys 0.5.0", + "nix 0.27.1", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1369f1679d6b706d234c4c1e0613c415c2c74b598a09ad28080ba2474b72e42d" +dependencies = [ + "libc", +] + +[[package]] +name = "drm-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f1c0468062a56cd5705f1e3b5409eb286d5596a2028ec8e947595d7e715ae" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "serde", + "serde_derive", + "winapi", + "wio", +] + [[package]] name = "either" version = "1.9.0" @@ -756,6 +1601,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "enumn" version = "0.1.12" @@ -764,7 +1630,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -775,19 +1641,66 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.0", + "pin-project-lite", ] [[package]] name = "exr" -version = "1.71.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "279d3efcc55e19917fff7ab3ddd6c14afb6a90881a0078465196fe2f99d08c56" dependencies = [ "bit_field", "flume", @@ -799,6 +1712,15 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -827,13 +1749,34 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" dependencies = [ "simd-adler32", ] +[[package]] +name = "femtovg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d900654f23fe7c254442e1902e22dff2c3facf61bc0fb6531cc103b66467864e" +dependencies = [ + "bitflags 2.4.1", + "fnv", + "generational-arena", + "glow", + "image", + "imgref", + "lru", + "rgb", + "rustybuzz 0.11.0", + "unicode-bidi", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "ffmpeg_cmdline_utils" version = "0.1.3" @@ -854,10 +1797,22 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.0", "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", +] + [[package]] name = "find-crate" version = "0.6.3" @@ -883,6 +1838,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "fluent" version = "0.16.0" @@ -904,7 +1865,7 @@ dependencies = [ "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -929,10 +1890,14 @@ dependencies = [ [[package]] name = "flume" -version = "0.11.0" +version = "0.10.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", "spin", ] @@ -943,10 +1908,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "form_urlencoded" -version = "1.2.0" +name = "fontconfig-parser" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2 0.8.0", + "slotmap", + "tinyvec", + "ttf-parser 0.19.2", +] + +[[package]] +name = "fontdue" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9099a2f86b8e674b75d03ff154b3fe4c5208ed249ced8d69cc313a9fa40bb488" +dependencies = [ + "hashbrown 0.14.3", + "ttf-parser 0.20.0", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -986,42 +2011,26 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", - "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1030,40 +2039,67 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -1076,10 +2112,32 @@ dependencies = [ ] [[package]] -name = "gdk-pixbuf" -version = "0.18.0" +name = "gbm" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc9c2ed73a81d556b65d08879ba4ee58808a6b1927ce915262185d6d547c6f3" +checksum = "f2ec389cda876966cf824111bf6e533fb934c711d473498279964a990853b3c6" +dependencies = [ + "bitflags 1.3.2", + "drm 0.9.0", + "drm-fourcc", + "gbm-sys", + "libc", +] + +[[package]] +name = "gbm-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63eba9b9b7a231514482deb08759301c9f9f049ac6869403f381834ebfeaf67" +dependencies = [ + "libc", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -1133,6 +2191,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generational-arena" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" +dependencies = [ + "cfg-if", +] + [[package]] name = "generator" version = "0.7.5" @@ -1157,14 +2224,26 @@ dependencies = [ ] [[package]] -name = "getrandom" -version = "0.2.10" +name = "gethostname" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1179,9 +2258,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57052f84e8e5999b258e8adf8f5f2af0ac69033864936b8b6838321db2f759b1" +checksum = "47d809baf02bdf1b5ef4ad3bf60dd9d4977149db4612b7bbb58e56aef168193b" dependencies = [ "futures-channel", "futures-core", @@ -1210,12 +2289,23 @@ dependencies = [ ] [[package]] -name = "glib" -version = "0.18.2" +name = "gl_generator" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c316afb01ce8067c5eaab1fc4f2cd47dc21ce7b6296358605e2ffab23ccbd19" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" dependencies = [ - "bitflags 2.4.0", + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glib" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cf801b6f7829fa76db37449ab67c9c98a2b1bf21076d9113225621e61a0fa6" +dependencies = [ + "bitflags 2.4.1", "futures-channel", "futures-core", "futures-executor", @@ -1234,16 +2324,16 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8da903822b136d42360518653fcf154455defc437d3e7a81475bf9a95ff1e47" +checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5" dependencies = [ "heck", - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1264,11 +2354,101 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globalcache" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469dba5c15b33d67400508ff1f640e8906fa6c8d5ee80540203eb9029ce475df" +checksum = "ccd40efe5b4f0021ca3c36a140cb365563be3c579653b573a5a8ac69bd6f9028" dependencies = [ "async-trait", + "tuple", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "glow" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886c2a30b160c4c6fec8f987430c26b526b7988ca71f664e6a699ddf6f9601e4" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca18d477e18c996c1fd1a50e04c6a745b67e2d512c7fb51f2757d9486a0e3ee" +dependencies = [ + "bitflags 2.4.1", + "cfg_aliases", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.1", + "objc2", + "once_cell", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle 0.5.2", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +dependencies = [ + "gl_generator", + "windows-sys 0.48.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", ] [[package]] @@ -1364,7 +2544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f" dependencies = [ "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", @@ -1392,10 +2572,12 @@ dependencies = [ [[package]] name = "half" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" dependencies = [ + "bytemuck", + "cfg-if", "crunchy", ] @@ -1425,9 +2607,13 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1435,6 +2621,18 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.12.1" @@ -1444,6 +2642,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "humansize" version = "2.1.3" @@ -1453,6 +2660,244 @@ dependencies = [ "libm", ] +[[package]] +name = "i-slint-backend-linuxkms" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d0c47573bf4fb9ffbab394464e51b564465e83e5860d4269e78f6c08cd4be9" +dependencies = [ + "calloop", + "drm 0.9.0", + "gbm", + "glutin", + "i-slint-common", + "i-slint-core", + "i-slint-renderer-femtovg", + "i-slint-renderer-skia", + "input", + "libseat", + "nix 0.27.1", + "raw-window-handle 0.5.2", + "vulkano", + "xkbcommon", +] + +[[package]] +name = "i-slint-backend-selector" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074d3ca24790f1be67e969c86356483cf9bef58ca77ef5d0a8051d25ddddc601" +dependencies = [ + "cfg-if", + "i-slint-backend-linuxkms", + "i-slint-backend-winit", + "i-slint-common", + "i-slint-core", + "i-slint-renderer-skia", +] + +[[package]] +name = "i-slint-backend-winit" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565ece613df99476e516a8a3c853269795fc06b553f66ed0a8f7159d4cb5c975" +dependencies = [ + "bytemuck", + "cfg-if", + "cfg_aliases", + "cocoa", + "const-field-offset", + "copypasta", + "derive_more", + "glutin", + "glutin-winit", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "i-slint-renderer-femtovg", + "i-slint-renderer-skia", + "imgref", + "lyon_path", + "once_cell", + "pin-weak", + "raw-window-handle 0.5.2", + "rgb", + "scoped-tls-hkt", + "scopeguard", + "send_wrapper", + "softbuffer", + "vtable", + "wasm-bindgen", + "web-sys", + "winit", +] + +[[package]] +name = "i-slint-common" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6666bf1b9c3140d426071dda1af839ce46c0f482f3513eecf700609f0cd43865" +dependencies = [ + "cfg-if", + "derive_more", + "fontdb", + "libloading 0.8.1", +] + +[[package]] +name = "i-slint-compiler" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27248681505254959a803253f3c8539b66412c14654d2dc905be869a7ff20159" +dependencies = [ + "by_address", + "codemap", + "codemap-diagnostic", + "css-color-parser2", + "derive_more", + "fontdue", + "i-slint-common", + "image", + "itertools 0.12.0", + "linked_hash_set", + "lyon_extra", + "lyon_path", + "num_enum", + "once_cell", + "proc-macro2", + "quote", + "resvg", + "rowan", + "smol_str", + "strum", + "thiserror", + "url", +] + +[[package]] +name = "i-slint-core" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462a3ca4e929f66d39a00436257baa5a4fb3e287c16675f2219757b67b2dad94" +dependencies = [ + "auto_enums", + "bytemuck", + "cfg-if", + "clru", + "const-field-offset", + "derive_more", + "euclid", + "fontdue", + "i-slint-common", + "i-slint-core-macros", + "image", + "integer-sqrt", + "lyon_algorithms", + "lyon_extra", + "lyon_geom", + "lyon_path", + "num-traits", + "once_cell", + "pin-project", + "pin-weak", + "portable-atomic", + "resvg", + "rgb", + "rustybuzz 0.11.0", + "scoped-tls-hkt", + "scopeguard", + "slab", + "static_assertions", + "strum", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", + "vtable", + "wasm-bindgen", + "web-sys", + "web-time", +] + +[[package]] +name = "i-slint-core-macros" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9148dba9642f055cdda5060f2e82be4a4c1c4a046ea1d08970e9279220b7ed13" +dependencies = [ + "quote", + "syn 2.0.39", +] + +[[package]] +name = "i-slint-renderer-femtovg" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3d130c56f63fa13ec909ba868af84f2e763ff177f5c00857a91e61e65bb55" +dependencies = [ + "cfg-if", + "const-field-offset", + "core-foundation", + "core-text", + "derive_more", + "dwrote", + "femtovg", + "glow", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "imgref", + "lyon_path", + "once_cell", + "pin-weak", + "raw-window-handle 0.5.2", + "rgb", + "scoped-tls-hkt", + "ttf-parser 0.20.0", + "unicode-script", + "unicode-segmentation", + "vtable", + "wasm-bindgen", + "web-sys", + "winapi", +] + +[[package]] +name = "i-slint-renderer-skia" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb23ce40a535a99e735a5b5be696a9aaf0b67a84287c0857f01dd29b2d62fb2" +dependencies = [ + "ash", + "bytemuck", + "cfg-if", + "cfg_aliases", + "cocoa", + "const-field-offset", + "core-foundation", + "core-graphics-types", + "derive_more", + "foreign-types", + "glow", + "glutin", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "lyon_path", + "metal", + "objc", + "once_cell", + "pin-weak", + "raw-window-handle 0.5.2", + "scoped-tls-hkt", + "skia-safe", + "softbuffer", + "unicode-segmentation", + "vtable", + "vulkano", + "winapi", + "wio", +] + [[package]] name = "i18n-config" version = "0.4.6" @@ -1463,7 +2908,7 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "toml 0.8.2", + "toml 0.8.8", "unic-langid", ] @@ -1506,35 +2951,35 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.38", + "syn 2.0.39", "unic-langid", ] [[package]] name = "i18n-embed-impl" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a4d5bff745c9a6e1459c490059281b353a4ab0a4e1e58b3eeeaef71f97d07b" +checksum = "81093c4701672f59416582fe3145676126fd23ba5db910acad0793c1108aaa58" dependencies = [ "find-crate", "i18n-config", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows-core", ] [[package]] @@ -1546,6 +2991,17 @@ dependencies = [ "cc", ] +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1554,9 +3010,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1614,6 +3070,18 @@ dependencies = [ "serde_yaml", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + +[[package]] +name = "imgref" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90d944e334f00f4449c9640b440a171f816be0152305c12ef90424fc35fd035c" + [[package]] name = "indexmap" version = "1.9.3" @@ -1626,12 +3094,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.3", ] [[package]] @@ -1662,6 +3130,47 @@ dependencies = [ "generic-array", ] +[[package]] +name = "input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" +dependencies = [ + "bitflags 1.3.2", + "input-sys", + "io-lifetimes", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + [[package]] name = "intl-memoizer" version = "0.5.1" @@ -1681,6 +3190,17 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -1718,12 +3238,52 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -1735,19 +3295,83 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "krokiet" +version = "6.1.0" +dependencies = [ + "chrono", + "crossbeam-channel", + "czkawka_core", + "directories-next", + "handsome_logger", + "home", + "humansize", + "i18n-embed", + "i18n-embed-fl", + "image", + "image_hasher", + "log", + "once_cell", + "open", + "rand", + "rayon", + "rfd", + "rust-embed", + "serde", + "serde_json", + "slint", + "slint-build", +] + +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lebe" version = "0.5.2" @@ -1756,9 +3380,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libheif-rs" @@ -1780,12 +3404,103 @@ dependencies = [ "libc", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libraw-rs" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ec60aab878560c299c6e70a0c6dc2278a2159ac6fe09650917266b8985387f" +dependencies = [ + "libraw-rs-sys", +] + +[[package]] +name = "libraw-rs-sys" +version = "0.0.4+libraw-0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba094a3b8b04cc42fdeafaff06f81d3b13a7d01cc7a8eae55b943dae1b65c3cc" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libseat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a0adf8d8607a73a5b74cbe4132f57cb349e4bf860103cd089461bbcbc9907e" +dependencies = [ + "errno", + "libseat-sys", + "log", +] + +[[package]] +name = "libseat-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3671cb5e03871f1d6bf0b3b5daa9275549e348fa6359e0f9adb910ca163d4c34" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1793,10 +3508,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "linux-raw-sys" -version = "0.4.10" +name = "linked_hash_set" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "locale_config" @@ -1813,9 +3543,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1823,9 +3553,9 @@ dependencies = [ [[package]] name = "lofty" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa7a62ede7d634892901a2be8bb32f3e13d0418f276d2a391a509afe050f01b" +checksum = "c18ba58211b3c3557970755d7afc3a7438c2d4557bcd684470b2195c0ae66e53" dependencies = [ "base64", "byteorder", @@ -1845,7 +3575,7 @@ checksum = "764b60e1ddd07e5665a6a17636a95cd7d8f3b86c73503a69c32979d05f72f3cf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1853,6 +3583,9 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] [[package]] name = "loom" @@ -1869,6 +3602,52 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" + +[[package]] +name = "lyon_algorithms" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3bca95f9a4955b3e4a821fbbcd5edfbd9be2a9a50bb5758173e5358bfb4c623" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_extra" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ce2ae38f2480094ec1f0d5df51a75581fa84f0e8f32a0edb1d264630c99f3b" +dependencies = [ + "lyon_path", +] + +[[package]] +name = "lyon_geom" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca507745ba7ccbc76e5c44e7b63b1a29d2b0d6126f375806a5bbaf657c7d6c45" +dependencies = [ + "lyon_geom", + "num-traits", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1899,6 +3678,33 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -1908,6 +3714,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.4.1", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "mime" version = "0.3.17" @@ -1924,6 +3745,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1943,6 +3770,79 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.4.1", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1999,6 +3899,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +dependencies = [ + "proc-macro-crate 2.0.0", + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] @@ -2017,6 +3939,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", + "objc_exception", ] [[package]] @@ -2030,6 +3953,37 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + [[package]] name = "objc_id" version = "0.1.1" @@ -2053,18 +4007,51 @@ name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +dependencies = [ + "atomic-polyfill", + "critical-section", +] [[package]] name = "open" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfabf1927dce4d6fdf563d63328a0a506101ced3ec780ca2135747336c98cef8" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" dependencies = [ "is-wsl", "libc", "pathdiff", ] +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox 0.0.2", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "winapi", +] + [[package]] name = "overload" version = "0.1.1" @@ -2072,10 +4059,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] -name = "pango" -version = "0.18.0" +name = "owned_ttf_parser" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a9e54b831d033206160096b825f2070cf5fda7e35167b1c01e9e774f9202d1" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser 0.20.0", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ "gio", "glib", @@ -2096,6 +4092,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -2108,13 +4110,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -2169,7 +4171,7 @@ dependencies = [ "globalcache", "inflate", "istring", - "itertools", + "itertools 0.10.5", "jpeg-decoder", "log", "md5", @@ -2193,10 +4195,42 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.3.0" +name = "peeking_take_while" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] [[package]] name = "pin-project-lite" @@ -2210,6 +4244,23 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pin-weak" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b330c9d1b92dfe68442ca20b009c717d5f0b1e3cf4965e62f704c3c6e95a1305" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -2229,6 +4280,51 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.26", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "portable-atomic" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +dependencies = [ + "critical-section", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2241,6 +4337,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] + [[package]] name = "primal-check" version = "0.3.3" @@ -2260,6 +4366,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2286,9 +4401,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -2302,6 +4417,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.33" @@ -2341,6 +4465,18 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rawloader" version = "0.37.1" @@ -2376,6 +4512,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + [[package]] name = "realfft" version = "3.3.0" @@ -2385,15 +4527,6 @@ dependencies = [ "rustfft 6.1.0", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -2404,26 +4537,35 @@ dependencies = [ ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox 0.0.1", "thiserror", ] [[package]] name = "regex" -version = "1.10.0" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.1", - "regex-syntax 0.8.1", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -2437,13 +4579,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.1", + "regex-syntax 0.8.2", ] [[package]] @@ -2454,9 +4596,93 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "resvg" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rfd" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +dependencies = [ + "ashpd", + "async-io 1.13.0", + "block", + "dispatch", + "futures-util", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle 0.5.2", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rowan" +version = "0.15.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "906057e449592587bf6724f00155bf82a6752c868d78a8fb3aa41f4e6357cfe8" +dependencies = [ + "countme", + "hashbrown 0.12.3", + "memoffset 0.9.0", + "rustc-hash", + "text-size", +] + +[[package]] +name = "roxmltree" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" +dependencies = [ + "xmlparser", +] [[package]] name = "rubato" @@ -2490,7 +4716,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.38", + "syn 2.0.39", "walkdir", ] @@ -2568,15 +4794,51 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.19" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ - "bitflags 2.4.0", + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +dependencies = [ + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys", - "windows-sys", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -2595,6 +4857,38 @@ dependencies = [ "rustfft 6.1.0", ] +[[package]] +name = "rustybuzz" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "smallvec", + "ttf-parser 0.19.2", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "rustybuzz" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee8fe2a8461a0854a37101fe7a1b13998d0cfa987e43248e81d2a5f4570f6fa" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "smallvec", + "ttf-parser 0.20.0", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.15" @@ -2616,6 +4910,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scoped-tls-hkt" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddc765d3410d9f6c6ca071bf0b67f6b01e3ec4595dc3892f02677e75819dddc" + [[package]] name = "scopeguard" version = "1.2.0" @@ -2623,10 +4923,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "self_cell" -version = "0.10.2" +name = "sct" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sctk-adwaita" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729a30a469de249c6effc17ec8d039b0aa29b3af79b819b7f51cb6ab8046a90" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.0", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "self_cell" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.2", +] + +[[package]] +name = "self_cell" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" [[package]] name = "semver" @@ -2635,30 +4967,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] -name = "serde" -version = "1.0.189" +name = "send_wrapper" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -2666,10 +5004,21 @@ dependencies = [ ] [[package]] -name = "serde_spanned" -version = "0.6.3" +name = "serde_repr" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -2717,12 +5066,73 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "skia-bindings" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0af1b86d01552a56d70b515e2bc1af9123acffcc679a7410010e2648f59fbec3" +dependencies = [ + "bindgen", + "cc", + "flate2", + "heck", + "lazy_static", + "regex", + "serde_json", + "tar", + "toml 0.8.8", + "ureq", +] + +[[package]] +name = "skia-safe" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed678303df69daf5b666faf477800ff7fcb7b67316b10a94c31d748d42dd5a83" +dependencies = [ + "bitflags 2.4.1", + "lazy_static", + "skia-bindings", + "winapi", + "wio", +] + [[package]] name = "slab" version = "0.4.9" @@ -2733,10 +5143,105 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.11.1" +name = "slint" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "39bf2b4e9a979d7a191d5e71728c1d15f8af8049f44208cb337126c17474f166" +dependencies = [ + "const-field-offset", + "i-slint-backend-selector", + "i-slint-core", + "i-slint-renderer-femtovg", + "num-traits", + "once_cell", + "pin-weak", + "slint-macros", + "vtable", +] + +[[package]] +name = "slint-build" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc96ba4cee939fb4015c213309fcdfe83178730766dbb5fb643d5938030d63e7" +dependencies = [ + "i-slint-compiler", + "spin_on", + "thiserror", + "toml_edit 0.21.0", +] + +[[package]] +name = "slint-macros" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f6e88032ed51af2445d1b317af16778348566ddc5b397fc4e5bb1bb6b358976" +dependencies = [ + "i-slint-compiler", + "proc-macro2", + "quote", + "spin_on", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" +dependencies = [ + "bitflags 2.4.1", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.0", + "rustix 0.38.26", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb62b280ce5a5cba847669933a0948d00904cf83845c944eae96a4738cea1a6" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] [[package]] name = "snafu" @@ -2760,6 +5265,47 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "softbuffer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826da2ead8e85d1b4ea579fae3d58ec10c81a77d61deab8918c4a4f7514b2948" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases", + "cocoa", + "core-graphics", + "drm 0.10.0", + "fastrand 2.0.1", + "foreign-types", + "js-sys", + "log", + "memmap2 0.9.0", + "objc", + "raw-window-handle 0.5.2", + "redox_syscall 0.4.1", + "rustix 0.38.26", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.48.0", + "x11rb", +] + [[package]] name = "spin" version = "0.9.8" @@ -2769,6 +5315,21 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spin_on" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076e103ed41b9864aa838287efe5f4e3a7a0362dd00671ae62a212e5e4612da2" +dependencies = [ + "pin-utils", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "state" version = "0.6.0" @@ -2778,12 +5339,27 @@ dependencies = [ "loom", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strength_reduce" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + [[package]] name = "stringprep" version = "0.1.4" @@ -2801,12 +5377,44 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.39", +] + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "svgtypes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +dependencies = [ + "kurbo", + "siphasher", +] + [[package]] name = "symphonia" version = "0.5.3" @@ -3003,9 +5611,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -3014,63 +5622,80 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.2" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af52f9402f94aac4948a2518b43359be8d9ce6cd9efc1c4de3b2f7b7e897d6" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 0.8.2", + "toml 0.8.8", "version-compare", ] [[package]] -name = "target-lexicon" -version = "0.12.11" +name = "tar" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.26", + "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] -name = "thiserror" -version = "1.0.49" +name = "text-size" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3126,10 +5751,48 @@ dependencies = [ ] [[package]] -name = "tinystr" -version = "0.7.4" +name = "tiny-skia" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" +checksum = "3b72a92a05db376db09fe6d50b7948d106011761c05a6a45e23e17ee9b556222" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac3865b9708fc7e1961a65c3a4fa55e984272f33092d3c859929f887fceb647" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d" +dependencies = [ + "as-raw-xcb-connection", + "ctor", + "libloading 0.8.1", + "tracing", +] + +[[package]] +name = "tinystr" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" dependencies = [ "displaydoc", ] @@ -3160,21 +5823,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.21.0", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -3185,18 +5848,29 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -3205,9 +5879,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3222,7 +5896,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -3237,20 +5911,20 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -3276,9 +5950,9 @@ dependencies = [ [[package]] name = "trash" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b23f2b0cf93f537bbe90cbb59ea9176cc8ce9b010a36dcd5b726facd82825e" +checksum = "8c646008e5144d988005bec12b1e56f5e0a951e957176686815eba8b025e0418" dependencies = [ "chrono", "libc", @@ -3296,6 +5970,28 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622b09ce2fe2df4618636fb92176d205662f59803f39e70d1c333393082de96c" +[[package]] +name = "ttf-parser" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "tuple" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a40ba241047e1174c927dc5f61c141a166b938d61a2ff61838441368cc7d0e" +dependencies = [ + "num-traits", + "serde", +] + [[package]] name = "type-map" version = "0.4.0" @@ -3320,6 +6016,27 @@ dependencies = [ "const_fn", ] +[[package]] +name = "udev" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" +dependencies = [ + "libc", + "libudev-sys", + "pkg-config", +] + +[[package]] +name = "uds_windows" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +dependencies = [ + "tempfile", + "winapi", +] + [[package]] name = "unic-langid" version = "0.9.1" @@ -3354,12 +6071,30 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -3370,14 +6105,128 @@ dependencies = [ ] [[package]] -name = "url" -version = "2.4.1" +name = "unicode-properties" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" + +[[package]] +name = "unicode-script" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-webpki", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "usvg" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" +dependencies = [ + "base64", + "log", + "pico-args", + "usvg-parser", + "usvg-text-layout", + "usvg-tree", + "xmlwriter", +] + +[[package]] +name = "usvg-parser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" +dependencies = [ + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "roxmltree", + "simplecss", + "siphasher", + "svgtypes", + "usvg-tree", +] + +[[package]] +name = "usvg-text-layout" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" +dependencies = [ + "fontdb", + "kurbo", + "log", + "rustybuzz 0.10.0", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "usvg-tree", +] + +[[package]] +name = "usvg-tree" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" +dependencies = [ + "rctree", + "strict-num", + "svgtypes", + "tiny-skia-path", ] [[package]] @@ -3388,9 +6237,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" [[package]] name = "valuable" @@ -3398,6 +6247,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" + [[package]] name = "version-compare" version = "0.1.1" @@ -3427,6 +6282,73 @@ dependencies = [ "transpose", ] +[[package]] +name = "vk-parse" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "vtable" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f4c7506238561777a1861d3dc3c0001877c475187e7bc4392ea87ebf631fd9c" +dependencies = [ + "const-field-offset", + "portable-atomic", + "stable_deref_trait", + "vtable-macro", +] + +[[package]] +name = "vtable-macro" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2b8eecdb8e4284adf5546fc518f048f6dc33e7203dbe36fa93a4add39b31f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "vulkano" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f4278f76307b3c388679234b397b4f90de29cdba53873c26b624ed82653d75" +dependencies = [ + "ahash", + "ash", + "bytemuck", + "core-graphics-types", + "crossbeam-queue", + "half", + "heck", + "indexmap 2.1.0", + "libloading 0.8.1", + "objc", + "once_cell", + "parking_lot", + "proc-macro2", + "quote", + "raw-window-handle 0.5.2", + "regex", + "serde", + "serde_json", + "smallvec", + "thread_local", + "vk-parse", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + [[package]] name = "walkdir" version = "2.4.0" @@ -3445,9 +6367,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3455,24 +6377,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" +name = "wasm-bindgen-futures" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3480,22 +6414,157 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wayland-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" +dependencies = [ + "cc", + "downcast-rs", + "nix 0.26.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +dependencies = [ + "bitflags 2.4.1", + "nix 0.26.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.4.1", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" +dependencies = [ + "nix 0.26.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "weezl" @@ -3503,6 +6572,18 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.26", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3528,6 +6609,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3552,6 +6642,24 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3561,6 +6669,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -3591,6 +6708,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3603,6 +6735,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3615,6 +6753,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3627,6 +6771,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3639,6 +6789,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3651,6 +6807,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3663,6 +6825,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3676,14 +6844,199 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winnow" -version = "0.5.17" +name = "windows_x86_64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winit" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.4.1", + "bytemuck", + "calloop", + "cfg_aliases", + "core-foundation", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2 0.9.0", + "ndk", + "ndk-sys", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "redox_syscall 0.3.5", + "rustix 0.38.26", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "x11-clipboard" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41aca1115b1f195f21c541c5efb423470848d48143127d0f07f8b90c27440df" +dependencies = [ + "x11rb", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.7.4", + "nix 0.26.4", + "once_cell", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" +dependencies = [ + "nix 0.26.4", +] + +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix 0.26.4", + "winapi", +] + +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" +dependencies = [ + "bitflags 2.4.1", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "xxhash-rust" version = "0.8.7" @@ -3699,6 +7052,92 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zbus" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zip" version = "0.6.6" @@ -3726,3 +7165,42 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/Cargo.toml b/Cargo.toml index b0946f4..c8806cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,10 @@ members = [ "czkawka_core", "czkawka_cli", "czkawka_gui", + "krokiet" ] exclude = [ "ci_tester", - "czkawka_slint_gui" ] resolver = "2" diff --git a/Changelog.md b/Changelog.md index 02f9a1f..89b8d1c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,22 @@ +## Version 7.0.0 - ? +### GTK GUI +- Added drag&drop support for included/excluded folders - [#1106](https://github.com/qarmin/czkawka/pull/1106) +- Added information where are saved scan results - [#1102](https://github.com/qarmin/czkawka/pull/1102) + +### CLI +- Providing full static rust binary with [Eyra](https://github.com/sunfishcode/eyra) - [#1102](https://github.com/qarmin/czkawka/pull/1102) + +### Krokiet GUI +- Initial release of new gui - [#1102](https://github.com/qarmin/czkawka/pull/1102) + +### Core +- Using normal crossbeam channels instead of asyncio tokio channel - [#1102](https://github.com/qarmin/czkawka/pull/1102) +- Fixed tool type when using progress of empty directories - [#1102](https://github.com/qarmin/czkawka/pull/1102) +- Fixed missing json support in saving size and name - [#1102](https://github.com/qarmin/czkawka/pull/1102) +- Fix cross-compiled debug windows build - [#1102](https://github.com/qarmin/czkawka/pull/1102) +- Added bigger stack size by default(fixes stack overflow in some musl apps) - [#1102](https://github.com/qarmin/czkawka/pull/1102) +- Added optional libraw dependency(better single-core performance and support more raw files) - [#1102](https://github.com/qarmin/czkawka/pull/1102) + ## Version 6.1.0 - 15.10.2023r - BREAKING CHANGE - Changed cache saving method, deduplicated, optimized and simplified procedure(all files needs to be hashed again) - [#1072](https://github.com/qarmin/czkawka/pull/1072), [#1086](https://github.com/qarmin/czkawka/pull/1086) - Remove up to 340ms of delay when waiting for results - [#1070](https://github.com/qarmin/czkawka/pull/1070) diff --git a/README.md b/README.md index 71080b9..b740b9a 100644 --- a/README.md +++ b/README.md @@ -27,24 +27,12 @@ ![Czkawka](https://user-images.githubusercontent.com/41945903/145280350-506f7e94-4db0-4de7-a68d-6e7c26bbd2bf.gif) -## Supported OS -Linux - Ubuntu 22.04+, Fedora 36+, Alpine Linux 3.16+, Debian 12+ and a lot of more - -Windows - 7, 8.1, 10, 11 -MacOS - 10.15+ - -If you are looking for older version that use GTK 3 and have support for more OS(like e.g. Ubuntu 20.04), look at [4.1.0](https://github.com/qarmin/czkawka/releases/tag/4.1.0) or older versions. - -## How do I use it? -You can find the instructions on how to use Czkawka [**here**](instructions/Instruction.md). - -Some helpful tricks you can find [**here**](instructions/Instruction.md#tips-tricks-and-known-bugs) - -## Installation -Installation instructions with download links you can find [**here**](instructions/Installation.md). - -## Compilation -If you want to try and develop Czkawka or just use the latest available feature, you may want to look at the [**compilation instructions**](instructions/Compilation.md). +## Usage, installation, compilation, requirements, license +Each tool uses different technologies, so you can find instructions for each of them in the appropriate file: +- [Czkawka GUI (GTK frontend)](czkawka_gui/README.md)
+- [Czkawka CLI](czkawka_cli/README.md)
+- [Czkawka Core](czkawka_core/README.md)
+- [Krokiet GUI (Slint frontend)](krokiet/README.md)
## Benchmarks @@ -99,31 +87,34 @@ Similar images which check 349 image files that occupied 1.7 GB Bleachbit is a master at finding and removing temporary files, while Czkawka only finds the most basic ones. So these two apps shouldn't be compared directly or be considered as an alternative to one another. -| | Czkawka | FSlint | DupeGuru | Bleachbit | -|:------------------------:|:-----------:|:----------:|:-----------------:|:-----------:| -| Language | Rust | Python | Python/Obj-C | Python | -| OS | Lin,Mac,Win | Lin | Lin,Mac,Win | Lin,Mac,Win | -| Framework | GTK 4 | PyGTK2 | Qt 5 (PyQt)/Cocoa | PyGTK3 | -| Duplicate finder | ✔ | ✔ | ✔ | | -| Empty files | ✔ | ✔ | | | -| Empty folders | ✔ | ✔ | | | -| Temporary files | ✔ | ✔ | | ✔ | -| Big files | ✔ | | | | -| Similar images | ✔ | | ✔ | | -| Similar videos | ✔ | | | | -| Music duplicates(tags) | ✔ | | ✔ | | -| Invalid symlinks | ✔ | ✔ | | | -| Broken files | ✔ | | | | -| Names conflict | ✔ | ✔ | | | -| Invalid names/extensions | ✔ | ✔ | | | -| Installed packages | | ✔ | | | -| Bad ID | | ✔ | | | -| Non stripped binaries | | ✔ | | | -| Redundant whitespace | | ✔ | | | -| Overwriting files | | ✔ | | ✔ | -| Multiple languages | ✔ | ✔ | ✔ | ✔ | -| Cache support | ✔ | | ✔ | | -| In active development | Yes | No | Yes | Yes | +In this comparison remember, that even if app have same features they may work different(e.g. one app may have more options to choose than other). + +| | Czkawka | Krokiet | FSlint | DupeGuru | Bleachbit | +|:------------------------:|:-----------:|:-----------:|:------:|:------------------:|:-----------:| +| Language | Rust | Rust | Python | Python/Obj-C | Python | +| Framework base language | C | Rust | C | C/C++/Obj-C/Swift | C | +| Framework | GTK 4 | Slint | PyGTK2 | Qt 5 (PyQt)/Cocoa | PyGTK3 | +| OS | Lin,Mac,Win | Lin,Mac,Win | Lin | Lin,Mac,Win | Lin,Mac,Win | +| Duplicate finder | ✔ | ✔ | ✔ | ✔ | | +| Empty files | ✔ | ✔ | ✔ | | | +| Empty folders | ✔ | ✔ | ✔ | | | +| Temporary files | ✔ | ✔ | ✔ | | ✔ | +| Big files | ✔ | ✔ | | | | +| Similar images | ✔ | ✔ | | ✔ | | +| Similar videos | ✔ | ✔ | | | | +| Music duplicates(tags) | ✔ | ✔ | | ✔ | | +| Invalid symlinks | ✔ | ✔ | ✔ | | | +| Broken files | ✔ | ✔ | | | | +| Names conflict | ✔ | ✔ | ✔ | | | +| Invalid names/extensions | ✔ | ✔ | ✔ | | | +| Installed packages | | | ✔ | | | +| Bad ID | | | ✔ | | | +| Non stripped binaries | | | ✔ | | | +| Redundant whitespace | | | ✔ | | | +| Overwriting files | | | ✔ | | ✔ | +| Multiple languages | ✔ | | ✔ | ✔ | ✔ | +| Cache support | ✔ | ✔ | | ✔ | | +| In active development | Yes | | No | Yes | Yes | ## Other apps There are many similar applications to Czkawka on the Internet, which do some things better and some things worse: @@ -156,30 +147,6 @@ You can also help by doing other things: - Creating videos - [First Video](https://www.youtube.com/watch?v=CWlRiTD4vDc) or [Spanish Tutorial](https://www.youtube.com/watch?v=V9x-pHJRmKY) - Recommending it to others -## Name -Czkawka is a Polish word which means _hiccup_. - -I chose this name because I wanted to hear people speaking other languages pronounce it, so feel free to spell it the way you want. - -This name is not as bad as it seems, because I was also thinking about using words like _żółć_, _gżegżółka_ or _żołądź_, -but I gave up on these ideas because they contained Polish characters, which would cause difficulty in searching for the project. - -At the beginning of the program creation, if the response concerning the name was unanimously negative, I prepared myself -for a possible change of the name of the program, and the opinions were extremely mixed. - -## License -Code is distributed under MIT license. - -Icon was created by [jannuary](https://github.com/jannuary) and licensed CC-BY-4.0. - -Windows dark theme is used from project [WhiteSur](https://github.com/slypy/whitesur-gtk4-theme) with MIT license. - -Some icons were taken from [ReShot](https://www.reshot.com) site and are licensed under Reshot Free License. - -The program is completely free to use. - -"Gratis to uczciwa cena" - "Free is a fair price" - ## Thanks Big thanks to Pádraig Brady, creator of fantastic FSlint, because without his work I wouldn't create this tool. diff --git a/czkawka_cli/Cargo.toml b/czkawka_cli/Cargo.toml index 783596e..1a3d539 100644 --- a/czkawka_cli/Cargo.toml +++ b/czkawka_cli/Cargo.toml @@ -3,7 +3,7 @@ name = "czkawka_cli" version = "6.1.0" authors = ["Rafał Mikrut "] edition = "2021" -rust-version = "1.70.0" +rust-version = "1.72.1" description = "CLI frontend of Czkawka" license = "MIT" homepage = "https://github.com/qarmin/czkawka" @@ -23,3 +23,4 @@ czkawka_core = { path = "../czkawka_core", version = "6.1.0", features = [] } [features] default = [] heif = ["czkawka_core/heif"] +libraw = ["czkawka_core/libraw"] diff --git a/czkawka_cli/LICENSE b/czkawka_cli/LICENSE new file mode 100644 index 0000000..8836e46 --- /dev/null +++ b/czkawka_cli/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 Rafał Mikrut + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/czkawka_cli/README.md b/czkawka_cli/README.md new file mode 100644 index 0000000..fb808d8 --- /dev/null +++ b/czkawka_cli/README.md @@ -0,0 +1,44 @@ +# Czkawka CLI + +CLI frontend, allows to use Czkawka from terminal. + +## Requirements +Precompiled binaries should work without any additional dependencies with Linux(Ubuntu 20.04+), Windows(10+) and macOS(10.15+). + +If you decide to compile the app, you probably will be able to run it on even older versions of OS, like Ubuntu 16.04 or Windows 7. + +On linux it is even possible with eyra to avoid entirely libc and using fully static rust binary. + +If you want to use similar videos tool, you need to install ffmpeg(runtime dependency) or use heif/libraw(build/runtime dependency) you need to install required packages. +- mac - `brew install ffmpeg libraw libheif` - https://formulae.brew.sh/formula/ffmpeg +- linux - `sudo apt install ffmpeg libraw-dev libheif-dev` +- windows - `choco install ffmpeg` - or if not working, download from https://ffmpeg.org/download.html#build-windows and unpack to location with `czkawka_cli.exe`, heif and libraw are not supported on windows + +## Compilation +For compilation, you need to have installed Rust via rustup - https://rustup.rs/ and compile it e.g. via +```shell +cargo run --release --bin czkawka_cli +``` +you can enable additional features via +```shell +cargo run --release --bin czkawka_cli --features "heif,libraw" +``` +on linux to build fully static binary with eyra you need to use (this is only for crazy people, so just use command above if you don't know what you are doing) +```shell +rustup default nightly-2023-11-16 # or any newer nightly that works fine with eyra +cd czkawka_cli +cargo add eyra --rename=std +echo 'fn main() { println!("cargo:rustc-link-arg=-nostartfiles"); }' > build.rs +cd .. +cargo build --release --bin czkawka_cli +``` + +## Limitations +Not all available features in core are available in CLI. + +List of not available features: +- Ability to use/choose referenced directories +- See progress of scanning + +## LICENSE +MIT \ No newline at end of file diff --git a/czkawka_core/Cargo.toml b/czkawka_core/Cargo.toml index ba49cc3..897d4dd 100644 --- a/czkawka_core/Cargo.toml +++ b/czkawka_core/Cargo.toml @@ -3,7 +3,7 @@ name = "czkawka_core" version = "6.1.0" authors = ["Rafał Mikrut "] edition = "2021" -rust-version = "1.70.0" +rust-version = "1.72.1" description = "Core of Czkawka app" license = "MIT" homepage = "https://github.com/qarmin/czkawka" @@ -28,9 +28,6 @@ hamming = "0.1" bitflags = "2.4" lofty = "0.16" -# Futures - needed by async progress sender -futures = "0.3.28" - # Needed by broken files zip = { version = "0.6", features = ["aes-crypto", "bzip2", "deflate", "time"], default-features = false } audio_checker = "0.1" @@ -59,12 +56,13 @@ serde_json = "1.0" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" -rust-embed = "8.0" +rust-embed = { version = "8.0", features = ["debug-embed"] } once_cell = "1.18" # Raw image files rawloader = "0.37" imagepipe = "0.5" +libraw-rs = { version = "0.0.4", optional = true } # Checking for invalid extensions mime_guess = "2.0" @@ -72,11 +70,14 @@ infer = "0.15" # Heif/Heic libheif-rs = { version = "=0.18.0", optional = true } # Do not upgrade now, since Ubuntu 22.04 not works with newer version -libheif-sys = { version = "=1.14.2", optional = true } # 1.14.3 brake compilation on Ubuntu 22.04 +libheif-sys = { version = "=1.14.2", optional = true } # 1.14.3 brake compilation on Ubuntu 22.04, so pin it to this version anyhow = { version = "1.0" } + state = "0.6" +os_info = { version = "3", default-features = false } +rustc_version = "0.4" log = "0.4.20" handsome_logger = "0.8" fun_time = { version = "0.3.1", features = ["log"] } @@ -84,3 +85,4 @@ fun_time = { version = "0.3.1", features = ["log"] } [features] default = [] heif = ["dep:libheif-rs", "dep:libheif-sys"] +libraw = ["dep:libraw-rs"] \ No newline at end of file diff --git a/czkawka_core/LICENSE b/czkawka_core/LICENSE new file mode 100644 index 0000000..8836e46 --- /dev/null +++ b/czkawka_core/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 Rafał Mikrut + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/czkawka_core/README.md b/czkawka_core/README.md new file mode 100644 index 0000000..6d39322 --- /dev/null +++ b/czkawka_core/README.md @@ -0,0 +1,3 @@ +# Czkawka Core + +Core of Czkawka GUI/CLI and Krokiet projects. diff --git a/czkawka_core/src/bad_extensions.rs b/czkawka_core/src/bad_extensions.rs index c2bc3b6..429d699 100644 --- a/czkawka_core/src/bad_extensions.rs +++ b/czkawka_core/src/bad_extensions.rs @@ -5,9 +5,8 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use log::debug; use mime_guess::get_mime_extensions; use rayon::prelude::*; @@ -161,7 +160,7 @@ const WORKAROUNDS: &[(&str, &str)] = &[ ("exe", "xls"), // Not sure why xls is not recognized ]; -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Debug)] pub struct BadFileEntry { pub path: PathBuf, pub modified_date: u64, @@ -195,7 +194,7 @@ impl BadExtensions { } #[fun_time(message = "find_bad_extensions_files", level = "info")] - pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -209,7 +208,7 @@ impl BadExtensions { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let result = DirTraversalBuilder::new() .root_dirs(self.common_data.directories.included_directories.clone()) .group_by(|_fe| ()) @@ -221,6 +220,7 @@ impl BadExtensions { .allowed_extensions(self.common_data.allowed_extensions.clone()) .excluded_items(self.common_data.excluded_items.clone()) .recursive_search(self.common_data.recursive_search) + .tool_type(self.common_data.tool_type) .build() .run(); @@ -239,7 +239,7 @@ impl BadExtensions { } #[fun_time(message = "look_for_bad_extensions_files", level = "debug")] - fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn look_for_bad_extensions_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) = prepare_thread_handler_common(progress_sender, 1, 1, self.files_to_check.len(), CheckingMethod::None, self.get_cd().tool_type); diff --git a/czkawka_core/src/big_file.rs b/czkawka_core/src/big_file.rs index 58ac2fa..d5815f7 100644 --- a/czkawka_core/src/big_file.rs +++ b/czkawka_core/src/big_file.rs @@ -6,9 +6,8 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use humansize::{format_size, BINARY}; use log::debug; use rayon::prelude::*; @@ -57,7 +56,7 @@ impl BigFile { } #[fun_time(message = "find_big_files", level = "info")] - pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.look_for_big_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -68,7 +67,7 @@ impl BigFile { } #[fun_time(message = "look_for_big_files", level = "debug")] - fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn look_for_big_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector let mut old_map: BTreeMap> = Default::default(); diff --git a/czkawka_core/src/broken_files.rs b/czkawka_core/src/broken_files.rs index 31f5259..915a75d 100644 --- a/czkawka_core/src/broken_files.rs +++ b/czkawka_core/src/broken_files.rs @@ -7,9 +7,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::{fs, mem, panic}; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use log::debug; use pdf::file::FileOptions; use pdf::object::ParseOptions; @@ -27,7 +26,7 @@ use crate::common_dir_traversal::{common_get_entry_data_metadata, common_read_di use crate::common_tool::{CommonData, CommonToolData, DeleteMethod}; use crate::common_traits::*; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct FileEntry { pub path: PathBuf, pub modified_date: u64, @@ -93,7 +92,7 @@ impl BrokenFiles { } #[fun_time(message = "find_broken_files", level = "info")] - pub fn find_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -108,7 +107,7 @@ impl BrokenFiles { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector // Add root folders for finding @@ -191,9 +190,7 @@ impl BrokenFiles { ) -> Option { atomic_counter.fetch_add(1, Ordering::Relaxed); - let Some(file_name_lowercase) = get_lowercase_name(entry_data, warnings) else { - return None; - }; + let file_name_lowercase = get_lowercase_name(entry_data, warnings)?; if !self.common_data.allowed_extensions.matches_filename(&file_name_lowercase) { return None; @@ -347,7 +344,7 @@ impl BrokenFiles { } #[fun_time(message = "look_for_broken_files", level = "debug")] - fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn look_for_broken_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache(); let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = diff --git a/czkawka_core/src/common.rs b/czkawka_core/src/common.rs index 7a715fe..551939b 100644 --- a/czkawka_core/src/common.rs +++ b/czkawka_core/src/common.rs @@ -1,24 +1,29 @@ +#![allow(unused_imports)] +// I don't wanna fight with unused imports in this file, so simply ignore it to avoid too much complexity use std::ffi::OsString; use std::fs::{DirEntry, File, OpenOptions}; -use std::io::BufReader; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread::{sleep, JoinHandle}; -use std::time::{Duration, SystemTime}; +use std::time::{Duration, Instant, SystemTime}; use std::{fs, thread}; #[cfg(feature = "heif")] use anyhow::Result; +use crossbeam_channel::Sender; use directories_next::ProjectDirs; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use handsome_logger::{ColorChoice, ConfigBuilder, TerminalMode}; use image::{DynamicImage, ImageBuffer, Rgb}; use imagepipe::{ImageSource, Pipeline}; #[cfg(feature = "heif")] use libheif_rs::{ColorSpace, HeifContext, RgbChroma}; -use log::{info, LevelFilter, Record}; +#[cfg(feature = "libraw")] +use libraw::Processor; +use log::{debug, error, info, warn, LevelFilter, Record}; +use rawloader::RawLoader; +use symphonia::core::conv::IntoSample; // #[cfg(feature = "heif")] // use libheif_rs::LibHeif; @@ -32,6 +37,13 @@ use crate::duplicate::make_hard_link; use crate::CZKAWKA_VERSION; static NUMBER_OF_THREADS: state::InitCell = state::InitCell::new(); +pub const DEFAULT_THREAD_SIZE: usize = 8 * 1024 * 1024; // 8 MB +pub const DEFAULT_WORKER_THREAD_SIZE: usize = 4 * 1024 * 1024; // 4 MB + +#[cfg(not(target_family = "windows"))] +pub const CHARACTER: char = '/'; +#[cfg(target_family = "windows")] +pub const CHARACTER: char = '\\'; pub fn get_number_of_threads() -> usize { let data = NUMBER_OF_THREADS.get(); @@ -44,7 +56,7 @@ pub fn get_number_of_threads() -> usize { fn filtering_messages(record: &Record) -> bool { if let Some(module_path) = record.module_path() { - module_path.starts_with("czkawka") + module_path.starts_with("czkawka") || module_path.starts_with("krokiet") } else { true } @@ -57,12 +69,30 @@ pub fn setup_logger(disabled_printing: bool) { handsome_logger::TermLogger::init(config, TerminalMode::Mixed, ColorChoice::Always).unwrap(); } +pub fn get_available_threads() -> usize { + thread::available_parallelism().map(std::num::NonZeroUsize::get).unwrap_or(1) +} + pub fn print_version_mode() { + let rust_version = match rustc_version::version_meta() { + Ok(meta) => meta.semver.to_string(), + Err(_) => "".to_string(), + }; + let debug_release = if cfg!(debug_assertions) { "debug" } else { "release" }; + + let processors = get_available_threads(); + + let info = os_info::get(); info!( - "Czkawka version: {}, was compiled with {} mode", - CZKAWKA_VERSION, - if cfg!(debug_assertions) { "debug" } else { "release" } + "App version: {CZKAWKA_VERSION}, {debug_release} mode, rust {rust_version}, os {} {} [{} {}], {processors} cpu/threads", + info.os_type(), + info.version(), + std::env::consts::ARCH, + info.bitness(), ); + if cfg!(debug_assertions) { + warn!("You are running debug version of app which is a lot of slower than release version."); + } } pub fn set_default_number_of_threads() { @@ -76,7 +106,18 @@ pub fn get_default_number_of_threads() -> usize { pub fn set_number_of_threads(thread_number: usize) { NUMBER_OF_THREADS.set(thread_number); - rayon::ThreadPoolBuilder::new().num_threads(get_number_of_threads()).build_global().unwrap(); + let additional_message = if thread_number == 0 { + " (0 - means that all available threads will be used)" + } else { + "" + }; + debug!("Number of threads set to {thread_number}{additional_message}"); + + rayon::ThreadPoolBuilder::new() + .num_threads(get_number_of_threads()) + .stack_size(DEFAULT_WORKER_THREAD_SIZE) + .build_global() + .unwrap(); } pub const RAW_IMAGE_EXTENSIONS: &[&str] = &[ @@ -111,6 +152,46 @@ pub const SEND_PROGRESS_DATA_TIME_BETWEEN: u32 = 200; //ms pub struct Common(); +pub fn remove_folder_if_contains_only_empty_folders(path: impl AsRef) -> bool { + let path = path.as_ref(); + if !path.is_dir() { + error!("Trying to remove folder which is not a directory"); + return false; + } + + let mut entries_to_check = Vec::new(); + let Ok(initial_entry) = path.read_dir() else { + return false; + }; + for entry in initial_entry { + if let Ok(entry) = entry { + entries_to_check.push(entry); + } else { + return false; + } + } + loop { + let Some(entry) = entries_to_check.pop() else { + break; + }; + if !entry.path().is_dir() { + return false; + } + let Ok(internal_read_dir) = entry.path().read_dir() else { + return false; + }; + for internal_elements in internal_read_dir { + if let Ok(internal_element) = internal_elements { + entries_to_check.push(internal_element); + } else { + return false; + } + } + } + + fs::remove_dir_all(path).is_ok() +} + pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: bool, warnings: &mut Vec) -> Option<((Option, PathBuf), (Option, PathBuf))> { if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") { let cache_dir = PathBuf::from(proj_dirs.cache_dir()); @@ -152,10 +233,7 @@ pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: b file_handler_default = Some(t); } else { if use_json { - file_handler_json = Some(match OpenOptions::new().read(true).open(&cache_file_json) { - Ok(t) => t, - Err(_) => return None, - }); + file_handler_json = Some(OpenOptions::new().read(true).open(&cache_file_json).ok()?); } else { // messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error or warning return None; @@ -183,45 +261,64 @@ pub fn get_dynamic_image_from_heic(path: &str) -> Result { .ok_or_else(|| anyhow::anyhow!("Failed to create image buffer")) } -pub fn get_dynamic_image_from_raw_image(path: impl AsRef + std::fmt::Debug) -> Option { - let file_handler = match OpenOptions::new().read(true).open(&path) { - Ok(t) => t, - Err(_e) => { - return None; - } - }; +#[cfg(feature = "libraw")] +pub fn get_dynamic_image_from_raw_image(path: impl AsRef) -> Option { + let buf = fs::read(path.as_ref()).ok()?; - let mut reader = BufReader::new(file_handler); - let raw = match rawloader::decode(&mut reader) { - Ok(raw) => raw, - Err(_e) => { - return None; - } - }; + let processor = Processor::new(); + let start_timer = Instant::now(); + let processed = processor.process_8bit(&buf).expect("processing successful"); + println!("Processing took {:?}", start_timer.elapsed()); + + let width = processed.width(); + let height = processed.height(); + dbg!(width, height); + + let data = processed.to_vec(); + + let buffer = ImageBuffer::from_raw(width, height, data)?; + // Utwórz DynamicImage z ImageBuffer + Some(DynamicImage::ImageRgb8(buffer)) +} + +#[cfg(not(feature = "libraw"))] +pub fn get_dynamic_image_from_raw_image(path: impl AsRef + std::fmt::Debug) -> Option { + let mut start_timer = Instant::now(); + let mut times = Vec::new(); + + let loader = RawLoader::new(); + let raw = loader.decode_file(path.as_ref()).ok()?; + + times.push(("After decoding", start_timer.elapsed())); + start_timer = Instant::now(); let source = ImageSource::Raw(raw); - let mut pipeline = match Pipeline::new_from_source(source) { - Ok(pipeline) => pipeline, - Err(_e) => { - return None; - } - }; + times.push(("After creating source", start_timer.elapsed())); + start_timer = Instant::now(); + + let mut pipeline = Pipeline::new_from_source(source).ok()?; + + times.push(("After creating pipeline", start_timer.elapsed())); + start_timer = Instant::now(); pipeline.run(None); - let image = match pipeline.output_8bit(None) { - Ok(image) => image, - Err(_e) => { - return None; - } - }; + let image = pipeline.output_8bit(None).ok()?; - let Some(image) = ImageBuffer::, Vec>::from_raw(image.width as u32, image.height as u32, image.data) else { - return None; - }; + times.push(("After creating image", start_timer.elapsed())); + start_timer = Instant::now(); + let image = ImageBuffer::, Vec>::from_raw(image.width as u32, image.height as u32, image.data)?; + + times.push(("After creating image buffer", start_timer.elapsed())); + start_timer = Instant::now(); // println!("Properly hashed {:?}", path); - Some(DynamicImage::ImageRgb8(image)) + let res = Some(DynamicImage::ImageRgb8(image)); + times.push(("After creating dynamic image", start_timer.elapsed())); + + let str_timer = times.into_iter().map(|(name, time)| format!("{name}: {time:?}")).collect::>().join(", "); + debug!("Loading raw image --- {str_timer}"); + res } pub fn split_path(path: &Path) -> (String, String) { @@ -460,13 +557,14 @@ where } pub fn prepare_thread_handler_common( - progress_sender: Option<&UnboundedSender>, + progress_sender: Option<&Sender>, current_stage: u8, max_stage: u8, max_value: usize, checking_method: CheckingMethod, tool_type: ToolType, ) -> (JoinHandle<()>, Arc, Arc, AtomicBool) { + assert_ne!(tool_type, ToolType::None, "ToolType::None should not exist"); let progress_thread_run = Arc::new(AtomicBool::new(true)); let atomic_counter = Arc::new(AtomicUsize::new(0)); let check_was_stopped = AtomicBool::new(false); @@ -480,7 +578,7 @@ pub fn prepare_thread_handler_common( loop { if time_since_last_send.elapsed().unwrap().as_millis() > SEND_PROGRESS_DATA_TIME_BETWEEN as u128 { progress_send - .unbounded_send(ProgressData { + .send(ProgressData { checking_method, current_stage, max_stage, @@ -521,9 +619,36 @@ pub fn send_info_and_wait_for_ending_all_threads(progress_thread_run: &Arc { group_by: Option, root_dirs: Vec, stop_receiver: Option<&'a Receiver<()>>, - progress_sender: Option<&'b UnboundedSender>, + progress_sender: Option<&'b Sender>, minimal_file_size: Option, maximal_file_size: Option, checking_method: CheckingMethod, @@ -153,7 +152,7 @@ pub struct DirTraversal<'a, 'b, F> { group_by: F, root_dirs: Vec, stop_receiver: Option<&'a Receiver<()>>, - progress_sender: Option<&'b UnboundedSender>, + progress_sender: Option<&'b Sender>, recursive_search: bool, directories: Directories, excluded_items: ExcludedItems, @@ -188,7 +187,7 @@ impl<'a, 'b> DirTraversalBuilder<'a, 'b, ()> { directories: None, allowed_extensions: None, excluded_items: None, - tool_type: ToolType::BadExtensions, + tool_type: ToolType::None, } } } @@ -204,7 +203,7 @@ impl<'a, 'b, F> DirTraversalBuilder<'a, 'b, F> { self } - pub fn progress_sender(mut self, progress_sender: Option<&'b UnboundedSender>) -> Self { + pub fn progress_sender(mut self, progress_sender: Option<&'b Sender>) -> Self { self.progress_sender = progress_sender; self } @@ -342,6 +341,8 @@ where { #[fun_time(message = "run(collecting files/dirs)", level = "debug")] pub fn run(self) -> DirTraversalResult { + assert!(self.tool_type != ToolType::None, "Tool type cannot be None"); + let mut all_warnings = vec![]; let mut grouped_file_entries: BTreeMap> = BTreeMap::new(); let mut folder_entries: BTreeMap = BTreeMap::new(); diff --git a/czkawka_core/src/common_items.rs b/czkawka_core/src/common_items.rs index f6ecf8c..668e86c 100644 --- a/czkawka_core/src/common_items.rs +++ b/czkawka_core/src/common_items.rs @@ -2,6 +2,11 @@ use std::path::Path; use crate::common::Common; +#[cfg(target_family = "unix")] +pub const DEFAULT_EXCLUDED_DIRECTORIES: &[&str] = &["/proc", "/dev", "/sys", "/run", "/snap"]; +#[cfg(not(target_family = "unix"))] +pub const DEFAULT_EXCLUDED_DIRECTORIES: &[&str] = &["C:\\Windows"]; + #[cfg(target_family = "unix")] pub const DEFAULT_EXCLUDED_ITEMS: &str = "*/.git/*,*/node_modules/*,*/lost+found/*,*/Trash/*,*/.Trash-*/*,*/snap/*,/home/*/.cache/*"; #[cfg(not(target_family = "unix"))] diff --git a/czkawka_core/src/common_traits.rs b/czkawka_core/src/common_traits.rs index 13a3da5..27aaebd 100644 --- a/czkawka_core/src/common_traits.rs +++ b/czkawka_core/src/common_traits.rs @@ -35,7 +35,7 @@ pub trait PrintResults { fn save_results_to_file_as_json(&self, file_name: &str, pretty_print: bool) -> std::io::Result<()>; - fn save_results_to_file_as_json_internal(&self, file_name: &str, item_to_serialize: &T, pretty_print: bool) -> std::io::Result<()> { + fn save_results_to_file_as_json_internal(&self, file_name: &str, item_to_serialize: &T, pretty_print: bool) -> std::io::Result<()> { if pretty_print { self.save_results_to_file_as_json_pretty(file_name, item_to_serialize) } else { @@ -44,7 +44,7 @@ pub trait PrintResults { } #[fun_time(message = "save_results_to_file_as_json_pretty", level = "debug")] - fn save_results_to_file_as_json_pretty(&self, file_name: &str, item_to_serialize: &T) -> std::io::Result<()> { + fn save_results_to_file_as_json_pretty(&self, file_name: &str, item_to_serialize: &T) -> std::io::Result<()> { let file_handler = File::create(file_name)?; let mut writer = BufWriter::new(file_handler); serde_json::to_writer_pretty(&mut writer, item_to_serialize)?; @@ -52,7 +52,7 @@ pub trait PrintResults { } #[fun_time(message = "save_results_to_file_as_json_compact", level = "debug")] - fn save_results_to_file_as_json_compact(&self, file_name: &str, item_to_serialize: &T) -> std::io::Result<()> { + fn save_results_to_file_as_json_compact(&self, file_name: &str, item_to_serialize: &T) -> std::io::Result<()> { let file_handler = File::create(file_name)?; let mut writer = BufWriter::new(file_handler); serde_json::to_writer(&mut writer, item_to_serialize)?; diff --git a/czkawka_core/src/duplicate.rs b/czkawka_core/src/duplicate.rs index ba38137..de47fcf 100644 --- a/czkawka_core/src/duplicate.rs +++ b/czkawka_core/src/duplicate.rs @@ -10,9 +10,8 @@ use std::path::Path; use std::sync::atomic::Ordering; use std::{fs, mem}; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use humansize::{format_size, BINARY}; use log::debug; use rayon::prelude::*; @@ -111,7 +110,7 @@ impl DuplicateFinder { } #[fun_time(message = "find_duplicates", level = "info")] - pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_duplicates(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); @@ -151,7 +150,7 @@ impl DuplicateFinder { } #[fun_time(message = "check_files_name", level = "debug")] - fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let group_by_func = if self.case_sensitive_name_comparison { |fe: &FileEntry| fe.path.file_name().unwrap().to_string_lossy().to_string() } else { @@ -170,6 +169,7 @@ impl DuplicateFinder { .recursive_search(self.common_data.recursive_search) .minimal_file_size(self.common_data.minimal_file_size) .maximal_file_size(self.common_data.maximal_file_size) + .tool_type(self.common_data.tool_type) .build() .run(); @@ -226,7 +226,7 @@ impl DuplicateFinder { } #[fun_time(message = "check_files_size_name", level = "debug")] - fn check_files_size_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files_size_name(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let group_by_func = if self.case_sensitive_name_comparison { |fe: &FileEntry| (fe.size, fe.path.file_name().unwrap().to_string_lossy().to_string()) } else { @@ -245,6 +245,7 @@ impl DuplicateFinder { .recursive_search(self.common_data.recursive_search) .minimal_file_size(self.common_data.minimal_file_size) .maximal_file_size(self.common_data.maximal_file_size) + .tool_type(self.common_data.tool_type) .build() .run(); @@ -303,7 +304,7 @@ impl DuplicateFinder { } #[fun_time(message = "check_files_size", level = "debug")] - fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files_size(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let max_stage = match self.check_method { CheckingMethod::Size => 0, CheckingMethod::Hash => MAX_STAGE, @@ -322,6 +323,7 @@ impl DuplicateFinder { .recursive_search(self.common_data.recursive_search) .minimal_file_size(self.common_data.minimal_file_size) .maximal_file_size(self.common_data.maximal_file_size) + .tool_type(self.common_data.tool_type) .build() .run(); @@ -491,7 +493,7 @@ impl DuplicateFinder { fn prehashing( &mut self, stop_receiver: Option<&Receiver<()>>, - progress_sender: Option<&UnboundedSender>, + progress_sender: Option<&Sender>, pre_checked_map: &mut BTreeMap>, ) -> Option<()> { let check_type = self.hash_type; @@ -679,12 +681,7 @@ impl DuplicateFinder { } #[fun_time(message = "full_hashing", level = "debug")] - fn full_hashing( - &mut self, - stop_receiver: Option<&Receiver<()>>, - progress_sender: Option<&UnboundedSender>, - pre_checked_map: BTreeMap>, - ) -> Option<()> { + fn full_hashing(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>, pre_checked_map: BTreeMap>) -> Option<()> { let (progress_thread_handle, progress_thread_run, _atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 4, MAX_STAGE, 0, self.check_method, self.common_data.tool_type); @@ -805,7 +802,7 @@ impl DuplicateFinder { } #[fun_time(message = "check_files_hash", level = "debug")] - fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files_hash(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { assert_eq!(self.check_method, CheckingMethod::Hash); let mut pre_checked_map: BTreeMap> = Default::default(); @@ -1163,11 +1160,14 @@ impl PrintResults for DuplicateFinder { Ok(()) } + // TODO - check if is possible to save also data in header about size and name in SizeName mode - https://github.com/qarmin/czkawka/issues/1137 fn save_results_to_file_as_json(&self, file_name: &str, pretty_print: bool) -> io::Result<()> { if self.get_use_reference() { match self.check_method { CheckingMethod::Name => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_names_referenced, pretty_print), - CheckingMethod::SizeName => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size_names_referenced, pretty_print), + CheckingMethod::SizeName => { + self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size_names_referenced.values().collect::>(), pretty_print) + } CheckingMethod::Size => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size_referenced, pretty_print), CheckingMethod::Hash => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_hashes_referenced, pretty_print), _ => panic!(), @@ -1175,7 +1175,7 @@ impl PrintResults for DuplicateFinder { } else { match self.check_method { CheckingMethod::Name => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_names, pretty_print), - CheckingMethod::SizeName => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size_names, pretty_print), + CheckingMethod::SizeName => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size_names.values().collect::>(), pretty_print), CheckingMethod::Size => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_size, pretty_print), CheckingMethod::Hash => self.save_results_to_file_as_json_internal(file_name, &self.files_with_identical_hashes, pretty_print), _ => panic!(), diff --git a/czkawka_core/src/empty_files.rs b/czkawka_core/src/empty_files.rs index a4acedf..372ec05 100644 --- a/czkawka_core/src/empty_files.rs +++ b/czkawka_core/src/empty_files.rs @@ -2,9 +2,8 @@ use std::fs; use std::io::prelude::*; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use log::debug; use crate::common_dir_traversal::{DirTraversalBuilder, DirTraversalResult, FileEntry, ProgressData, ToolType}; @@ -41,7 +40,7 @@ impl EmptyFiles { } #[fun_time(message = "find_empty_files", level = "info")] - pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_empty_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -52,7 +51,7 @@ impl EmptyFiles { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let result = DirTraversalBuilder::new() .root_dirs(self.common_data.directories.included_directories.clone()) .group_by(|_fe| ()) @@ -64,6 +63,7 @@ impl EmptyFiles { .allowed_extensions(self.common_data.allowed_extensions.clone()) .excluded_items(self.common_data.excluded_items.clone()) .recursive_search(self.common_data.recursive_search) + .tool_type(self.common_data.tool_type) .build() .run(); diff --git a/czkawka_core/src/empty_folder.rs b/czkawka_core/src/empty_folder.rs index ed5fa99..2d7a09f 100644 --- a/czkawka_core/src/empty_folder.rs +++ b/czkawka_core/src/empty_folder.rs @@ -3,9 +3,8 @@ use std::fs; use std::io::Write; use std::path::PathBuf; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use log::debug; use rayon::prelude::*; @@ -42,7 +41,7 @@ impl EmptyFolder { } #[fun_time(message = "find_empty_folders", level = "info")] - pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_for_empty_folders(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -74,7 +73,7 @@ impl EmptyFolder { } #[fun_time(message = "check_for_empty_folders", level = "debug")] - fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_for_empty_folders(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let result = DirTraversalBuilder::new() .root_dirs(self.common_data.directories.included_directories.clone()) .group_by(|_fe| ()) @@ -84,6 +83,7 @@ impl EmptyFolder { .excluded_items(self.common_data.excluded_items.clone()) .collect(Collect::EmptyFolders) .max_stage(0) + .tool_type(self.common_data.tool_type) .build() .run(); diff --git a/czkawka_core/src/invalid_symlinks.rs b/czkawka_core/src/invalid_symlinks.rs index fd57ae6..574af56 100644 --- a/czkawka_core/src/invalid_symlinks.rs +++ b/czkawka_core/src/invalid_symlinks.rs @@ -2,9 +2,8 @@ use std::fs; use std::io::prelude::*; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use log::debug; use crate::common_dir_traversal::{Collect, DirTraversalBuilder, DirTraversalResult, ErrorType, FileEntry, ProgressData, ToolType}; @@ -31,7 +30,7 @@ impl InvalidSymlinks { } #[fun_time(message = "find_invalid_links", level = "info")] - pub fn find_invalid_links(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_invalid_links(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -42,7 +41,7 @@ impl InvalidSymlinks { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let result = DirTraversalBuilder::new() .root_dirs(self.common_data.directories.included_directories.clone()) .group_by(|_fe| ()) @@ -53,6 +52,7 @@ impl InvalidSymlinks { .allowed_extensions(self.common_data.allowed_extensions.clone()) .excluded_items(self.common_data.excluded_items.clone()) .recursive_search(self.common_data.recursive_search) + .tool_type(self.common_data.tool_type) .build() .run(); diff --git a/czkawka_core/src/same_music.rs b/czkawka_core/src/same_music.rs index 84e339d..89e5fab 100644 --- a/czkawka_core/src/same_music.rs +++ b/czkawka_core/src/same_music.rs @@ -9,9 +9,9 @@ use std::sync::Arc; use std::{mem, panic}; use anyhow::Context; -use crossbeam_channel::Receiver; + +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use humansize::{format_size, BINARY}; use lofty::{read_from, AudioFile, ItemKey, TaggedFileExt}; use log::debug; @@ -137,7 +137,7 @@ impl SameMusic { } #[fun_time(message = "find_same_music", level = "info")] - pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_same_music(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); if !self.check_files(stop_receiver, progress_sender) { @@ -176,7 +176,7 @@ impl SameMusic { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { if !self.common_data.allowed_extensions.using_custom_extensions() { self.common_data.allowed_extensions.extend_allowed_extensions(AUDIO_FILES_EXTENSIONS); } else { @@ -203,6 +203,7 @@ impl SameMusic { .allowed_extensions(self.common_data.allowed_extensions.clone()) .excluded_items(self.common_data.excluded_items.clone()) .recursive_search(self.common_data.recursive_search) + .tool_type(self.common_data.tool_type) .max_stage(max_stage) .build() .run(); @@ -277,7 +278,7 @@ impl SameMusic { } #[fun_time(message = "calculate_fingerprint", level = "debug")] - fn calculate_fingerprint(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn calculate_fingerprint(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (progress_thread_handle, progress_thread_run, _atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 1, MAX_STAGE_CONTENT, 0, self.check_type, self.common_data.tool_type); @@ -341,7 +342,7 @@ impl SameMusic { } #[fun_time(message = "read_tags", level = "debug")] - fn read_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn read_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (progress_thread_handle, progress_thread_run, _atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 1, MAX_STAGE_TAGS, 0, self.check_type, self.common_data.tool_type); @@ -404,7 +405,7 @@ impl SameMusic { } #[fun_time(message = "check_for_duplicate_tags", level = "debug")] - fn check_for_duplicate_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_for_duplicate_tags(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 4, MAX_STAGE_TAGS, self.music_to_check.len(), self.check_type, self.common_data.tool_type); @@ -503,7 +504,7 @@ impl SameMusic { true } #[fun_time(message = "read_tags_to_files_similar_by_content", level = "debug")] - fn read_tags_to_files_similar_by_content(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn read_tags_to_files_similar_by_content(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let groups_to_check = max(self.duplicated_music_entries.len(), self.duplicated_music_entries_referenced.len()); let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) = prepare_thread_handler_common(progress_sender, 5, MAX_STAGE_CONTENT, groups_to_check, self.check_type, self.common_data.tool_type); @@ -629,7 +630,7 @@ impl SameMusic { } #[fun_time(message = "check_for_duplicate_fingerprints", level = "debug")] - fn check_for_duplicate_fingerprints(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_for_duplicate_fingerprints(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (base_files, files_to_compare) = self.split_fingerprints_to_check(); let (progress_thread_handle, progress_thread_run, atomic_counter, _check_was_stopped) = prepare_thread_handler_common(progress_sender, 2, 3, base_files.len(), self.check_type, self.common_data.tool_type); diff --git a/czkawka_core/src/similar_images.rs b/czkawka_core/src/similar_images.rs index 8055f9d..c3d63ed 100644 --- a/czkawka_core/src/similar_images.rs +++ b/czkawka_core/src/similar_images.rs @@ -7,9 +7,9 @@ use std::time::SystemTime; use std::{mem, panic}; use bk_tree::BKTree; -use crossbeam_channel::Receiver; + +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use humansize::{format_size, BINARY}; use image::GenericImageView; use image_hasher::{FilterType, HashAlg, HasherConfig}; @@ -94,7 +94,7 @@ pub struct SimilarImages { // Hashmap with image hashes and Vector with names of files similarity: u32, images_to_check: BTreeMap, - hash_size: u8, + pub hash_size: u8, // TODO to remove pub, this is needeed by new gui, because there is no way to check what exactly was seelected hash_alg: HashAlg, image_filter: FilterType, exclude_images_with_same_size: bool, @@ -125,7 +125,7 @@ impl SimilarImages { } #[fun_time(message = "find_similar_images", level = "info")] - pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); self.common_data.use_reference_folders = !self.common_data.directories.reference_directories.is_empty(); if !self.check_for_similar_images(stop_receiver, progress_sender) { @@ -145,7 +145,7 @@ impl SimilarImages { } #[fun_time(message = "check_for_similar_images", level = "debug")] - fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_for_similar_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector if !self.common_data.allowed_extensions.using_custom_extensions() { @@ -304,7 +304,7 @@ impl SimilarImages { // - Join all hashes and save it to file #[fun_time(message = "hash_images", level = "debug")] - fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn hash_images(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.hash_images_load_cache(); let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) = @@ -550,7 +550,7 @@ impl SimilarImages { &mut self, all_hashed_images: &HashMap>, collected_similar_images: &mut HashMap>, - progress_sender: Option<&UnboundedSender>, + progress_sender: Option<&Sender>, stop_receiver: Option<&Receiver<()>>, tolerance: u32, ) -> bool { @@ -684,7 +684,7 @@ impl SimilarImages { } #[fun_time(message = "find_similar_hashes", level = "debug")] - fn find_similar_hashes(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn find_similar_hashes(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { if self.image_hashes.is_empty() { return true; } @@ -1132,6 +1132,7 @@ mod tests { use std::collections::HashMap; use std::path::PathBuf; + use crate::common_dir_traversal::ToolType; use bk_tree::BKTree; use crate::common_directory::Directories; @@ -1152,6 +1153,10 @@ mod tests { for _ in 0..100 { let mut similar_images = SimilarImages { similarity: 0, + common_data: CommonToolData { + tool_type: ToolType::SimilarImages, + ..Default::default() + }, ..Default::default() }; @@ -1183,6 +1188,10 @@ mod tests { for _ in 0..100 { let mut similar_images = SimilarImages { similarity: 1, + common_data: CommonToolData { + tool_type: ToolType::SimilarImages, + ..Default::default() + }, ..Default::default() }; @@ -1203,6 +1212,7 @@ mod tests { similarity: 2, common_data: CommonToolData { use_reference_folders: false, + tool_type: ToolType::SimilarImages, ..Default::default() }, ..Default::default() @@ -1227,6 +1237,10 @@ mod tests { // for _ in 0..100 { // let mut similar_images = SimilarImages { // similarity: 10, + // common_data: CommonToolData { + // tool_type: ToolType::SimilarImages, + // ..Default::default() + // }, // use_reference_folders: false, // ..Default::default() // }; @@ -1250,6 +1264,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 0, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1276,6 +1291,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 0, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1303,6 +1319,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 0, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1334,6 +1351,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 1, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, use_reference_folders: false, ..Default::default() }, @@ -1358,6 +1376,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 4, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, use_reference_folders: false, ..Default::default() }, @@ -1391,6 +1410,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 1, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1421,6 +1441,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 1, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1448,6 +1469,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 1, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() @@ -1486,6 +1508,7 @@ mod tests { let mut similar_images = SimilarImages { similarity: 10, common_data: CommonToolData { + tool_type: ToolType::SimilarImages, directories: Directories { reference_directories: vec![PathBuf::from("/home/rr/")], ..Default::default() diff --git a/czkawka_core/src/similar_videos.rs b/czkawka_core/src/similar_videos.rs index d76bd2f..3d162ba 100644 --- a/czkawka_core/src/similar_videos.rs +++ b/czkawka_core/src/similar_videos.rs @@ -5,10 +5,9 @@ use std::mem; use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use ffmpeg_cmdline_utils::FfmpegErrorKind::FfmpegNotFound; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use humansize::{format_size, BINARY}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -103,7 +102,7 @@ impl SimilarVideos { } #[fun_time(message = "find_similar_videos", level = "info")] - pub fn find_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { if !check_if_ffmpeg_is_installed() { self.common_data.text_messages.errors.push(flc!("core_ffmpeg_not_found")); #[cfg(target_os = "windows")] @@ -130,7 +129,7 @@ impl SimilarVideos { } #[fun_time(message = "check_for_similar_videos", level = "debug")] - fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_for_similar_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector if !self.common_data.allowed_extensions.using_custom_extensions() { @@ -266,7 +265,7 @@ impl SimilarVideos { } #[fun_time(message = "sort_videos", level = "debug")] - fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn sort_videos(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let (loaded_hash_map, records_already_cached, non_cached_files_to_check) = self.load_cache_at_start(); let (progress_thread_handle, progress_thread_run, atomic_counter, check_was_stopped) = diff --git a/czkawka_core/src/temporary.rs b/czkawka_core/src/temporary.rs index 0785303..51d82d6 100644 --- a/czkawka_core/src/temporary.rs +++ b/czkawka_core/src/temporary.rs @@ -6,9 +6,8 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; use rayon::prelude::*; use serde::Serialize; @@ -33,7 +32,7 @@ const TEMP_EXTENSIONS: &[&str] = &[ ".partial", ]; -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Debug)] pub struct FileEntry { pub path: PathBuf, pub modified_date: u64, @@ -60,7 +59,7 @@ impl Temporary { } #[fun_time(message = "find_temporary_files", level = "info")] - pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) { + pub fn find_temporary_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) { self.optimize_dirs_before_start(); if !self.check_files(stop_receiver, progress_sender) { self.common_data.stopped_search = true; @@ -71,7 +70,7 @@ impl Temporary { } #[fun_time(message = "check_files", level = "debug")] - fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&UnboundedSender>) -> bool { + fn check_files(&mut self, stop_receiver: Option<&Receiver<()>>, progress_sender: Option<&Sender>) -> bool { let mut folders_to_check: Vec = Vec::with_capacity(1024 * 2); // This should be small enough too not see to big difference and big enough to store most of paths without needing to resize vector // Add root folders for finding @@ -153,9 +152,7 @@ impl Temporary { ) -> Option { atomic_counter.fetch_add(1, Ordering::Relaxed); - let Some(file_name_lowercase) = get_lowercase_name(entry_data, warnings) else { - return None; - }; + let file_name_lowercase = get_lowercase_name(entry_data, warnings)?; if !TEMP_EXTENSIONS.iter().any(|f| file_name_lowercase.ends_with(f)) { return None; diff --git a/czkawka_gui/Cargo.toml b/czkawka_gui/Cargo.toml index 431bdb9..743d050 100644 --- a/czkawka_gui/Cargo.toml +++ b/czkawka_gui/Cargo.toml @@ -3,7 +3,7 @@ name = "czkawka_gui" version = "6.1.0" authors = ["Rafał Mikrut "] edition = "2021" -rust-version = "1.70.0" +rust-version = "1.72.1" description = "GTK frontend of Czkawka" license = "MIT" homepage = "https://github.com/qarmin/czkawka" @@ -19,9 +19,6 @@ chrono = "0.4.31" # Used for sending stop signal across threads crossbeam-channel = "0.5.8" -# To get information about progress -futures = "0.3.28" - # For saving/loading config files to specific directories directories-next = "2.0" @@ -46,7 +43,7 @@ fs_extra = "1.3" # Language i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" -rust-embed = "8.0" +rust-embed = { version = "8.0", features = ["debug-embed"] } once_cell = "1.18" log = "0.4.20" @@ -62,3 +59,4 @@ winapi = { version = "0.3.9", features = ["combaseapi", "objbase", "shobjidl_cor [features] default = [] heif = ["czkawka_core/heif"] +libraw = ["czkawka_core/libraw"] diff --git a/LICENSE b/czkawka_gui/LICENSE similarity index 100% rename from LICENSE rename to czkawka_gui/LICENSE diff --git a/czkawka_gui/README.md b/czkawka_gui/README.md new file mode 100644 index 0000000..07739fa --- /dev/null +++ b/czkawka_gui/README.md @@ -0,0 +1,91 @@ +# Czkawka GUI +Czkawka GUI is a graphical user interface for Czkawka Core written with GTK 4. + +![Screenshot from 2023-11-26 12-43-32](https://github.com/qarmin/czkawka/assets/41945903/722ed490-0be1-4dac-bcfc-182a4d0787dc) + +## Requirements +Requirements depends on platform that you are using. + +Prebuild binareies are available here - https://github.com/qarmin/czkawka/releases/ + +### Linux +#### Prebuild binaries + Ubuntu - `sudo apt install libgtk-4 libheif libraw ffmpeg -y` +#### Snap - + none - all needed libraries are bundled in snap [except ffmpeg](https://github.com/snapcrafters/ffmpeg/issues/73) - https://snapcraft.io/czkawka +#### Flatpak + none - all needed libraries are bundled - https://flathub.org/apps/com.github.qarmin.czkawka +### Mac +``` +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +brew install gtk4 adwaita-icon-theme ffmpeg librsvg libheif libraw +``` + +### Windows +All needed libraries should be bundled in zip(except ffmpeg which you need download and unpack to location with `czkawka_gui.exe` - https://ffmpeg.org/download.html#build-windows) + +You can also install app via msys2(webp and heif should work here) - https://www.msys2.org/#installation (czkawka package - https://packages.msys2.org/base/mingw-w64-czkawka) +``` +pacman -S mingw-w64-x86_64-czkawka-gui +``` +and you can create shortcut to `C:\msys64\mingw64\bin\czkawka_gui.exe` + +## Compilation +Compilation of gui is harder that compilation cli or core, because uses gtk4 which is written in C and also requires a lot build and runtime dependencies. + +### Requirements +| Program | Minimal version | +|:---------:|:-----------------:| +| Rust | 1.72.1 | +| GTK | 4.6 | + +### Linux (Ubuntu, but on other OS should work similar) +```shell +sudo apt install libgtk-4-dev libheif-dev libraw-dev -y +cargo run --release --bin czkawka_gui +# Or with support for heif and libraw +cargo run --release --bin czkawka_gui --features "heif,libraw" +``` +### Mac +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +brew install rustup gtk4 adwaita-icon-theme ffmpeg librsvg libheif libraw pkg-config +rustup-init +cargo run --release --bin czkawka_gui +# Or with support for heif and libraw +cargo run --release --bin czkawka_gui --features "heif,libraw" +``` +### Windows +Currently, there is no instruction how to compile app on Windows natively.
+You can check for CI for instructions how to cross-compile app from linux to windows(uses prebuilt docker image) - [CI Instructions](../.github/workflows/windows.yml)
+There exists mingw recipe which you can try to convert for your purposes - https://github.com/msys2/MINGW-packages/blob/master/mingw-w64-czkawka/PKGBUILD + +## Limitations +Not all available features other components implemented here, so this is list of limitations: +- Snap versions not allows to use similar videos feature +- Windows version not supports heif and webp files with prebuild binaries +- Prebuild binaries for mac arm not exists + +## License +Code is distributed under MIT license. + +Icon was created by [jannuary](https://github.com/jannuary) and licensed CC-BY-4.0. + +Windows dark theme is used from project [WhiteSur](https://github.com/slypy/whitesur-gtk4-theme) with MIT license. + +Some icons were taken from [ReShot](https://www.reshot.com) site and are licensed under Reshot Free License. + +The program is completely free to use. + +"Gratis to uczciwa cena" - "Free is a fair price" + +## Name +Czkawka is a Polish word which means _hiccup_. + +I chose this name because I wanted to hear people speaking other languages pronounce it, so feel free to spell it the way you want. + +This name is not as bad as it seems, because I was also thinking about using words like _żółć_, _gżegżółka_ or _żołądź_, +but I gave up on these ideas because they contained Polish characters, which would cause difficulty in searching for the project. + +At the beginning of the program creation, if the response concerning the name was unanimously negative, I prepared myself +for a possible change of the name of the program, and the opinions were extremely mixed. diff --git a/czkawka_gui/i18n/en/czkawka_gui.ftl b/czkawka_gui/i18n/en/czkawka_gui.ftl index 70d7780..68f1182 100644 --- a/czkawka_gui/i18n/en/czkawka_gui.ftl +++ b/czkawka_gui/i18n/en/czkawka_gui.ftl @@ -544,7 +544,7 @@ move_files_title_dialog = Choose folder to which you want to move duplicated fil move_files_choose_more_than_1_path = Only one path may be selected to be able to copy their duplicated files, selected {$path_number}. move_stats = Properly moved {$num_files}/{$all_files} items -save_results_to_file = Saved results both to txt and json files. +save_results_to_file = Saved results both to txt and json files into {$name} folder. search_not_choosing_any_music = ERROR: You must select at least one checkbox with music searching types. search_not_choosing_any_broken_files = ERROR: You must select at least one checkbox with type of checked broken files. diff --git a/czkawka_gui/src/connect_things/connect_button_save.rs b/czkawka_gui/src/connect_things/connect_button_save.rs index e407e08..c06e5c4 100644 --- a/czkawka_gui/src/connect_things/connect_button_save.rs +++ b/czkawka_gui/src/connect_things/connect_button_save.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::HashMap; +use std::env; use std::rc::Rc; use gtk4::prelude::*; @@ -10,6 +11,7 @@ use czkawka_core::common_traits::PrintResults; use crate::flg; use crate::gui_structs::gui_data::GuiData; use crate::help_functions::BottomButtonsEnum; +use crate::localizer_core::generate_translation_hashmap; use crate::notebook_enums::*; pub fn connect_button_save(gui_data: &GuiData) { @@ -44,10 +46,15 @@ pub fn connect_button_save(gui_data: &GuiData) { NotebookMainEnum::BadExtensions => shared_bad_extensions_state.borrow().save_all_in_one("results_bad_extensions"), }; + let current_path = match env::current_dir() { + Ok(t) => t.to_string_lossy().to_string(), + Err(_) => "".to_string(), + }; + match result { Ok(()) => (), Err(e) => { - entry_info.set_text(&format!("Failed to save results to file {e}")); + entry_info.set_text(&format!("Failed to save results to folder {current_path}, reason {e}")); return; } } @@ -57,6 +64,7 @@ pub fn connect_button_save(gui_data: &GuiData) { &shared_buttons, &entry_info, &buttons_save_clone, + current_path, ); }); } @@ -66,8 +74,9 @@ fn post_save_things( shared_buttons: &Rc>>>, entry_info: &Entry, buttons_save: &Button, + current_path: String, ) { - entry_info.set_text(&flg!("save_results_to_file")); + entry_info.set_text(&flg!("save_results_to_file", generate_translation_hashmap(vec![("name", current_path),]))); // Set state { buttons_save.hide(); diff --git a/czkawka_gui/src/connect_things/connect_button_search.rs b/czkawka_gui/src/connect_things/connect_button_search.rs index 4468eb6..603f43c 100644 --- a/czkawka_gui/src/connect_things/connect_button_search.rs +++ b/czkawka_gui/src/connect_things/connect_button_search.rs @@ -3,16 +3,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; -use crossbeam_channel::Receiver; +use crossbeam_channel::{Receiver, Sender}; use fun_time::fun_time; -use futures::channel::mpsc::UnboundedSender; -use glib::Sender; +use glib::Sender as glibSender; use gtk4::prelude::*; use gtk4::Grid; use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::big_file::BigFile; use czkawka_core::broken_files::{BrokenFiles, CheckedTypes}; +use czkawka_core::common::DEFAULT_THREAD_SIZE; use czkawka_core::common_dir_traversal::{CheckingMethod, ProgressData}; use czkawka_core::common_tool::CommonData; use czkawka_core::duplicate::DuplicateFinder; @@ -35,7 +35,7 @@ use crate::taskbar_progress::tbp_flags::TBPF_NOPROGRESS; use crate::{flg, DEFAULT_MAXIMAL_FILE_SIZE, DEFAULT_MINIMAL_CACHE_SIZE, DEFAULT_MINIMAL_FILE_SIZE}; #[allow(clippy::too_many_arguments)] -pub fn connect_button_search(gui_data: &GuiData, glib_stop_sender: Sender, progress_sender: UnboundedSender) { +pub fn connect_button_search(gui_data: &GuiData, glib_stop_sender: glibSender, progress_sender: Sender) { let buttons_array = gui_data.bottom_buttons.buttons_array.clone(); let buttons_search_clone = gui_data.bottom_buttons.buttons_search.clone(); let grid_progress_stages = gui_data.progress_window.grid_progress_stages.clone(); @@ -284,9 +284,9 @@ fn duplicate_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.show(); @@ -316,90 +316,99 @@ fn duplicate_search( let delete_outdated_cache = check_button_settings_duplicates_delete_outdated_cache.is_active(); // Find duplicates - thread::spawn(move || { - let mut df = DuplicateFinder::new(); - df.set_included_directory(loaded_common_items.included_directories); - df.set_excluded_directory(loaded_common_items.excluded_directories); - df.set_reference_directory(loaded_common_items.reference_directories); - df.set_recursive_search(loaded_common_items.recursive_search); - df.set_excluded_items(loaded_common_items.excluded_items); - df.set_allowed_extensions(loaded_common_items.allowed_extensions); - df.set_minimal_file_size(loaded_common_items.minimal_file_size); - df.set_maximal_file_size(loaded_common_items.maximal_file_size); - df.set_minimal_cache_file_size(loaded_common_items.minimal_cache_file_size); - df.set_minimal_prehash_cache_file_size(minimal_prehash_cache_file_size); - df.set_check_method(check_method); - df.set_hash_type(hash_type); - df.set_save_also_as_json(loaded_common_items.save_also_as_json); - df.set_ignore_hard_links(loaded_common_items.hide_hard_links); - df.set_use_cache(loaded_common_items.use_cache); - df.set_use_prehash_cache(use_prehash_cache); - df.set_delete_outdated_cache(delete_outdated_cache); - df.set_case_sensitive_name_comparison(case_sensitive_name_comparison); - df.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - df.find_duplicates(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::Duplicates(df)).unwrap(); - }); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut df = DuplicateFinder::new(); + df.set_included_directory(loaded_common_items.included_directories); + df.set_excluded_directory(loaded_common_items.excluded_directories); + df.set_reference_directory(loaded_common_items.reference_directories); + df.set_recursive_search(loaded_common_items.recursive_search); + df.set_excluded_items(loaded_common_items.excluded_items); + df.set_allowed_extensions(loaded_common_items.allowed_extensions); + df.set_minimal_file_size(loaded_common_items.minimal_file_size); + df.set_maximal_file_size(loaded_common_items.maximal_file_size); + df.set_minimal_cache_file_size(loaded_common_items.minimal_cache_file_size); + df.set_minimal_prehash_cache_file_size(minimal_prehash_cache_file_size); + df.set_check_method(check_method); + df.set_hash_type(hash_type); + df.set_save_also_as_json(loaded_common_items.save_also_as_json); + df.set_ignore_hard_links(loaded_common_items.hide_hard_links); + df.set_use_cache(loaded_common_items.use_cache); + df.set_use_prehash_cache(use_prehash_cache); + df.set_delete_outdated_cache(delete_outdated_cache); + df.set_case_sensitive_name_comparison(case_sensitive_name_comparison); + df.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + df.find_duplicates(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::Duplicates(df)).unwrap(); + }) + .unwrap(); } fn empty_files_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.hide(); let tree_view_empty_files_finder = gui_data.main_notebook.tree_view_empty_files_finder.clone(); clean_tree_view(&tree_view_empty_files_finder); // Find empty files - thread::spawn(move || { - let mut vf = EmptyFiles::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut vf = EmptyFiles::new(); - vf.set_included_directory(loaded_common_items.included_directories); - vf.set_excluded_directory(loaded_common_items.excluded_directories); - vf.set_recursive_search(loaded_common_items.recursive_search); - vf.set_excluded_items(loaded_common_items.excluded_items); - vf.set_allowed_extensions(loaded_common_items.allowed_extensions); - vf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - vf.find_empty_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::EmptyFiles(vf)).unwrap(); - }); + vf.set_included_directory(loaded_common_items.included_directories); + vf.set_excluded_directory(loaded_common_items.excluded_directories); + vf.set_recursive_search(loaded_common_items.recursive_search); + vf.set_excluded_items(loaded_common_items.excluded_items); + vf.set_allowed_extensions(loaded_common_items.allowed_extensions); + vf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + vf.find_empty_files(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::EmptyFiles(vf)).unwrap(); + }) + .unwrap(); } fn empty_directories_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.hide(); let tree_view_empty_folder_finder = gui_data.main_notebook.tree_view_empty_folder_finder.clone(); clean_tree_view(&tree_view_empty_folder_finder); - thread::spawn(move || { - let mut ef = EmptyFolder::new(); - ef.set_included_directory(loaded_common_items.included_directories); - ef.set_excluded_directory(loaded_common_items.excluded_directories); - ef.set_excluded_items(loaded_common_items.excluded_items); - ef.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - ef.find_empty_folders(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::EmptyFolders(ef)).unwrap(); - }); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut ef = EmptyFolder::new(); + ef.set_included_directory(loaded_common_items.included_directories); + ef.set_excluded_directory(loaded_common_items.excluded_directories); + ef.set_excluded_items(loaded_common_items.excluded_items); + ef.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + ef.find_empty_folders(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::EmptyFolders(ef)).unwrap(); + }) + .unwrap(); } fn big_files_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.hide(); @@ -413,55 +422,61 @@ fn big_files_search( let numbers_of_files_to_check = entry_big_files_number.text().as_str().parse::().unwrap_or(50); - thread::spawn(move || { - let mut bf = BigFile::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut bf = BigFile::new(); - bf.set_included_directory(loaded_common_items.included_directories); - bf.set_excluded_directory(loaded_common_items.excluded_directories); - bf.set_recursive_search(loaded_common_items.recursive_search); - bf.set_excluded_items(loaded_common_items.excluded_items); - bf.set_allowed_extensions(loaded_common_items.allowed_extensions); - bf.set_number_of_files_to_check(numbers_of_files_to_check); - bf.set_search_mode(big_files_mode); - bf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - bf.find_big_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BigFiles(bf)).unwrap(); - }); + bf.set_included_directory(loaded_common_items.included_directories); + bf.set_excluded_directory(loaded_common_items.excluded_directories); + bf.set_recursive_search(loaded_common_items.recursive_search); + bf.set_excluded_items(loaded_common_items.excluded_items); + bf.set_allowed_extensions(loaded_common_items.allowed_extensions); + bf.set_number_of_files_to_check(numbers_of_files_to_check); + bf.set_search_mode(big_files_mode); + bf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + bf.find_big_files(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::BigFiles(bf)).unwrap(); + }) + .unwrap(); } fn temporary_files_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.hide(); let tree_view_temporary_files_finder = gui_data.main_notebook.tree_view_temporary_files_finder.clone(); clean_tree_view(&tree_view_temporary_files_finder); - thread::spawn(move || { - let mut tf = Temporary::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut tf = Temporary::new(); - tf.set_included_directory(loaded_common_items.included_directories); - tf.set_excluded_directory(loaded_common_items.excluded_directories); - tf.set_recursive_search(loaded_common_items.recursive_search); - tf.set_excluded_items(loaded_common_items.excluded_items); - tf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - tf.find_temporary_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::Temporary(tf)).unwrap(); - }); + tf.set_included_directory(loaded_common_items.included_directories); + tf.set_excluded_directory(loaded_common_items.excluded_directories); + tf.set_recursive_search(loaded_common_items.recursive_search); + tf.set_excluded_items(loaded_common_items.excluded_items); + tf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + tf.find_temporary_files(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::Temporary(tf)).unwrap(); + }) + .unwrap(); } fn same_music_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, show_dialog: &Arc, ) { grid_progress_stages.show(); @@ -510,28 +525,31 @@ fn same_music_search( let minimum_segment_duration = scale_seconds_same_music.value() as f32; if music_similarity != MusicSimilarity::NONE || check_method == CheckingMethod::AudioContent { - thread::spawn(move || { - let mut mf = SameMusic::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut mf = SameMusic::new(); - mf.set_included_directory(loaded_common_items.included_directories); - mf.set_excluded_directory(loaded_common_items.excluded_directories); - mf.set_reference_directory(loaded_common_items.reference_directories); - mf.set_excluded_items(loaded_common_items.excluded_items); - mf.set_use_cache(loaded_common_items.use_cache); - mf.set_minimal_file_size(loaded_common_items.minimal_file_size); - mf.set_maximal_file_size(loaded_common_items.maximal_file_size); - mf.set_allowed_extensions(loaded_common_items.allowed_extensions); - mf.set_recursive_search(loaded_common_items.recursive_search); - mf.set_music_similarity(music_similarity); - mf.set_maximum_difference(maximum_difference); - mf.set_minimum_segment_duration(minimum_segment_duration); - mf.set_check_type(check_method); - mf.set_approximate_comparison(approximate_comparison); - mf.set_save_also_as_json(loaded_common_items.save_also_as_json); - mf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - mf.find_same_music(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SameMusic(mf)).unwrap(); - }); + mf.set_included_directory(loaded_common_items.included_directories); + mf.set_excluded_directory(loaded_common_items.excluded_directories); + mf.set_reference_directory(loaded_common_items.reference_directories); + mf.set_excluded_items(loaded_common_items.excluded_items); + mf.set_use_cache(loaded_common_items.use_cache); + mf.set_minimal_file_size(loaded_common_items.minimal_file_size); + mf.set_maximal_file_size(loaded_common_items.maximal_file_size); + mf.set_allowed_extensions(loaded_common_items.allowed_extensions); + mf.set_recursive_search(loaded_common_items.recursive_search); + mf.set_music_similarity(music_similarity); + mf.set_maximum_difference(maximum_difference); + mf.set_minimum_segment_duration(minimum_segment_duration); + mf.set_check_type(check_method); + mf.set_approximate_comparison(approximate_comparison); + mf.set_save_also_as_json(loaded_common_items.save_also_as_json); + mf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + mf.find_same_music(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::SameMusic(mf)).unwrap(); + }) + .unwrap(); } else { let shared_buttons = gui_data.shared_buttons.clone(); let buttons_array = gui_data.bottom_buttons.buttons_array.clone(); @@ -561,9 +579,9 @@ fn broken_files_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, show_dialog: &Arc, ) { grid_progress_stages.show(); @@ -592,21 +610,24 @@ fn broken_files_search( } if checked_types != CheckedTypes::NONE { - thread::spawn(move || { - let mut br = BrokenFiles::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut br = BrokenFiles::new(); - br.set_included_directory(loaded_common_items.included_directories); - br.set_excluded_directory(loaded_common_items.excluded_directories); - br.set_recursive_search(loaded_common_items.recursive_search); - br.set_excluded_items(loaded_common_items.excluded_items); - br.set_use_cache(loaded_common_items.use_cache); - br.set_allowed_extensions(loaded_common_items.allowed_extensions); - br.set_save_also_as_json(loaded_common_items.save_also_as_json); - br.set_checked_types(checked_types); - br.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - br.find_broken_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BrokenFiles(br)).unwrap(); - }); + br.set_included_directory(loaded_common_items.included_directories); + br.set_excluded_directory(loaded_common_items.excluded_directories); + br.set_recursive_search(loaded_common_items.recursive_search); + br.set_excluded_items(loaded_common_items.excluded_items); + br.set_use_cache(loaded_common_items.use_cache); + br.set_allowed_extensions(loaded_common_items.allowed_extensions); + br.set_save_also_as_json(loaded_common_items.save_also_as_json); + br.set_checked_types(checked_types); + br.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + br.find_broken_files(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::BrokenFiles(br)).unwrap(); + }) + .unwrap(); } else { let shared_buttons = gui_data.shared_buttons.clone(); let buttons_array = gui_data.bottom_buttons.buttons_array.clone(); @@ -636,9 +657,9 @@ fn similar_image_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.show(); @@ -669,38 +690,41 @@ fn similar_image_search( let delete_outdated_cache = check_button_settings_similar_images_delete_outdated_cache.is_active(); - thread::spawn(move || { - let mut sf = SimilarImages::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut sf = SimilarImages::new(); - sf.set_included_directory(loaded_common_items.included_directories); - sf.set_excluded_directory(loaded_common_items.excluded_directories); - sf.set_reference_directory(loaded_common_items.reference_directories); - sf.set_recursive_search(loaded_common_items.recursive_search); - sf.set_excluded_items(loaded_common_items.excluded_items); - sf.set_minimal_file_size(loaded_common_items.minimal_file_size); - sf.set_maximal_file_size(loaded_common_items.maximal_file_size); - sf.set_similarity(similarity); - sf.set_use_cache(loaded_common_items.use_cache); - sf.set_hash_alg(hash_alg); - sf.set_hash_size(hash_size); - sf.set_image_filter(image_filter); - sf.set_allowed_extensions(loaded_common_items.allowed_extensions); - sf.set_delete_outdated_cache(delete_outdated_cache); - sf.set_exclude_images_with_same_size(ignore_same_size); - sf.set_save_also_as_json(loaded_common_items.save_also_as_json); - sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - sf.find_similar_images(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SimilarImages(sf)).unwrap(); - }); + sf.set_included_directory(loaded_common_items.included_directories); + sf.set_excluded_directory(loaded_common_items.excluded_directories); + sf.set_reference_directory(loaded_common_items.reference_directories); + sf.set_recursive_search(loaded_common_items.recursive_search); + sf.set_excluded_items(loaded_common_items.excluded_items); + sf.set_minimal_file_size(loaded_common_items.minimal_file_size); + sf.set_maximal_file_size(loaded_common_items.maximal_file_size); + sf.set_similarity(similarity); + sf.set_use_cache(loaded_common_items.use_cache); + sf.set_hash_alg(hash_alg); + sf.set_hash_size(hash_size); + sf.set_image_filter(image_filter); + sf.set_allowed_extensions(loaded_common_items.allowed_extensions); + sf.set_delete_outdated_cache(delete_outdated_cache); + sf.set_exclude_images_with_same_size(ignore_same_size); + sf.set_save_also_as_json(loaded_common_items.save_also_as_json); + sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + sf.find_similar_images(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::SimilarImages(sf)).unwrap(); + }) + .unwrap(); } fn similar_video_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.show(); @@ -716,82 +740,91 @@ fn similar_video_search( let ignore_same_size = check_button_video_ignore_same_size.is_active(); - thread::spawn(move || { - let mut sf = SimilarVideos::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut sf = SimilarVideos::new(); - sf.set_included_directory(loaded_common_items.included_directories); - sf.set_excluded_directory(loaded_common_items.excluded_directories); - sf.set_reference_directory(loaded_common_items.reference_directories); - sf.set_recursive_search(loaded_common_items.recursive_search); - sf.set_excluded_items(loaded_common_items.excluded_items); - sf.set_minimal_file_size(loaded_common_items.minimal_file_size); - sf.set_maximal_file_size(loaded_common_items.maximal_file_size); - sf.set_allowed_extensions(loaded_common_items.allowed_extensions); - sf.set_use_cache(loaded_common_items.use_cache); - sf.set_tolerance(tolerance); - sf.set_delete_outdated_cache(delete_outdated_cache); - sf.set_exclude_videos_with_same_size(ignore_same_size); - sf.set_save_also_as_json(loaded_common_items.save_also_as_json); - sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - sf.find_similar_videos(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::SimilarVideos(sf)).unwrap(); - }); + sf.set_included_directory(loaded_common_items.included_directories); + sf.set_excluded_directory(loaded_common_items.excluded_directories); + sf.set_reference_directory(loaded_common_items.reference_directories); + sf.set_recursive_search(loaded_common_items.recursive_search); + sf.set_excluded_items(loaded_common_items.excluded_items); + sf.set_minimal_file_size(loaded_common_items.minimal_file_size); + sf.set_maximal_file_size(loaded_common_items.maximal_file_size); + sf.set_allowed_extensions(loaded_common_items.allowed_extensions); + sf.set_use_cache(loaded_common_items.use_cache); + sf.set_tolerance(tolerance); + sf.set_delete_outdated_cache(delete_outdated_cache); + sf.set_exclude_videos_with_same_size(ignore_same_size); + sf.set_save_also_as_json(loaded_common_items.save_also_as_json); + sf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + sf.find_similar_videos(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::SimilarVideos(sf)).unwrap(); + }) + .unwrap(); } fn bad_symlinks_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.hide(); let tree_view_invalid_symlinks = gui_data.main_notebook.tree_view_invalid_symlinks.clone(); clean_tree_view(&tree_view_invalid_symlinks); - thread::spawn(move || { - let mut isf = InvalidSymlinks::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut isf = InvalidSymlinks::new(); - isf.set_included_directory(loaded_common_items.included_directories); - isf.set_excluded_directory(loaded_common_items.excluded_directories); - isf.set_recursive_search(loaded_common_items.recursive_search); - isf.set_excluded_items(loaded_common_items.excluded_items); - isf.set_allowed_extensions(loaded_common_items.allowed_extensions); - isf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - isf.find_invalid_links(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::InvalidSymlinks(isf)).unwrap(); - }); + isf.set_included_directory(loaded_common_items.included_directories); + isf.set_excluded_directory(loaded_common_items.excluded_directories); + isf.set_recursive_search(loaded_common_items.recursive_search); + isf.set_excluded_items(loaded_common_items.excluded_items); + isf.set_allowed_extensions(loaded_common_items.allowed_extensions); + isf.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + isf.find_invalid_links(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::InvalidSymlinks(isf)).unwrap(); + }) + .unwrap(); } fn bad_extensions_search( gui_data: &GuiData, loaded_common_items: LoadedCommonItems, stop_receiver: Receiver<()>, - glib_stop_sender: Sender, + glib_stop_sender: glibSender, grid_progress_stages: &Grid, - progress_data_sender: UnboundedSender, + progress_data_sender: Sender, ) { grid_progress_stages.show(); let tree_view_bad_extensions = gui_data.main_notebook.tree_view_bad_extensions.clone(); clean_tree_view(&tree_view_bad_extensions); - thread::spawn(move || { - let mut be = BadExtensions::new(); + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut be = BadExtensions::new(); - be.set_included_directory(loaded_common_items.included_directories); - be.set_excluded_directory(loaded_common_items.excluded_directories); - be.set_excluded_items(loaded_common_items.excluded_items); - be.set_minimal_file_size(loaded_common_items.minimal_file_size); - be.set_maximal_file_size(loaded_common_items.maximal_file_size); - be.set_allowed_extensions(loaded_common_items.allowed_extensions); - be.set_recursive_search(loaded_common_items.recursive_search); - be.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); - be.find_bad_extensions_files(Some(&stop_receiver), Some(&progress_data_sender)); - glib_stop_sender.send(Message::BadExtensions(be)).unwrap(); - }); + be.set_included_directory(loaded_common_items.included_directories); + be.set_excluded_directory(loaded_common_items.excluded_directories); + be.set_excluded_items(loaded_common_items.excluded_items); + be.set_minimal_file_size(loaded_common_items.minimal_file_size); + be.set_maximal_file_size(loaded_common_items.maximal_file_size); + be.set_allowed_extensions(loaded_common_items.allowed_extensions); + be.set_recursive_search(loaded_common_items.recursive_search); + be.set_exclude_other_filesystems(loaded_common_items.ignore_other_filesystems); + be.find_bad_extensions_files(Some(&stop_receiver), Some(&progress_data_sender)); + glib_stop_sender.send(Message::BadExtensions(be)).unwrap(); + }) + .unwrap(); } #[fun_time(message = "clean_tree_view", level = "debug")] diff --git a/czkawka_gui/src/connect_things/connect_change_language.rs b/czkawka_gui/src/connect_things/connect_change_language.rs index 425aa24..f52817e 100644 --- a/czkawka_gui/src/connect_things/connect_change_language.rs +++ b/czkawka_gui/src/connect_things/connect_change_language.rs @@ -37,7 +37,7 @@ fn change_language(gui_data: &GuiData) { pub fn load_system_language(gui_data: &GuiData) { let requested_languages = DesktopLanguageRequester::requested_languages(); - if let Some(language) = requested_languages.get(0) { + if let Some(language) = requested_languages.first() { let old_short_lang = language.to_string(); let mut short_lang = String::new(); // removes from e.g. en_zb, ending _zd since Czkawka don't support this(maybe could add this in future, but only when) diff --git a/czkawka_gui/src/connect_things/connect_progress_window.rs b/czkawka_gui/src/connect_things/connect_progress_window.rs index 955ad6b..facf5f4 100644 --- a/czkawka_gui/src/connect_things/connect_progress_window.rs +++ b/czkawka_gui/src/connect_things/connect_progress_window.rs @@ -1,9 +1,10 @@ +use crossbeam_channel::Receiver; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use futures::channel::mpsc::UnboundedReceiver; -use futures::StreamExt; +use std::time::Duration; + use glib::MainContext; use gtk4::prelude::*; use gtk4::ProgressBar; @@ -19,29 +20,39 @@ use crate::taskbar_progress::tbp_flags::TBPF_INDETERMINATE; use crate::taskbar_progress::TaskbarProgress; #[allow(clippy::too_many_arguments)] -pub fn connect_progress_window(gui_data: &GuiData, mut progress_receiver: UnboundedReceiver) { +pub fn connect_progress_window(gui_data: &GuiData, progress_receiver: Receiver) { let main_context = MainContext::default(); let _guard = main_context.acquire().unwrap(); let gui_data = gui_data.clone(); + let future = async move { - while let Some(item) = progress_receiver.next().await { - match item.tool_type { - ToolType::Duplicate => process_bar_duplicates(&gui_data, &item), - ToolType::EmptyFiles => process_bar_empty_files(&gui_data, &item), - ToolType::EmptyFolders => process_bar_empty_folder(&gui_data, &item), - ToolType::BigFile => process_bar_big_files(&gui_data, &item), - ToolType::SameMusic => process_bar_same_music(&gui_data, &item), - ToolType::SimilarImages => process_bar_similar_images(&gui_data, &item), - ToolType::SimilarVideos => process_bar_similar_videos(&gui_data, &item), - ToolType::TemporaryFiles => process_bar_temporary(&gui_data, &item), - ToolType::InvalidSymlinks => process_bar_invalid_symlinks(&gui_data, &item), - ToolType::BrokenFiles => process_bar_broken_files(&gui_data, &item), - ToolType::BadExtensions => process_bar_bad_extensions(&gui_data, &item), - ToolType::None => panic!(), + loop { + loop { + let item = progress_receiver.try_recv(); + if let Ok(item) = item { + match item.tool_type { + ToolType::Duplicate => process_bar_duplicates(&gui_data, &item), + ToolType::EmptyFiles => process_bar_empty_files(&gui_data, &item), + ToolType::EmptyFolders => process_bar_empty_folder(&gui_data, &item), + ToolType::BigFile => process_bar_big_files(&gui_data, &item), + ToolType::SameMusic => process_bar_same_music(&gui_data, &item), + ToolType::SimilarImages => process_bar_similar_images(&gui_data, &item), + ToolType::SimilarVideos => process_bar_similar_videos(&gui_data, &item), + ToolType::TemporaryFiles => process_bar_temporary(&gui_data, &item), + ToolType::InvalidSymlinks => process_bar_invalid_symlinks(&gui_data, &item), + ToolType::BrokenFiles => process_bar_broken_files(&gui_data, &item), + ToolType::BadExtensions => process_bar_bad_extensions(&gui_data, &item), + ToolType::None => panic!(), + } + } else { + break; + } } + glib::timeout_future(Duration::from_millis(300)).await; } }; + main_context.spawn_local(future); } diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index 37388df..649b6f7 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -14,6 +14,7 @@ use once_cell::sync::OnceCell; use czkawka_core::bad_extensions::BadExtensions; use czkawka_core::big_file::BigFile; use czkawka_core::broken_files::BrokenFiles; +use czkawka_core::common::CHARACTER; use czkawka_core::common_dir_traversal; use czkawka_core::common_messages::Messages; use czkawka_core::duplicate::DuplicateFinder; @@ -29,11 +30,6 @@ use crate::flg; use crate::notebook_enums::{NotebookMainEnum, NotebookUpperEnum}; use crate::notebook_info::{NotebookObject, NOTEBOOKS_INFO}; -#[cfg(not(target_family = "windows"))] -pub const CHARACTER: char = '/'; -#[cfg(target_family = "windows")] -pub const CHARACTER: char = '\\'; - pub const KEY_DELETE: u32 = 119; pub const KEY_ENTER: u32 = 36; pub const KEY_SPACE: u32 = 65; diff --git a/czkawka_gui/src/main.rs b/czkawka_gui/src/main.rs index 0707d96..e685317 100644 --- a/czkawka_gui/src/main.rs +++ b/czkawka_gui/src/main.rs @@ -5,11 +5,10 @@ #![allow(clippy::type_complexity)] #![allow(clippy::needless_late_init)] +use crossbeam_channel::{unbounded, Receiver, Sender}; use std::env; use std::ffi::OsString; -use futures::channel::mpsc; -use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use glib::Priority; use gtk4::gio::ApplicationFlags; use gtk4::prelude::*; @@ -87,7 +86,7 @@ fn build_ui(application: &Application, arguments: &[OsString]) { let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(Priority::default()); // Futures progress report - let (progress_sender, progress_receiver): (UnboundedSender, UnboundedReceiver) = mpsc::unbounded(); + let (progress_sender, progress_receiver): (Sender, Receiver) = unbounded(); initialize_gui(&gui_data); validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup diff --git a/czkawka_gui/src/tests.rs b/czkawka_gui/src/tests.rs index f70a469..e43b96c 100644 --- a/czkawka_gui/src/tests.rs +++ b/czkawka_gui/src/tests.rs @@ -6,7 +6,7 @@ use crate::GuiData; pub fn validate_notebook_data(gui_data: &GuiData) { // Test treeviews names, each treeview should have set name same as variable name - for (_i, item) in gui_data.main_notebook.get_main_tree_views().iter().enumerate() { + for item in &gui_data.main_notebook.get_main_tree_views() { // println!("Checking {} element", i); get_notebook_enum_from_tree_view(item); diff --git a/czkawka_slint_gui/Cargo.toml b/czkawka_slint_gui/Cargo.toml deleted file mode 100644 index 4123b97..0000000 --- a/czkawka_slint_gui/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "czkawka_slint" -version = "6.1.0" -authors = ["Rafał Mikrut "] -edition = "2021" -rust-version = "1.72.1" -description = "Slint frontend of Czkawka" -license = "GPL-3" -homepage = "https://github.com/qarmin/czkawka" -repository = "https://github.com/qarmin/czkawka" - -[dependencies] -slint = "1.2.2" -rand = "0.8.5" \ No newline at end of file diff --git a/czkawka_slint_gui/src/main.rs b/czkawka_slint_gui/src/main.rs deleted file mode 100644 index 3ebd063..0000000 --- a/czkawka_slint_gui/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() {} - -slint::slint! {} diff --git a/instructions/Compilation.md b/instructions/Compilation.md index 2fe33b4..287b647 100644 --- a/instructions/Compilation.md +++ b/instructions/Compilation.md @@ -1,5 +1,12 @@ # Compiling Czkawka from sources +This instruction is outdated and will be removed in one of next version, please look at README.md files in each module folder for more up to date instructions. +- [Czkawka GUI (GTK frontend)](../czkawka_gui/README.md)
+- [Czkawka CLI](../czkawka_cli/README.md)
+- [Czkawka Core](../czkawka_core/README.md)
+- [Krokiet GUI (Slint frontend)](../krokiet/README.md)
+ + ## Requirements If you only want the terminal version without a GUI, just skip all the packages with `gtk` in their names. @@ -12,7 +19,7 @@ New versions of GTK fixes some bugs, so e.g. middle button selection will work o | Program | Min | What for | |---------|--------|--------------------------------------------------------------------------------------| -| Rust | 1.70.0 | The minimum version of rust does not depend on anything, so it can change frequently | +| Rust | 1.72.1 | The minimum version of rust does not depend on anything, so it can change frequently | | GTK | 4.6 | Only for the `GTK` backend | #### Debian / Ubuntu diff --git a/instructions/Installation.md b/instructions/Installation.md index be03689..df4e1d0 100644 --- a/instructions/Installation.md +++ b/instructions/Installation.md @@ -1,4 +1,11 @@ # Installation + +This instruction is outdated and will be removed in one of next version, please look at README.md files in each module folder for more up to date instructions. +- [Czkawka GUI (GTK frontend)](../czkawka_gui/README.md)
+- [Czkawka CLI](../czkawka_cli/README.md)
+- [Czkawka Core](../czkawka_core/README.md)
+- [Krokiet GUI (Slint frontend)](../krokiet/README.md)
+ ## Requirements ### Linux If you use Snap, Flatpak or Appimage, you need to only install ffmpeg if you want to use Similar Videos tool. @@ -140,7 +147,7 @@ Flathub page with Czkawka can be found [**here**](https://flathub.org/apps/detai ### PPA - Debian/Ubuntu (unofficial) ``` sudo add-apt-repository ppa:xtradeb/apps -sudo apt-get update +sudo apt update sudo apt-get install czkawka ``` diff --git a/instructions/Translations.md b/instructions/Translations.md index 4c6010f..d4e14ae 100644 --- a/instructions/Translations.md +++ b/instructions/Translations.md @@ -8,6 +8,10 @@ Main/Default language is English, but also Polish is officially supported. Translating is mostly done by site - https://crowdin.com/project/czkawka +If you want to translate Czkawka to your language, you can do it in this site. + +Next chapters are only for internal use, so just use crowdin page. + ## How to translate Czkawka? Base translatable strings are placed under `i18n/en/czkawka_gui.ftl` file. diff --git a/krokiet/.clippy.toml b/krokiet/.clippy.toml new file mode 100644 index 0000000..e69de29 diff --git a/krokiet/Cargo.toml b/krokiet/Cargo.toml new file mode 100644 index 0000000..d92d9ba --- /dev/null +++ b/krokiet/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "krokiet" +version = "6.1.0" +authors = ["Rafał Mikrut "] +edition = "2021" +rust-version = "1.72.1" +description = "Slint frontend of Czkawka Core" +license = "GPL-3" +homepage = "https://github.com/qarmin/czkawka" +repository = "https://github.com/qarmin/czkawka" +build = "build.rs" + +[dependencies] +# Try to use only needed features from https://github.com/slint-ui/slint/blob/master/api/rs/slint/Cargo.toml#L23-L31 +#slint = { path = "/home/rafal/test/slint/api/rs/slint/", default-features = false, features = ["std", +#slint = { git = "https://github.com/slint-ui/slint.git", default-features = false, features = [ +slint = { version = "1.3", default-features = false, features = [ + "std", + "backend-winit", + "compat-1-2" +] } + +rand = "0.8" +czkawka_core = { version = "6.1.0", path = "../czkawka_core" } +chrono = "0.4.31" +open = "5.0" +crossbeam-channel = "0.5.8" +handsome_logger = "0.8" +rfd = { version = "0.12", default-features = false, features = ["xdg-portal"] } +home = "0.5" +log = "0.4.20" +serde = "1.0" +serde_json = "1.0" +humansize = "2.1" +image = "0.24" +directories-next = "2.0" +image_hasher = "1.2" +rayon = "1.8.0" + +# Translations +i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } +i18n-embed-fl = "0.7" +rust-embed = { version = "8.0", features = ["debug-embed"] } +once_cell = "1.18" + +[build-dependencies] +slint-build = "1.3" +#slint-build = { git = "https://github.com/slint-ui/slint.git" } +#slint-build = { path = "/home/rafal/test/slint/api/rs/build/"} + +[features] +default = ["winit_femtovg", "winit_software"] +skia_opengl = ["slint/renderer-skia-opengl"] +skia_vulkan = ["slint/renderer-skia-vulkan"] +software = ["slint/renderer-software"] +femtovg = ["slint/renderer-femtovg"] +winit_femtovg = ["slint/renderer-winit-femtovg"] +winit_skia_opengl = ["slint/renderer-winit-skia-opengl"] +winit_skia_vulkan = ["slint/renderer-winit-skia-vulkan"] +winit_software = ["slint/renderer-winit-software"] + +heif = ["czkawka_core/heif"] +libraw = ["czkawka_core/libraw"] diff --git a/czkawka_slint_gui/LICENSE b/krokiet/LICENSE similarity index 100% rename from czkawka_slint_gui/LICENSE rename to krokiet/LICENSE diff --git a/krokiet/LICENSE_MIT_CODE b/krokiet/LICENSE_MIT_CODE new file mode 100644 index 0000000..8836e46 --- /dev/null +++ b/krokiet/LICENSE_MIT_CODE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 Rafał Mikrut + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/krokiet/README.md b/krokiet/README.md new file mode 100644 index 0000000..7ba9f93 --- /dev/null +++ b/krokiet/README.md @@ -0,0 +1,160 @@ +# Krokiet + +Krokiet is new Czkawka frontend written in Slint(written mostly in Rust) in opposite to Gtk 4 frontend which uses mostly +C code. + +Different toolkit means different look, limitations and features, so you should not expect same features like in Gtk 4 +frontend(but of course I want implement most of features from other project). + +## Usage + +Krokiet should not have any special runtime requirements - it should work on almost any device non-antic device. + +Prebuild binaries should work on Windows 10,11, Mac, Ubuntu 22.04/20.04 and similar(libheif version + czkawka_gui requires Ubuntu 22.04+ and rest Ubuntu 20.04) - https://github.com/qarmin/czkawka/releases/ + + + +## Compilation + +On Ubuntu you need to install this dependencies: + +``` +sudo apt install libfontconfig-dev libfreetype-dev +``` + +Default compilation is done by `cargo build --release` and should work on most systems. + +You need the latest available version of Rust to compile it, because Krokiet aims to support the latest slint verions, +that should provide best experience. + +The only exception is building skia renderer which is non default feature that can be enabled manually if you want to +use it, that require on windows msvc compiler(not sure how to exactly install it). + +Also skia renderer is written in C++ and uses on platforms like x86_64 and arm64 prebuild binaries, so if you are using +different architecture, this library will be build from source, which can take a lot of time and require additional +dependencies. + +## Additional Renderers + +By default, only femtovg(opengl) and software renderer are enabled, but you can enable more renderers by compiling app +with additional features. + +Most of the users will want to use app with windowing system/compositor, so features starting with `winit` in name are +recommended. + +E.g. + +``` +cargo build --release --features "winit_skia_opengl" +cargo build --release --features "winit_software" +``` + +to run app with different renderers you need to use it, by adding `SLINT_BACKEND` environment + +``` +SLINT_BACKEND=winit-femtovg ./target/release/krokiet +SLINT_BACKEND=software ./target/release/krokiet +SLINT_BACKEND=skia ./target/release/krokiet # This uses now opengl - https://github.com/slint-ui/slint/discussions/3799 +``` + +when you will use invalid/non-existing backend, app will show warning + +``` +slint winit: unrecognized renderer skia, falling back to FemtoVG +``` + +to check what is really used, add `SLINT_DEBUG_PERFORMANCE=refresh_lazy,console,overlay` env + +``` +SLINT_DEBUG_PERFORMANCE=refresh_lazy,console,overlay cargo run +``` + +should print something like + +``` +Slint: Build config: debug; Backend: software +``` + +## Different theme + +App was created with dark fluent theme in mind, but is possible to use light theme by setting `SLINT_STYLE` environment +variable to `fluent-light` during compilation e.g. + +``` +SLINT_STYLE=fluent-light cargo run -- --path . +``` + +Slint supports also other themes, but they are not officially supported by this app and may be broken(but looks that +cupertino looks quite good with current style). + +``` +SLINT_STYLE=cupertino-light cargo run -- --path . +SLINT_STYLE=cupertino-dark cargo run -- --path . +SLINT_STYLE=material-light cargo run -- --path . +SLINT_STYLE=material-dark cargo run -- --path . +``` + +## How to help? + +- Suggesting possible design changes in the gui - of course, they should be possible to be simply implemented in the + slint keeping in mind the performance aspect as well +- Modifying user interface - gui is written in simple language similar to qml, that can be modified in vscode/web with + live + preview - [slint live preview example](https://slint.dev/releases/1.3.0/editor/?load_demo=examples/printerdemo/ui/printerdemo.slint) +- Improving app rust code + +## Missing features available in GTK 4 frontend + +- icons in buttons +- resizable input files panel +- settings +- moving files +- deleting files +- sorting files +- saving results +- symlink/hardlink +- implementing all modes +- multiple selection +- proper popup windows - slint not handle them properly +- logo +- about window +- reference folders +- translations(problem is only with interface, messages like "Checking {x} file" can be easily translated from rust + side) + +## Why Slint? + +There are multiple reasons why I decided to use Slint as toolkit for Krokiet over other toolkits. + +| Toolkit | Pros | Cons | +|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Gtk 4 | - Hard compilation/cross compilation and bundling all required libraries - mostly on windows
- Cambalache can be used to create graphically gui
- Good gtk4-rs bindings(but sometimes not really intuitive) | - Hard compilation/cross compilation and bundling all required libraries - mostly on windows
- Forcing the use of a specific gui creation style
- Strange crashes, not working basic features, etc.(again, mostly on windows)
- Forcing to use bugged/outdated but dynamically loaded version of libraries on linux (e.g. 4.6 on Ubuntu 22.04) - not all fixes are backported | +| Qt | - QML support - simplify creating of gui from code it is easy to use and powerful
- Very flexible framework
- Typescript/javascript <=> qml interoperability
- Probably the most mature GUI library | - New and limited qt bindings
- Hard to cross-compile
- Very easy to create and use invalid state in QML(unexpected null/undefined values, messed properties bindings etc.)
- Commercial license or GPL | +| Slint | - Internal language is compiled to native code
- Live gui preview with Vscode/Vscodium without needing to use rust
- Full rust solution - easy to compile/cross compile, minimal runtime requirements
- Static type checks in slint files | - Internal .slint language is more limited than QML
- Out of bounds and similar errors are quietly being corrected instead printing error - this can lead to hard to debug problems
- Only GPL is only available open-source license
- Popup windows almost not exists
- Internal widgets are almost not customizable and usually quite limited | +| Iced | - ~100% rust code - so compilation is simple
- Elm architecture - simple to understand | - Mostly maintained by one person - slows down fixing bugs and implementing new features
- GUI can be created only from rust code, which really is bad for creating complex GUIs(mostly due rust compile times)
- Docs are almost non-existent | +| Tauri | - Easy to create ui(at least for web developers) - uses html/css/js
- Quite portable | - Webview dependency - it is not really lightweight and can be hard to compile on some platforms and on Linux e.g. webRTC not working
- Cannot select directory - file chooser only can choose files - small thing but important for me
- Not very performant Rust <=> Javascript communication | + +Since I don't have time to create really complex and good looking GUI, I needed a helper tool to create GUI not from +Rust(I don't want to use different language, because this will make communication with czkawka_core harder) so I decided +to not look at Iced which only allows to create GUI from Rust. + +GTK and QT also I throw away due cross compilation problems caused mostly by using C/C++ internally. Using GTK in +Czkawka was a reason why I started to find other toolkits. + +Tauri - I don't really like to use Javascript because I already used it with Qt(C++) + QML + Typescript combination and +I found that creating ui in such language may be simple at start but later any bigger changes cause a lot of runtime +errors. + +So only Slint left with its cons and pros. + +## License + +Code is licensed under MIT license but entire project is licensed under GPL-3.0 license, due Slint license restrictions. + +## Name + +Why Krokiet(eng. Croquette)? +Because I like croquettes(Polish version), the ones with meat, mushrooms wrapped in breadcrumbs... it makes my mouth +water. +I considered also other dishes which I like to eat like pierogi, żurek, pączek, schabowy or zapiekanka. +This name should be a lot of easier to remember than czkawka or szyszka. \ No newline at end of file diff --git a/krokiet/build.rs b/krokiet/build.rs new file mode 100644 index 0000000..7f1c722 --- /dev/null +++ b/krokiet/build.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + if env::var("SLINT_STYLE").is_err() || env::var("SLINT_STYLE") == Ok(String::new()) { + slint_build::compile_with_config("ui/main_window.slint", slint_build::CompilerConfiguration::new().with_style("fluent-dark".into())).unwrap(); + } else { + slint_build::compile("ui/main_window.slint").unwrap(); + } +} diff --git a/krokiet/i18n.toml b/krokiet/i18n.toml new file mode 100644 index 0000000..f2456c8 --- /dev/null +++ b/krokiet/i18n.toml @@ -0,0 +1,13 @@ +# (Required) The language identifier of the language used in the +# source code for gettext system, and the primary fallback language +# (for which all strings must be present) when using the fluent +# system. +fallback_language = "en" + +# Use the fluent localization system. +[fluent] +# (Required) The path to the assets directory. +# The paths inside the assets directory should be structured like so: +# `assets_dir/{language}/{domain}.ftl` +assets_dir = "i18n" + diff --git a/krokiet/i18n/en/krokiet.ftl b/krokiet/i18n/en/krokiet.ftl new file mode 100644 index 0000000..458f490 --- /dev/null +++ b/krokiet/i18n/en/krokiet.ftl @@ -0,0 +1 @@ +settings_language = Languages \ No newline at end of file diff --git a/krokiet/icons/logo.png b/krokiet/icons/logo.png new file mode 100644 index 0000000..20230dc Binary files /dev/null and b/krokiet/icons/logo.png differ diff --git a/krokiet/icons/settings.svg b/krokiet/icons/settings.svg new file mode 100644 index 0000000..ac19230 --- /dev/null +++ b/krokiet/icons/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/krokiet/src/common.rs b/krokiet/src/common.rs new file mode 100644 index 0000000..2f29e37 --- /dev/null +++ b/krokiet/src/common.rs @@ -0,0 +1,55 @@ +use crate::CurrentTab; +use slint::{ModelRc, SharedString, StandardListViewItem, VecModel}; +use std::path::PathBuf; + +// Remember to match updated this according to ui/main_lists.slint and connect_scan.rs files +pub fn get_path_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::EmptyFolders => 1, + CurrentTab::EmptyFiles => 1, + CurrentTab::SimilarImages => 4, + CurrentTab::Settings => panic!("Button should be disabled"), + } +} +pub fn get_name_idx(active_tab: CurrentTab) -> usize { + match active_tab { + CurrentTab::EmptyFolders => 0, + CurrentTab::EmptyFiles => 0, + CurrentTab::SimilarImages => 3, + CurrentTab::Settings => panic!("Button should be disabled"), + } +} +pub fn get_is_header_mode(active_tab: CurrentTab) -> bool { + match active_tab { + CurrentTab::EmptyFolders | CurrentTab::EmptyFiles => false, + CurrentTab::SimilarImages => true, + CurrentTab::Settings => panic!("Button should be disabled"), + } +} + +// pub fn create_string_standard_list_view(items: &[String]) -> ModelRc { +// let new_folders_standard_list_view = items +// .iter() +// .map(|x| { +// let mut element = StandardListViewItem::default(); +// element.text = x.into(); +// element +// }) +// .collect::>(); +// ModelRc::new(VecModel::from(new_folders_standard_list_view)) +// } +pub fn create_string_standard_list_view_from_pathbuf(items: &[PathBuf]) -> ModelRc { + let new_folders_standard_list_view = items + .iter() + .map(|x| { + let mut element = StandardListViewItem::default(); + element.text = x.to_string_lossy().to_string().into(); + element + }) + .collect::>(); + ModelRc::new(VecModel::from(new_folders_standard_list_view)) +} + +pub fn create_vec_model_from_vec_string(items: Vec) -> VecModel { + VecModel::from(items.into_iter().map(SharedString::from).collect::>()) +} diff --git a/krokiet/src/connect_delete.rs b/krokiet/src/connect_delete.rs new file mode 100644 index 0000000..b278be5 --- /dev/null +++ b/krokiet/src/connect_delete.rs @@ -0,0 +1,276 @@ +use slint::{ComponentHandle, Model, ModelRc, VecModel}; + +use crate::common::{get_is_header_mode, get_name_idx, get_path_idx}; +use crate::{Callabler, CurrentTab, GuiState, MainListModel, MainWindow}; +use czkawka_core::common::{remove_folder_if_contains_only_empty_folders, CHARACTER}; +use log::info; +use rayon::prelude::*; + +pub fn connect_delete_button(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_delete_selected_items(move || { + let app = a.upgrade().unwrap(); + + let active_tab = app.global::().get_active_tab(); + + let model = match active_tab { + CurrentTab::EmptyFolders => app.get_empty_folder_model(), + CurrentTab::SimilarImages => app.get_similar_images_model(), + CurrentTab::EmptyFiles => app.get_empty_files_model(), + CurrentTab::Settings => panic!("Button should be disabled"), + }; + + let new_model = handle_delete_items(&model, active_tab); + + if let Some(new_model) = new_model { + match active_tab { + CurrentTab::EmptyFolders => app.set_empty_folder_model(new_model), + CurrentTab::SimilarImages => app.set_similar_images_model(new_model), + CurrentTab::EmptyFiles => app.set_empty_files_model(new_model), + CurrentTab::Settings => panic!("Button should be disabled"), + } + } + + app.global::().set_preview_visible(false); + }); +} + +fn handle_delete_items(items: &ModelRc, active_tab: CurrentTab) -> Option> { + let (entries_to_delete, mut entries_left) = filter_out_checked_items(items, get_is_header_mode(active_tab)); + + if !entries_to_delete.is_empty() { + remove_selected_items(entries_to_delete, active_tab); + deselect_all_items(&mut entries_left); + + let r = ModelRc::new(VecModel::from(entries_left)); // TODO here maybe should also stay old model if entries cannot be removed + return Some(r); + } + None +} + +// TODO delete in parallel items, consider to add progress bar +// For empty folders double check if folders are really empty - this function probably should be run in thread +// and at the end should be send signal to main thread to update model +// TODO handle also situations where cannot delete file/folder +fn remove_selected_items(items: Vec, active_tab: CurrentTab) { + let path_idx = get_path_idx(active_tab); + let name_idx = get_name_idx(active_tab); + let items_to_remove = items + .iter() + .map(|item| { + let path = item.val.iter().nth(path_idx).unwrap(); + let name = item.val.iter().nth(name_idx).unwrap(); + format!("{}{}{}", path, CHARACTER, name) + }) + .collect::>(); + + info!("Removing items: {:?} {:?}", items_to_remove, active_tab); + // Iterate over empty folders and not delete them if they are not empty + if active_tab == CurrentTab::EmptyFolders { + items_to_remove.into_par_iter().for_each(|item| { + remove_folder_if_contains_only_empty_folders(item); + }); + } else { + items_to_remove.into_par_iter().for_each(|item| { + let _ = std::fs::remove_file(item); + }); + } +} + +fn deselect_all_items(items: &mut [MainListModel]) { + for item in items { + item.selected_row = false; + } +} + +fn filter_out_checked_items(items: &ModelRc, have_header: bool) -> (Vec, Vec) { + if cfg!(debug_assertions) { + check_if_header_is_checked(items); + check_if_header_is_selected_but_should_not_be(items, have_header); + } + + let (entries_to_delete, mut entries_left): (Vec<_>, Vec<_>) = items.iter().partition(|item| item.checked); + + // When have header, we must also throw out orphaned items - this needs to be + if have_header && !entries_left.is_empty() { + // First row must be header + assert!(entries_left[0].header_row); + + if entries_left.len() == 3 { + // First row is header, so if second or third is also header, then there is no enough items to fill model + if entries_left[1].header_row || entries_left[2].header_row { + entries_left = Vec::new(); + } + } else if entries_left.len() < 3 { + // Not have enough items to fill model + entries_left = Vec::new(); + } else { + let mut last_header = 0; + let mut new_items: Vec = Vec::new(); + for i in 1..entries_left.len() { + if entries_left[i].header_row { + if i - last_header > 2 { + new_items.extend(entries_left[last_header..i].iter().cloned()); + } + last_header = i; + } + } + if entries_left.len() - last_header > 2 { + new_items.extend(entries_left[last_header..].iter().cloned()); + } + + entries_left = new_items; + } + } + + (entries_to_delete, entries_left) +} + +// Function to verify if really headers are not checked +// Checked header is big bug +fn check_if_header_is_checked(items: &ModelRc) { + if cfg!(debug_assertions) { + for item in items.iter() { + if item.header_row { + assert!(!item.checked); + } + } + } +} + +// In some modes header should not be visible, but if are, then it is a bug +fn check_if_header_is_selected_but_should_not_be(items: &ModelRc, can_have_header: bool) { + if cfg!(debug_assertions) { + if !can_have_header { + for item in items.iter() { + assert!(!item.header_row); + } + } + } +} + +#[cfg(test)] +mod tests { + use slint::{Model, ModelRc, SharedString, VecModel}; + + use crate::connect_delete::filter_out_checked_items; + use crate::MainListModel; + + #[test] + fn test_filter_out_checked_items_empty() { + let items: ModelRc = create_new_model(vec![]); + + let (to_delete, left) = filter_out_checked_items(&items, false); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + let (to_delete, left) = filter_out_checked_items(&items, true); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + } + #[test] + fn test_filter_out_checked_items_one_element_valid_normal() { + let items = create_new_model(vec![(false, false, false, vec![])]); + let (to_delete, left) = filter_out_checked_items(&items, false); + assert!(to_delete.is_empty()); + assert_eq!(left.len(), items.iter().count()); + } + + #[test] + fn test_filter_out_checked_items_one_element_valid_header() { + let items = create_new_model(vec![(false, true, false, vec![])]); + let (to_delete, left) = filter_out_checked_items(&items, true); + assert!(to_delete.is_empty()); + assert!(left.is_empty()); + } + + #[test] + #[should_panic] + fn test_filter_out_checked_items_one_element_invalid_normal() { + let items = create_new_model(vec![(false, true, false, vec![])]); + filter_out_checked_items(&items, false); + } + #[test] + #[should_panic] + fn test_filter_out_checked_items_one_element_invalid_header() { + let items = create_new_model(vec![(false, false, false, vec![])]); + filter_out_checked_items(&items, true); + } + + #[test] + fn test_filter_out_checked_items_multiple_element_valid_normal() { + let items = create_new_model(vec![ + (false, false, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (true, false, false, vec!["4"]), + (false, false, false, vec!["5"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, false); + let to_delete_data = get_single_data_from_model(&to_delete); + let left_data = get_single_data_from_model(&left); + + assert_eq!(to_delete_data, vec!["3", "4"]); + assert_eq!(left_data, vec!["1", "2", "5"]); + } + + #[test] + fn test_filter_out_checked_items_multiple_element_valid_header() { + let items = create_new_model(vec![ + (false, true, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (false, true, false, vec!["4"]), + (false, false, false, vec!["5"]), + (false, true, false, vec!["6"]), + (false, false, false, vec!["7"]), + (false, false, false, vec!["8"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + let to_delete_data = get_single_data_from_model(&to_delete); + let left_data = get_single_data_from_model(&left); + + assert_eq!(to_delete_data, vec!["3"]); + assert_eq!(left_data, vec!["6", "7", "8"]); + } + + #[test] + fn test_filter_out_checked_items_multiple2_element_valid_header() { + let items = create_new_model(vec![ + (false, true, false, vec!["1"]), + (false, false, false, vec!["2"]), + (true, false, false, vec!["3"]), + (false, false, false, vec!["4"]), + (false, false, false, vec!["5"]), + (false, false, false, vec!["6"]), + (false, true, false, vec!["7"]), + (false, false, false, vec!["8"]), + ]); + let (to_delete, left) = filter_out_checked_items(&items, true); + let to_delete_data = get_single_data_from_model(&to_delete); + let left_data = get_single_data_from_model(&left); + + assert_eq!(to_delete_data, vec!["3"]); + assert_eq!(left_data, vec!["1", "2", "4", "5", "6"]); + } + + fn get_single_data_from_model(model: &[MainListModel]) -> Vec { + let mut d = model.iter().map(|item| item.val.iter().next().unwrap().to_string()).collect::>(); + d.sort(); + d + } + + fn create_new_model(items: Vec<(bool, bool, bool, Vec<&'static str>)>) -> ModelRc { + let model = VecModel::default(); + for item in items { + let all_items: Vec = item.3.iter().map(|item| (*item).into()).collect::>(); + let all_items = VecModel::from(all_items); + model.push(MainListModel { + checked: item.0, + header_row: item.1, + selected_row: item.2, + val: ModelRc::new(all_items), + }); + } + ModelRc::new(model) + } +} diff --git a/krokiet/src/connect_directories_changes.rs b/krokiet/src/connect_directories_changes.rs new file mode 100644 index 0000000..be8d5a7 --- /dev/null +++ b/krokiet/src/connect_directories_changes.rs @@ -0,0 +1,121 @@ +use rfd::FileDialog; +use slint::{ComponentHandle, Model, ModelRc, VecModel}; + +use crate::{Callabler, MainWindow, Settings}; + +pub fn connect_add_remove_directories(app: &MainWindow) { + connect_add_directories(app); + connect_remove_directories(app); + connect_add_manual_directories(app); +} + +fn connect_add_manual_directories(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_added_manual_directories(move |included_directories, list_of_files_to_add| { + let non_empty_lines = list_of_files_to_add.lines().filter(|x| !x.is_empty()).collect::>(); + if non_empty_lines.is_empty() { + return; + } + let app = a.upgrade().unwrap(); + let settings = app.global::(); + + if included_directories { + let included_model = settings.get_included_directories(); + let mut included_model = included_model.iter().collect::>(); + included_model.extend(non_empty_lines.iter().map(|x| { + let mut element = slint::StandardListViewItem::default(); + element.text = (*x).into(); + element + })); + included_model.sort_by_cached_key(|x| x.text.to_string()); + included_model.dedup(); + settings.set_included_directories(ModelRc::new(VecModel::from(included_model))); + } else { + let excluded_model = settings.get_excluded_directories(); + let mut excluded_model = excluded_model.iter().collect::>(); + excluded_model.extend(non_empty_lines.iter().map(|x| { + let mut element = slint::StandardListViewItem::default(); + element.text = (*x).into(); + element + })); + excluded_model.sort_by_cached_key(|x| x.text.to_string()); + excluded_model.dedup(); + settings.set_excluded_directories(ModelRc::new(VecModel::from(excluded_model))); + } + }); +} + +fn connect_remove_directories(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_remove_item_directories(move |included_directories, current_index| { + // Nothing selected + if current_index == -1 { + return; + } + let app = a.upgrade().unwrap(); + let settings = app.global::(); + + if included_directories { + let included_model = settings.get_included_directories(); + let model_count = included_model.iter().count(); + + if model_count > current_index as usize { + let mut included_model = included_model.iter().collect::>(); + included_model.remove(current_index as usize); + settings.set_included_directories(ModelRc::new(VecModel::from(included_model))); + } + } else { + let excluded_model = settings.get_excluded_directories(); + let model_count = excluded_model.iter().count(); + + if model_count > current_index as usize { + let mut excluded_model = excluded_model.iter().collect::>(); + excluded_model.remove(current_index as usize); + settings.set_excluded_directories(ModelRc::new(VecModel::from(excluded_model))); + } + } + }); +} + +fn connect_add_directories(app: &MainWindow) { + let a = app.as_weak(); + app.on_folder_choose_requested(move |included_directories| { + let app = a.upgrade().unwrap(); + + let directory = std::env::current_dir().unwrap_or(std::path::PathBuf::from("/")); + + let file_dialog = FileDialog::new().set_directory(directory); + + let Some(folders) = file_dialog.pick_folders() else { + return; + }; + + let settings = app.global::(); + let old_folders = if included_directories { + settings.get_included_directories() + } else { + settings.get_excluded_directories() + }; + + let mut new_folders = old_folders.iter().map(|x| x.text.to_string()).collect::>(); + new_folders.extend(folders.iter().map(|x| x.to_string_lossy().to_string())); + new_folders.sort(); + new_folders.dedup(); + + let new_folders_standard_list_view = new_folders + .iter() + .map(|x| { + let mut element = slint::StandardListViewItem::default(); + element.text = x.into(); + element + }) + .collect::>(); + let new_folders_model = ModelRc::new(VecModel::from(new_folders_standard_list_view)); + + if included_directories { + settings.set_included_directories(new_folders_model); + } else { + settings.set_excluded_directories(new_folders_model); + } + }); +} diff --git a/krokiet/src/connect_open.rs b/krokiet/src/connect_open.rs new file mode 100644 index 0000000..d9e99b4 --- /dev/null +++ b/krokiet/src/connect_open.rs @@ -0,0 +1,39 @@ +use crate::{Callabler, MainWindow}; +use directories_next::ProjectDirs; +use log::error; +use slint::ComponentHandle; + +pub fn connect_open_items(app: &MainWindow) { + app.global::().on_item_opened(move |path| { + match open::that(&*path) { + Ok(()) => {} + Err(e) => { + eprintln!("Failed to open file: {e}"); + } + }; + // TODO - this should be added to line edit + }); + + app.global::().on_open_config_folder(move || { + let Some(dirs) = ProjectDirs::from("pl", "Qarmin", "Krokiet") else { + error!("Failed to open config folder"); + return; + }; + let config_folder = dirs.config_dir(); + if let Err(e) = open::that(config_folder) { + error!("Failed to open config folder {:?}: {e}", config_folder); + } + }); + + // Cache uses Czkawka name to easily change between apps + app.global::().on_open_cache_folder(move || { + let Some(dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") else { + error!("Failed to open cache folder"); + return; + }; + let cache_folder = dirs.cache_dir(); + if let Err(e) = open::that(cache_folder) { + error!("Failed to open cache folder {:?}: {e}", cache_folder); + } + }); +} diff --git a/krokiet/src/connect_progress_receiver.rs b/krokiet/src/connect_progress_receiver.rs new file mode 100644 index 0000000..376ec38 --- /dev/null +++ b/krokiet/src/connect_progress_receiver.rs @@ -0,0 +1,93 @@ +use crate::{MainWindow, ProgressToSend}; + +use crossbeam_channel::Receiver; +use czkawka_core::common_dir_traversal::{ProgressData, ToolType}; +use slint::ComponentHandle; +use std::thread; + +pub fn connect_progress_gathering(app: &MainWindow, progress_receiver: Receiver) { + let a = app.as_weak(); + + thread::spawn(move || loop { + let Ok(progress_data) = progress_receiver.recv() else { + return; // Channel closed, so exit the thread since app closing + }; + + a.upgrade_in_event_loop(move |app| { + let to_send; + match progress_data.tool_type { + ToolType::EmptyFiles => { + let (all_progress, current_progress) = no_current_stage_get_data(&progress_data); + to_send = ProgressToSend { + all_progress, + current_progress, + step_name: format!("Checked {} files", progress_data.entries_checked).into(), + }; + } + ToolType::EmptyFolders => { + let (all_progress, current_progress) = no_current_stage_get_data(&progress_data); + to_send = ProgressToSend { + all_progress, + current_progress, + step_name: format!("Checked {} folders", progress_data.entries_checked).into(), + }; + } + ToolType::SimilarImages => { + let step_name; + let all_progress; + let current_progress; + match progress_data.current_stage { + 0 => { + (all_progress, current_progress) = no_current_stage_get_data(&progress_data); + step_name = format!("Scanning {} file", progress_data.entries_checked); + } + 1 => { + (all_progress, current_progress) = common_get_data(&progress_data); + step_name = format!("Hashing {}/{} image", progress_data.entries_checked, progress_data.entries_to_check); + } + 2 => { + (all_progress, current_progress) = common_get_data(&progress_data); + step_name = format!("Comparing {}/{} image hash", progress_data.entries_checked, progress_data.entries_to_check); + } + _ => panic!(), + } + + to_send = ProgressToSend { + all_progress, + current_progress, + step_name: step_name.into(), + }; + } + _ => { + panic!("Invalid tool type {:?}", progress_data.tool_type); + } + } + app.set_progress_datas(to_send); + }) + .unwrap(); + }); +} + +// Used when current stage not have enough data to show status, so we show only all_stages +// Happens if we searching files and we don't know how many files we need to check +fn no_current_stage_get_data(item: &ProgressData) -> (i32, i32) { + let all_stages = (item.current_stage as f64) / (item.max_stage + 1) as f64; + + ((all_stages * 100.0) as i32, -1) +} + +// Used to calculate number of files to check and also to calculate current progress according to number of files to check and checked +fn common_get_data(item: &ProgressData) -> (i32, i32) { + 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 * 100.0) as i32, (current_stage * 100.0) as i32) + } 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 * 100.0) as i32, 0) + } +} diff --git a/krokiet/src/connect_scan.rs b/krokiet/src/connect_scan.rs new file mode 100644 index 0000000..7e3c834 --- /dev/null +++ b/krokiet/src/connect_scan.rs @@ -0,0 +1,208 @@ +use crate::settings::{collect_settings, SettingsCustom, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; +use crate::{CurrentTab, GuiState, MainListModel, MainWindow, ProgressToSend}; +use chrono::NaiveDateTime; +use crossbeam_channel::{Receiver, Sender}; +use czkawka_core::common::{split_path, DEFAULT_THREAD_SIZE}; +use czkawka_core::common_dir_traversal::ProgressData; +use czkawka_core::common_tool::CommonData; +use czkawka_core::common_traits::ResultEntry; +use czkawka_core::empty_files::EmptyFiles; +use czkawka_core::empty_folder::EmptyFolder; +use czkawka_core::similar_images; +use czkawka_core::similar_images::SimilarImages; +use humansize::{format_size, BINARY}; +use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak}; +use std::path::PathBuf; +use std::rc::Rc; +use std::thread; + +pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender, stop_receiver: Receiver<()>) { + let a = app.as_weak(); + app.on_scan_starting(move |active_tab| { + let progress_sender = progress_sender.clone(); + let stop_receiver = stop_receiver.clone(); + let app = a.upgrade().unwrap(); + + app.set_progress_datas(ProgressToSend { + all_progress: 0, + current_progress: -1, + step_name: "".into(), + }); + + let custom_settings = collect_settings(&app); + + let a = app.as_weak(); + match active_tab { + CurrentTab::EmptyFolders => { + scan_empty_folders(a, progress_sender, stop_receiver, custom_settings); + } + CurrentTab::EmptyFiles => { + scan_empty_files(a, progress_sender, stop_receiver, custom_settings); + } + CurrentTab::SimilarImages => { + scan_similar_images(a, progress_sender, stop_receiver, custom_settings); + } + CurrentTab::Settings => panic!("Button should be disabled"), + } + }); +} + +// TODO handle referenced folders +fn scan_similar_images(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = SimilarImages::new(); + set_common_settings(&mut finder, &custom_settings); + finder.set_hash_size(custom_settings.similar_images_sub_hash_size); + let resize_algortithm = ALLOWED_RESIZE_ALGORITHM_VALUES + .iter() + .find(|(setting_name, _gui_name, _resize_alg)| setting_name == &custom_settings.similar_images_sub_resize_algorithm) + .expect("Resize algorithm not found") + .2; + finder.set_image_filter(resize_algortithm); + let hash_type = ALLOWED_HASH_TYPE_VALUES + .iter() + .find(|(setting_name, _gui_name, _resize_alg)| setting_name == &custom_settings.similar_images_sub_hash_type) + .expect("Hash type not found") + .2; + finder.set_hash_alg(hash_type); + finder.set_exclude_images_with_same_size(custom_settings.similar_images_sub_ignore_same_size); + finder.set_similarity(custom_settings.similar_images_sub_similarity as u32); + finder.find_similar_images(Some(&stop_receiver), Some(&progress_sender)); + + let mut vector = finder.get_similar_images().clone(); + let messages = finder.get_text_messages().create_messages_text(); + + for vec_fe in &mut vector { + vec_fe.sort_unstable_by_key(|e| e.similarity); + } + + let hash_size = finder.hash_size; + + a.upgrade_in_event_loop(move |app| { + let number_of_empty_files = vector.len(); + let items = Rc::new(VecModel::default()); + for vec_fe in vector { + insert_data_to_model(&items, ModelRc::new(VecModel::default()), true); + for fe in vec_fe { + let (directory, file) = split_path(fe.get_path()); + let data_model = VecModel::from_slice(&[ + similar_images::get_string_from_similarity(&fe.similarity, hash_size).into(), + format_size(fe.size, BINARY).into(), + fe.dimensions.clone().into(), + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + + insert_data_to_model(&items, data_model, false); + } + } + app.set_similar_images_model(items.into()); + app.invoke_scan_ended(format!("Found {} similar images files", number_of_empty_files).into()); + app.global::().set_info_text(messages.into()); + }) + }) + .unwrap(); +} + +fn scan_empty_files(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = EmptyFiles::new(); + set_common_settings(&mut finder, &custom_settings); + finder.find_empty_files(Some(&stop_receiver), Some(&progress_sender)); + + let mut vector = finder.get_empty_files().clone(); + let messages = finder.get_text_messages().create_messages_text(); + + vector.sort_unstable_by_key(|e| { + let t = split_path(e.get_path()); + (t.0, t.1) + }); + + a.upgrade_in_event_loop(move |app| { + let number_of_empty_files = vector.len(); + let items = Rc::new(VecModel::default()); + for fe in vector { + let (directory, file) = split_path(fe.get_path()); + let data_model = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string().into(), + ]); + + insert_data_to_model(&items, data_model, false); + } + app.set_empty_files_model(items.into()); + app.invoke_scan_ended(format!("Found {} empty files", number_of_empty_files).into()); + app.global::().set_info_text(messages.into()); + }) + }) + .unwrap(); +} + +fn scan_empty_folders(a: Weak, progress_sender: Sender, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) { + thread::Builder::new() + .stack_size(DEFAULT_THREAD_SIZE) + .spawn(move || { + let mut finder = EmptyFolder::new(); + set_common_settings(&mut finder, &custom_settings); + finder.find_empty_folders(Some(&stop_receiver), Some(&progress_sender)); + + let mut vector = finder.get_empty_folder_list().keys().cloned().collect::>(); + let messages = finder.get_text_messages().create_messages_text(); + + vector.sort_unstable_by_key(|e| { + let t = split_path(e.as_path()); + (t.0, t.1) + }); + + a.upgrade_in_event_loop(move |app| { + let folder_map = finder.get_empty_folder_list(); + let items = Rc::new(VecModel::default()); + for path in vector { + let (directory, file) = split_path(&path); + let data_model = VecModel::from_slice(&[ + file.into(), + directory.into(), + NaiveDateTime::from_timestamp_opt(folder_map[&path].modified_date as i64, 0).unwrap().to_string().into(), + ]); + + insert_data_to_model(&items, data_model, false); + } + app.set_empty_folder_model(items.into()); + app.invoke_scan_ended(format!("Found {} empty folders", folder_map.len()).into()); + app.global::().set_info_text(messages.into()); + }) + }) + .unwrap(); +} + +fn insert_data_to_model(items: &Rc>, data_model: ModelRc, header_row: bool) { + let main = MainListModel { + checked: false, + header_row, + selected_row: false, + val: ModelRc::new(data_model), + }; + items.push(main); +} + +fn set_common_settings(component: &mut T, custom_settings: &SettingsCustom) +where + T: CommonData, +{ + component.set_included_directory(custom_settings.included_directories.clone()); + component.set_excluded_directory(custom_settings.excluded_directories.clone()); + component.set_recursive_search(custom_settings.recursive_search); + component.set_minimal_file_size(custom_settings.minimum_file_size as u64 * 1024); + component.set_maximal_file_size(custom_settings.maximum_file_size as u64 * 1024); + component.set_allowed_extensions(custom_settings.allowed_extensions.clone()); + component.set_excluded_items(custom_settings.excluded_items.split(',').map(str::to_string).collect()); + component.set_exclude_other_filesystems(custom_settings.ignore_other_file_systems); + component.set_use_cache(custom_settings.use_cache); + component.set_save_also_as_json(custom_settings.save_also_as_json); +} diff --git a/krokiet/src/connect_show_preview.rs b/krokiet/src/connect_show_preview.rs new file mode 100644 index 0000000..30aeec3 --- /dev/null +++ b/krokiet/src/connect_show_preview.rs @@ -0,0 +1,82 @@ +use crate::{Callabler, GuiState, MainWindow}; +use czkawka_core::common::{get_dynamic_image_from_raw_image, IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS}; +use image::DynamicImage; +use log::{debug, error}; +use slint::ComponentHandle; +use std::path::Path; +use std::time::{Duration, Instant}; + +pub type ImageBufferRgba = image::ImageBuffer, Vec>; + +pub fn connect_show_preview(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_load_image_preview(move |image_path| { + let app = a.upgrade().unwrap(); + + let path = Path::new(image_path.as_str()); + + let res = load_image(path); + if let Some((load_time, img)) = res { + let start_timer_convert_time = Instant::now(); + let slint_image = convert_into_slint_image(img); + let convert_time = start_timer_convert_time.elapsed(); + + let start_set_time = Instant::now(); + app.global::().set_preview_image(slint_image); + let set_time = start_set_time.elapsed(); + + debug!( + "Loading image took: {:?}, converting image took: {:?}, setting image took: {:?}", + load_time, convert_time, set_time + ); + app.global::().set_preview_visible(true); + } else { + app.global::().set_preview_visible(false); + } + }); +} + +fn convert_into_slint_image(img: DynamicImage) -> slint::Image { + let image_buffer: ImageBufferRgba = img.to_rgba8(); + let buffer = slint::SharedPixelBuffer::::clone_from_slice(image_buffer.as_raw(), image_buffer.width(), image_buffer.height()); + slint::Image::from_rgba8(buffer) +} + +fn load_image(image_path: &Path) -> Option<(Duration, image::DynamicImage)> { + if !image_path.is_file() { + return None; + } + let image_name = image_path.to_string_lossy().to_string(); + let image_extension = image_path.extension()?.to_string_lossy().to_lowercase(); + let extension_with_dot = format!(".{}", image_extension); + + let is_raw_image = RAW_IMAGE_EXTENSIONS.contains(&extension_with_dot.as_str()); + let is_normal_image = IMAGE_RS_EXTENSIONS.contains(&extension_with_dot.as_str()); + + if !is_raw_image && !is_normal_image { + return None; + } + let load_img_start_timer = Instant::now(); + + // TODO this needs to be run inside closure + let img = if is_normal_image { + match image::open(image_name) { + Ok(img) => img, + Err(e) => { + error!("Error while loading image: {}", e); + return None; + } + } + } else if is_raw_image { + if let Some(img) = get_dynamic_image_from_raw_image(image_name) { + img + } else { + error!("Error while loading raw image - not sure why - try to guess"); + return None; + } + } else { + panic!("Used not supported image extension"); + }; + + Some((load_img_start_timer.elapsed(), img)) +} diff --git a/krokiet/src/connect_stop.rs b/krokiet/src/connect_stop.rs new file mode 100644 index 0000000..8a1c7fd --- /dev/null +++ b/krokiet/src/connect_stop.rs @@ -0,0 +1,8 @@ +use crate::MainWindow; +use crossbeam_channel::Sender; + +pub fn connect_stop_button(app: &MainWindow, stop_sender: Sender<()>) { + app.on_scan_stopping(move || { + stop_sender.send(()).unwrap(); + }); +} diff --git a/krokiet/src/connect_translation.rs b/krokiet/src/connect_translation.rs new file mode 100644 index 0000000..01960d8 --- /dev/null +++ b/krokiet/src/connect_translation.rs @@ -0,0 +1,23 @@ +use crate::localizer_krokiet::LANGUAGE_LOADER_GUI; +use crate::{Callabler, MainWindow}; +use slint::ComponentHandle; +use slint::Model; +use std::collections::HashMap; + +pub fn connect_translations(app: &MainWindow) { + app.global::().on_translate(move |text_to_translate, args| { + let text_to_translate = text_to_translate.to_string(); + + let mut arguments = HashMap::new(); + args.iter().for_each(|(key, value)| { + arguments.insert(key.to_string(), value.to_string()); + }); + + if arguments.is_empty() { + LANGUAGE_LOADER_GUI.get(&text_to_translate) + } else { + LANGUAGE_LOADER_GUI.get_args(&text_to_translate, arguments) + } + .into() + }); +} diff --git a/krokiet/src/localizer_krokiet.rs b/krokiet/src/localizer_krokiet.rs new file mode 100644 index 0000000..2c0cabc --- /dev/null +++ b/krokiet/src/localizer_krokiet.rs @@ -0,0 +1,32 @@ +use i18n_embed::fluent::{fluent_language_loader, FluentLanguageLoader}; +use i18n_embed::LanguageLoader; +use once_cell::sync::Lazy; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n/"] +struct Localizations; + +pub static LANGUAGE_LOADER_GUI: Lazy = Lazy::new(|| { + let loader: FluentLanguageLoader = fluent_language_loader!(); + + loader.load_fallback_language(&Localizations).expect("Error while loading fallback language"); + + loader +}); + +#[macro_export] +macro_rules! flk { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::localizer_krokiet::LANGUAGE_LOADER_GUI, $message_id) + }}; + + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::localizer_krokiet::LANGUAGE_LOADER_GUI, $message_id, $($args), *) + }}; +} + +// // Get the `Localizer` to be used for localizing this library. +// pub fn localizer_krokiet() -> Box { +// Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER_GUI, &Localizations)) +// } diff --git a/krokiet/src/main.rs b/krokiet/src/main.rs new file mode 100644 index 0000000..612ffcd --- /dev/null +++ b/krokiet/src/main.rs @@ -0,0 +1,134 @@ +// Remove console window in Windows OS +#![windows_subsystem = "windows"] +#![allow(unknown_lints)] // May be disabled, but locally I use nightly clippy +#![allow(clippy::comparison_chain)] +#![allow(clippy::collapsible_if)] +#![allow(clippy::should_panic_without_expect)] +#![allow(clippy::struct_field_names)] // Generated code +#![allow(clippy::overly_complex_bool_expr)] // Generated code +#![allow(clippy::semicolon_if_nothing_returned)] // Generated code +#![allow(clippy::used_underscore_binding)] // Generated code +#![allow(clippy::unreadable_literal)] // Generated code +#![allow(clippy::float_cmp)] // Generated code +#![allow(clippy::no_effect_underscore_binding)] // Generated code +#![allow(clippy::uninlined_format_args)] // Generated code +#![allow(clippy::needless_pass_by_value)] // Generated code +#![allow(clippy::redundant_closure_for_method_calls)] // Generated code +#![allow(clippy::items_after_statements)] // Generated code +#![allow(clippy::match_same_arms)] // Generated code + +mod common; +mod connect_delete; +mod connect_directories_changes; +mod connect_open; +mod connect_progress_receiver; +mod connect_scan; +mod connect_show_preview; +mod connect_stop; +mod connect_translation; +mod localizer_krokiet; +mod set_initial_gui_info; +mod settings; + +use crossbeam_channel::{unbounded, Receiver, Sender}; +// use std::rc::Rc; + +use crate::connect_delete::connect_delete_button; +use crate::connect_open::connect_open_items; +use crate::connect_scan::connect_scan_button; + +use crate::connect_directories_changes::connect_add_remove_directories; +use crate::connect_progress_receiver::connect_progress_gathering; +use crate::connect_show_preview::connect_show_preview; +use crate::connect_stop::connect_stop_button; +use crate::connect_translation::connect_translations; +use crate::set_initial_gui_info::set_initial_gui_infos; +use crate::settings::{connect_changing_settings_preset, create_default_settings_files, load_settings_from_file, save_all_settings_to_file}; +use czkawka_core::common::{print_version_mode, setup_logger}; +use czkawka_core::common_dir_traversal::ProgressData; +// use slint::{ModelRc, VecModel}; + +slint::include_modules!(); +fn main() { + setup_logger(false); + print_version_mode(); + + let app = MainWindow::new().unwrap(); + + let (progress_sender, progress_receiver): (Sender, Receiver) = unbounded(); + let (stop_sender, stop_receiver): (Sender<()>, Receiver<()>) = unbounded(); + + // to_remove_debug(&app); + + set_initial_gui_infos(&app); + + create_default_settings_files(); + load_settings_from_file(&app); + + connect_delete_button(&app); + connect_scan_button(&app, progress_sender, stop_receiver); + connect_stop_button(&app, stop_sender); + connect_open_items(&app); + connect_progress_gathering(&app, progress_receiver); + connect_add_remove_directories(&app); + connect_show_preview(&app); + connect_translations(&app); + connect_changing_settings_preset(&app); + + app.run().unwrap(); + + save_all_settings_to_file(&app); +} + +// // TODO remove this after debugging - or leave commented +// pub fn to_remove_debug(app: &MainWindow) { +// app.set_empty_folder_model(to_remove_create_without_header("@@").into()); +// app.set_empty_files_model(to_remove_create_without_header("%%").into()); +// app.set_similar_images_model(to_remove_create_with_header().into()); +// } + +// fn to_remove_create_with_header() -> Rc> { +// let header_row_data: Rc> = Rc::new(VecModel::default()); +// for r in 0..10_000 { +// let items = VecModel::default(); +// +// for c in 0..3 { +// items.push(slint::format!("Item {r}.{c}")); +// } +// +// let is_header = r % 3 == 0; +// let is_checked = (r % 2 == 0) && !is_header; +// +// let item = MainListModel { +// checked: is_checked, +// header_row: is_header, +// selected_row: false, +// val: ModelRc::new(items), +// }; +// +// header_row_data.push(item); +// } +// header_row_data +// } +// fn to_remove_create_without_header(s: &str) -> Rc> { +// let non_header_row_data: Rc> = Rc::new(VecModel::default()); +// for r in 0..100_000 { +// let items = VecModel::default(); +// +// for c in 0..3 { +// items.push(slint::format!("Item {r}.{c}.{s}")); +// } +// +// let is_checked = r % 2 == 0; +// +// let item = MainListModel { +// checked: is_checked, +// header_row: false, +// selected_row: false, +// val: ModelRc::new(items), +// }; +// +// non_header_row_data.push(item); +// } +// non_header_row_data +// } diff --git a/krokiet/src/set_initial_gui_info.rs b/krokiet/src/set_initial_gui_info.rs new file mode 100644 index 0000000..0dc9e42 --- /dev/null +++ b/krokiet/src/set_initial_gui_info.rs @@ -0,0 +1,32 @@ +use czkawka_core::common::get_available_threads; +use slint::{ComponentHandle, SharedString, VecModel}; + +use crate::settings::{ALLOWED_HASH_SIZE_VALUES, ALLOWED_HASH_TYPE_VALUES, ALLOWED_RESIZE_ALGORITHM_VALUES}; +use crate::GuiState; +use crate::MainWindow; +use crate::Settings; + +// Some info needs to be send to gui at the start like available thread number in OS. +// +pub fn set_initial_gui_infos(app: &MainWindow) { + let threads = get_available_threads(); + let settings = app.global::(); + app.global::().set_maximum_threads(threads as f32); + + let available_hash_size: Vec = ALLOWED_HASH_SIZE_VALUES + .iter() + .map(|(hash_size, _max_similarity)| hash_size.to_string().into()) + .collect::>(); + let available_resize_algorithm: Vec = ALLOWED_RESIZE_ALGORITHM_VALUES + .iter() + .map(|(_settings_key, gui_name, _filter_type)| (*gui_name).into()) + .collect::>(); + let available_hash_type: Vec = ALLOWED_HASH_TYPE_VALUES + .iter() + .map(|(_settings_key, gui_name, _hash_type)| (*gui_name).into()) + .collect::>(); + + settings.set_similar_images_sub_available_hash_size(VecModel::from_slice(&available_hash_size)); + settings.set_similar_images_sub_available_resize_algorithm(VecModel::from_slice(&available_resize_algorithm)); + settings.set_similar_images_sub_available_hash_type(VecModel::from_slice(&available_hash_type)); +} diff --git a/krokiet/src/settings.rs b/krokiet/src/settings.rs new file mode 100644 index 0000000..1a5bc33 --- /dev/null +++ b/krokiet/src/settings.rs @@ -0,0 +1,578 @@ +use std::cmp::{max, min}; +use std::env; +use std::path::PathBuf; + +use directories_next::ProjectDirs; +use home::home_dir; +use image_hasher::{FilterType, HashAlg}; +use log::{debug, error, info, warn}; +use serde::{Deserialize, Serialize}; +use slint::{ComponentHandle, Model, ModelRc}; + +use czkawka_core::common::{get_available_threads, set_number_of_threads}; +use czkawka_core::common_items::{DEFAULT_EXCLUDED_DIRECTORIES, DEFAULT_EXCLUDED_ITEMS}; + +use crate::common::{create_string_standard_list_view_from_pathbuf, create_vec_model_from_vec_string}; +use crate::{Callabler, MainWindow}; +use crate::{GuiState, Settings}; + +pub const DEFAULT_MINIMUM_SIZE_KB: i32 = 16; +pub const DEFAULT_MAXIMUM_SIZE_KB: i32 = i32::MAX / 1024; +pub const DEFAULT_MINIMUM_CACHE_SIZE: i32 = 256; +pub const DEFAULT_MINIMUM_PREHASH_CACHE_SIZE: i32 = 256; + +// (Hash size, Maximum difference) - Ehh... to simplify it, just use everywhere 40 as maximum similarity - for now I'm to lazy to change it, when hash size changes +// So if you want to change it, you need to change it in multiple places +pub const ALLOWED_HASH_SIZE_VALUES: &[(u8, u8)] = &[(8, 40), (16, 40), (32, 40), (64, 40)]; + +pub const ALLOWED_RESIZE_ALGORITHM_VALUES: &[(&str, &str, FilterType)] = &[ + ("lanczos3", "Lanczos3", FilterType::Lanczos3), + ("gaussian", "Gaussian", FilterType::Gaussian), + ("catmullrom", "CatmullRom", FilterType::CatmullRom), + ("triangle", "Triangle", FilterType::Triangle), + ("nearest", "Nearest", FilterType::Nearest), +]; + +pub const ALLOWED_HASH_TYPE_VALUES: &[(&str, &str, HashAlg)] = &[ + ("mean", "Mean", HashAlg::Mean), + ("gradient", "Gradient", HashAlg::Gradient), + ("blockhash", "BlockHash", HashAlg::Blockhash), + ("vertgradient", "VertGradient", HashAlg::VertGradient), + ("doublegradient", "DoubleGradient", HashAlg::DoubleGradient), +]; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SettingsCustom { + #[serde(default = "default_included_directories")] + pub included_directories: Vec, + #[serde(default = "default_excluded_directories")] + pub excluded_directories: Vec, + #[serde(default = "default_excluded_items")] + pub excluded_items: String, + #[serde(default)] + pub allowed_extensions: String, + #[serde(default = "minimum_file_size")] + pub minimum_file_size: i32, + #[serde(default = "maximum_file_size")] + pub maximum_file_size: i32, + #[serde(default = "ttrue")] + pub recursive_search: bool, + #[serde(default = "ttrue")] + pub use_cache: bool, + #[serde(default)] + pub save_also_as_json: bool, + #[serde(default)] + pub move_deleted_files_to_trash: bool, + #[serde(default)] + pub ignore_other_file_systems: bool, + #[serde(default)] + pub thread_number: i32, + #[serde(default = "ttrue")] + pub duplicate_image_preview: bool, + #[serde(default = "ttrue")] + pub duplicate_hide_hard_links: bool, + #[serde(default = "ttrue")] + pub duplicate_use_prehash: bool, + #[serde(default = "minimal_hash_cache_size")] + pub duplicate_minimal_hash_cache_size: i32, + #[serde(default = "minimal_prehash_cache_size")] + pub duplicate_minimal_prehash_cache_size: i32, + #[serde(default = "ttrue")] + pub duplicate_delete_outdated_entries: bool, + #[serde(default = "ttrue")] + pub similar_images_show_image_preview: bool, + #[serde(default = "ttrue")] + pub similar_images_delete_outdated_entries: bool, + #[serde(default = "ttrue")] + pub similar_videos_delete_outdated_entries: bool, + #[serde(default = "ttrue")] + pub similar_music_delete_outdated_entries: bool, + #[serde(default = "default_sub_hash_size")] + pub similar_images_sub_hash_size: u8, + #[serde(default = "default_hash_type")] + pub similar_images_sub_hash_type: String, + #[serde(default = "default_resize_algorithm")] + pub similar_images_sub_resize_algorithm: String, + #[serde(default)] + pub similar_images_sub_ignore_same_size: bool, + #[serde(default = "default_similarity")] + pub similar_images_sub_similarity: i32, +} + +pub fn default_similarity() -> i32 { + 10 +} + +impl Default for SettingsCustom { + fn default() -> Self { + serde_json::from_str("{}").unwrap() + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BasicSettings { + #[serde(default = "default_language")] + pub language: String, + #[serde(default)] + pub default_preset: i32, + #[serde(default = "default_preset_names")] + pub preset_names: Vec, +} + +impl Default for BasicSettings { + fn default() -> Self { + serde_json::from_str("{}").unwrap() + } +} + +pub fn connect_changing_settings_preset(app: &MainWindow) { + let a = app.as_weak(); + app.global::().on_changed_settings_preset(move || { + let app = a.upgrade().unwrap(); + let current_item = app.global::().get_settings_preset_idx(); + let loaded_data = load_data_from_file::(get_config_file(current_item)); + match loaded_data { + Ok(loaded_data) => { + set_settings_to_gui(&app, &loaded_data); + app.set_text_summary_text(format!("Changed and loaded properly preset {}", current_item + 1).into()); + } + Err(e) => { + set_settings_to_gui(&app, &SettingsCustom::default()); + app.set_text_summary_text(format!("Cannot change and load preset {} - reason {e}", current_item + 1).into()); + } + } + }); + let a = app.as_weak(); + app.global::().on_save_current_preset(move || { + let app = a.upgrade().unwrap(); + let settings = app.global::(); + let current_item = settings.get_settings_preset_idx(); + let result = save_data_to_file(get_config_file(current_item), &collect_settings(&app)); + match result { + Ok(()) => { + app.set_text_summary_text(format!("Saved preset {}", current_item + 1).into()); + } + Err(e) => { + app.set_text_summary_text(format!("Cannot save preset {} - reason {e}", current_item + 1).into()); + error!("{e}"); + } + } + }); + let a = app.as_weak(); + app.global::().on_reset_current_preset(move || { + let app = a.upgrade().unwrap(); + let settings = app.global::(); + let current_item = settings.get_settings_preset_idx(); + set_settings_to_gui(&app, &SettingsCustom::default()); + app.set_text_summary_text(format!("Reset preset {}", current_item + 1).into()); + }); + let a = app.as_weak(); + app.global::().on_load_current_preset(move || { + let app = a.upgrade().unwrap(); + let settings = app.global::(); + let current_item = settings.get_settings_preset_idx(); + let loaded_data = load_data_from_file::(get_config_file(current_item)); + match loaded_data { + Ok(loaded_data) => { + set_settings_to_gui(&app, &loaded_data); + app.set_text_summary_text(format!("Loaded preset {}", current_item + 1).into()); + } + Err(e) => { + set_settings_to_gui(&app, &SettingsCustom::default()); + let err_message = format!("Cannot load preset {} - reason {e}", current_item + 1); + app.set_text_summary_text(err_message.into()); + error!("{e}"); + } + } + }); +} + +pub fn create_default_settings_files() { + let base_config_file = get_base_config_file(); + if let Some(base_config_file) = base_config_file { + if !base_config_file.is_file() { + let _ = save_data_to_file(Some(base_config_file), &BasicSettings::default()); + } + } + + for i in 1..=10 { + let config_file = get_config_file(i); + if let Some(config_file) = config_file { + if !config_file.is_file() { + let _ = save_data_to_file(Some(config_file), &SettingsCustom::default()); + } + } + } +} + +pub fn load_settings_from_file(app: &MainWindow) { + let result_base_settings = load_data_from_file::(get_base_config_file()); + + let mut base_settings; + if let Ok(base_settings_temp) = result_base_settings { + base_settings = base_settings_temp; + } else { + info!("Cannot load base settings, using default instead"); + base_settings = BasicSettings::default(); + } + + let results_custom_settings = load_data_from_file::(get_config_file(base_settings.default_preset)); + + let mut custom_settings; + if let Ok(custom_settings_temp) = results_custom_settings { + custom_settings = custom_settings_temp; + } else { + info!("Cannot load custom settings, using default instead"); + custom_settings = SettingsCustom::default(); + } + + // Validate here values and set "proper" + // preset_names should have 10 items + if base_settings.preset_names.len() > 10 { + base_settings.preset_names.truncate(10); + } else if base_settings.preset_names.len() < 10 { + while base_settings.preset_names.len() < 10 { + base_settings.preset_names.push(format!("Preset {}", base_settings.preset_names.len() + 1)); + } + } + base_settings.default_preset = max(min(base_settings.default_preset, 9), 0); + custom_settings.thread_number = max(min(custom_settings.thread_number, get_available_threads() as i32), 0); + + // Ended validating + set_settings_to_gui(app, &custom_settings); + set_base_settings_to_gui(app, &base_settings); + set_number_of_threads(custom_settings.thread_number as usize); +} + +pub fn save_all_settings_to_file(app: &MainWindow) { + save_base_settings_to_file(app); + save_custom_settings_to_file(app); +} + +pub fn save_base_settings_to_file(app: &MainWindow) { + let result = save_data_to_file(get_base_config_file(), &collect_base_settings(app)); + + if let Err(e) = result { + error!("{e}"); + } +} + +pub fn save_custom_settings_to_file(app: &MainWindow) { + let current_item = app.global::().get_settings_preset_idx(); + let result = save_data_to_file(get_config_file(current_item), &collect_settings(app)); + + if let Err(e) = result { + error!("{e}"); + } +} + +pub fn load_data_from_file(config_file: Option) -> Result +where + for<'de> T: Deserialize<'de>, +{ + let current_time = std::time::Instant::now(); + let Some(config_file) = config_file else { + return Err("Cannot get config file".into()); + }; + if !config_file.is_file() { + return Err("Config file doesn't exists".into()); + } + + let result = match std::fs::read_to_string(&config_file) { + Ok(serialized) => match serde_json::from_str(&serialized) { + Ok(custom_settings) => Ok(custom_settings), + Err(e) => Err(format!("Cannot deserialize settings: {e}")), + }, + Err(e) => Err(format!("Cannot read config file: {e}")), + }; + + debug!("Loading data from file {:?} took {:?}", config_file, current_time.elapsed()); + + result +} + +pub fn save_data_to_file(config_file: Option, serializable_data: &T) -> Result<(), String> +where + T: Serialize, +{ + let current_time = std::time::Instant::now(); + let Some(config_file) = config_file else { + return Err("Cannot get config file".into()); + }; + // Create dirs if not exists + if let Some(parent) = config_file.parent() { + if let Err(e) = std::fs::create_dir_all(parent) { + return Err(format!("Cannot create config folder: {e}")); + } + } + + match serde_json::to_string_pretty(&serializable_data) { + Ok(serialized) => { + if let Err(e) = std::fs::write(&config_file, serialized) { + return Err(format!("Cannot save config file: {e}")); + } + } + Err(e) => { + return Err(format!("Cannot serialize settings: {e}")); + } + } + + debug!("Saving data to file {:?} took {:?}", config_file, current_time.elapsed()); + Ok(()) +} + +pub fn get_base_config_file() -> Option { + let configs = ProjectDirs::from("pl", "Qarmin", "Krokiet")?; + let config_folder = configs.config_dir(); + let base_config_file = config_folder.join("config_general.json"); + Some(base_config_file) +} +pub fn get_config_file(number: i32) -> Option { + let configs = ProjectDirs::from("pl", "Qarmin", "Krokiet")?; + let config_folder = configs.config_dir(); + let config_file = config_folder.join(format!("config_preset_{number}.json")); + Some(config_file) +} + +pub fn set_base_settings_to_gui(app: &MainWindow, basic_settings: &BasicSettings) { + let settings = app.global::(); + // settings.set_language(basic_settings.language.clone()); + settings.set_settings_preset_idx(basic_settings.default_preset); + settings.set_settings_presets(ModelRc::new(create_vec_model_from_vec_string(basic_settings.preset_names.clone()))); +} +pub fn set_settings_to_gui(app: &MainWindow, custom_settings: &SettingsCustom) { + let settings = app.global::(); + + // Included directories + let included_directories = create_string_standard_list_view_from_pathbuf(&custom_settings.included_directories); + settings.set_included_directories(included_directories); + + // Excluded directories + let excluded_directories = create_string_standard_list_view_from_pathbuf(&custom_settings.excluded_directories); + settings.set_excluded_directories(excluded_directories); + + settings.set_excluded_items(custom_settings.excluded_items.clone().into()); + settings.set_allowed_extensions(custom_settings.allowed_extensions.clone().into()); + settings.set_minimum_file_size(custom_settings.minimum_file_size.to_string().into()); + settings.set_maximum_file_size(custom_settings.maximum_file_size.to_string().into()); + settings.set_use_cache(custom_settings.use_cache); + settings.set_save_as_json(custom_settings.save_also_as_json); + settings.set_move_to_trash(custom_settings.move_deleted_files_to_trash); + settings.set_ignore_other_filesystems(custom_settings.ignore_other_file_systems); + settings.set_thread_number(custom_settings.thread_number as f32); + + settings.set_recursive_search(custom_settings.recursive_search); + settings.set_duplicate_image_preview(custom_settings.duplicate_image_preview); + settings.set_duplicate_hide_hard_links(custom_settings.duplicate_hide_hard_links); + settings.set_duplicate_use_prehash(custom_settings.duplicate_use_prehash); + settings.set_duplicate_minimal_hash_cache_size(custom_settings.duplicate_minimal_hash_cache_size.to_string().into()); + settings.set_duplicate_minimal_prehash_cache_size(custom_settings.duplicate_minimal_prehash_cache_size.to_string().into()); + settings.set_duplicate_delete_outdated_entries(custom_settings.duplicate_delete_outdated_entries); + settings.set_similar_images_show_image_preview(custom_settings.similar_images_show_image_preview); + settings.set_similar_images_delete_outdated_entries(custom_settings.similar_images_delete_outdated_entries); + settings.set_similar_videos_delete_outdated_entries(custom_settings.similar_videos_delete_outdated_entries); + settings.set_similar_music_delete_outdated_entries(custom_settings.similar_music_delete_outdated_entries); + + let similar_images_sub_hash_size_idx = if let Some(idx) = ALLOWED_HASH_SIZE_VALUES + .iter() + .position(|(hash_size, _max_similarity)| *hash_size == custom_settings.similar_images_sub_hash_size) + { + idx + } else { + warn!( + "Value of hash size \"{}\" is invalid, setting it to default value", + custom_settings.similar_images_sub_hash_size + ); + 0 + }; + settings.set_similar_images_sub_hash_size_index(similar_images_sub_hash_size_idx as i32); + + let similar_images_sub_hash_type_idx = if let Some(idx) = ALLOWED_HASH_TYPE_VALUES + .iter() + .position(|(settings_key, _gui_name, _hash_type)| *settings_key == custom_settings.similar_images_sub_hash_type) + { + idx + } else { + warn!( + "Value of hash type \"{}\" is invalid, setting it to default value", + custom_settings.similar_images_sub_hash_type + ); + 0 + }; + settings.set_similar_images_sub_hash_type_index(similar_images_sub_hash_type_idx as i32); + + let similar_images_sub_resize_algorithm_idx = if let Some(idx) = ALLOWED_RESIZE_ALGORITHM_VALUES + .iter() + .position(|(settings_key, _gui_name, _resize_alg)| *settings_key == custom_settings.similar_images_sub_resize_algorithm) + { + idx + } else { + warn!( + "Value of resize algorithm \"{}\" is invalid, setting it to default value", + custom_settings.similar_images_sub_resize_algorithm + ); + 0 + }; + settings.set_similar_images_sub_resize_algorithm_index(similar_images_sub_resize_algorithm_idx as i32); + + settings.set_similar_images_sub_ignore_same_size(custom_settings.similar_images_sub_ignore_same_size); + settings.set_similar_images_sub_max_similarity(40.0); // TODO this is now set to stable 40 + settings.set_similar_images_sub_current_similarity(custom_settings.similar_images_sub_similarity as f32); + + // Clear text + app.global::().set_info_text("".into()); +} + +pub fn collect_settings(app: &MainWindow) -> SettingsCustom { + let settings = app.global::(); + + let included_directories = settings.get_included_directories(); + let included_directories = included_directories.iter().map(|x| PathBuf::from(x.text.as_str())).collect::>(); + + let excluded_directories = settings.get_excluded_directories(); + let excluded_directories = excluded_directories.iter().map(|x| PathBuf::from(x.text.as_str())).collect::>(); + + let excluded_items = settings.get_excluded_items().to_string(); + let allowed_extensions = settings.get_allowed_extensions().to_string(); + let minimum_file_size = settings.get_minimum_file_size().parse::().unwrap_or(DEFAULT_MINIMUM_SIZE_KB); + let maximum_file_size = settings.get_maximum_file_size().parse::().unwrap_or(DEFAULT_MAXIMUM_SIZE_KB); + + let recursive_search = settings.get_recursive_search(); + let use_cache = settings.get_use_cache(); + let save_also_as_json = settings.get_save_as_json(); + let move_deleted_files_to_trash = settings.get_move_to_trash(); + let ignore_other_file_systems = settings.get_ignore_other_filesystems(); + let thread_number = settings.get_thread_number().round() as i32; + + let duplicate_image_preview = settings.get_duplicate_image_preview(); + let duplicate_hide_hard_links = settings.get_duplicate_hide_hard_links(); + let duplicate_use_prehash = settings.get_duplicate_use_prehash(); + let duplicate_minimal_hash_cache_size = settings.get_duplicate_minimal_hash_cache_size().parse::().unwrap_or(DEFAULT_MINIMUM_CACHE_SIZE); + let duplicate_minimal_prehash_cache_size = settings + .get_duplicate_minimal_prehash_cache_size() + .parse::() + .unwrap_or(DEFAULT_MINIMUM_PREHASH_CACHE_SIZE); + let duplicate_delete_outdated_entries = settings.get_duplicate_delete_outdated_entries(); + + let similar_images_show_image_preview = settings.get_similar_images_show_image_preview(); + let similar_images_delete_outdated_entries = settings.get_similar_images_delete_outdated_entries(); + + let similar_videos_delete_outdated_entries = settings.get_similar_videos_delete_outdated_entries(); + + let similar_music_delete_outdated_entries = settings.get_similar_music_delete_outdated_entries(); + + let similar_images_sub_hash_size_idx = settings.get_similar_images_sub_hash_size_index(); + let similar_images_sub_hash_size = ALLOWED_HASH_SIZE_VALUES[similar_images_sub_hash_size_idx as usize].0; + + let similar_images_sub_hash_type_idx = settings.get_similar_images_sub_hash_type_index(); + let similar_images_sub_hash_type = ALLOWED_HASH_TYPE_VALUES[similar_images_sub_hash_type_idx as usize].0.to_string(); + + let similar_images_sub_resize_algorithm_idx = settings.get_similar_images_sub_resize_algorithm_index(); + let similar_images_sub_resize_algorithm = ALLOWED_RESIZE_ALGORITHM_VALUES[similar_images_sub_resize_algorithm_idx as usize].0.to_string(); + + let similar_images_sub_ignore_same_size = settings.get_similar_images_sub_ignore_same_size(); + let similar_images_sub_similarity = settings.get_similar_images_sub_current_similarity().round() as i32; + SettingsCustom { + included_directories, + excluded_directories, + excluded_items, + allowed_extensions, + minimum_file_size, + maximum_file_size, + recursive_search, + use_cache, + save_also_as_json, + move_deleted_files_to_trash, + ignore_other_file_systems, + thread_number, + duplicate_image_preview, + duplicate_hide_hard_links, + duplicate_use_prehash, + duplicate_minimal_hash_cache_size, + duplicate_minimal_prehash_cache_size, + duplicate_delete_outdated_entries, + similar_images_show_image_preview, + similar_images_delete_outdated_entries, + similar_videos_delete_outdated_entries, + similar_music_delete_outdated_entries, + similar_images_sub_hash_size, + similar_images_sub_hash_type, + similar_images_sub_resize_algorithm, + similar_images_sub_ignore_same_size, + similar_images_sub_similarity, + } +} + +pub fn collect_base_settings(app: &MainWindow) -> BasicSettings { + let settings = app.global::(); + + let default_preset = settings.get_settings_preset_idx(); + let preset_names = settings.get_settings_presets().iter().map(|x| x.to_string()).collect::>(); + + assert_eq!(preset_names.len(), 10); + BasicSettings { + language: "en".to_string(), + default_preset, + preset_names, + } +} + +fn default_included_directories() -> Vec { + let mut included_directories = vec![]; + if let Ok(current_dir) = env::current_dir() { + included_directories.push(current_dir.to_string_lossy().to_string()); + } else if let Some(home_dir) = home_dir() { + included_directories.push(home_dir.to_string_lossy().to_string()); + } else if cfg!(target_family = "unix") { + included_directories.push("/".to_string()); + } else { + // This could be set to default + included_directories.push("C:\\".to_string()); + }; + included_directories.sort(); + included_directories.iter().map(PathBuf::from).collect::>() +} + +fn default_excluded_directories() -> Vec { + let mut excluded_directories = DEFAULT_EXCLUDED_DIRECTORIES.iter().map(PathBuf::from).collect::>(); + excluded_directories.sort(); + excluded_directories +} + +fn default_excluded_items() -> String { + DEFAULT_EXCLUDED_ITEMS.to_string() +} + +fn default_language() -> String { + "en".to_string() +} + +fn default_preset_names() -> Vec { + (0..10).map(|x| format!("Preset {}", x + 1)).collect::>() +} + +fn minimum_file_size() -> i32 { + DEFAULT_MINIMUM_SIZE_KB +} +fn maximum_file_size() -> i32 { + DEFAULT_MAXIMUM_SIZE_KB +} +fn ttrue() -> bool { + true +} +fn minimal_hash_cache_size() -> i32 { + DEFAULT_MINIMUM_CACHE_SIZE +} +fn minimal_prehash_cache_size() -> i32 { + DEFAULT_MINIMUM_PREHASH_CACHE_SIZE +} + +pub fn default_resize_algorithm() -> String { + ALLOWED_RESIZE_ALGORITHM_VALUES[0].0.to_string() +} +pub fn default_hash_type() -> String { + ALLOWED_HASH_TYPE_VALUES[0].0.to_string() +} +pub fn default_sub_hash_size() -> u8 { + 16 +} diff --git a/krokiet/ui/action_buttons.slint b/krokiet/ui/action_buttons.slint new file mode 100644 index 0000000..996f6cb --- /dev/null +++ b/krokiet/ui/action_buttons.slint @@ -0,0 +1,120 @@ +import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox} from "std-widgets.slint"; +import {LeftSidePanel} from "left_side_panel.slint"; +import {MainList} from "main_lists.slint"; +import {CurrentTab} from "common.slint"; +import {BottomPanelVisibility} from "common.slint"; +import {Callabler} from "callabler.slint"; +import {GuiState} from "gui_state.slint"; + +export component VisibilityButton inherits Button { + in-out property button_visibility; + in-out property bottom_panel_visibility; + enabled: bottom_panel_visibility != button-visibility; + height: 30px; + width: 70px; + clicked => { + bottom-panel-visibility = button_visibility; + } +} + +export component ActionButtons inherits HorizontalLayout { + callback scan_stopping; + callback scan_starting(CurrentTab); + in-out property bottom_panel_visibility: BottomPanelVisibility.Directories; + in-out property stop_requested: false; + in-out property scanning; + in-out property lists_enabled: GuiState.active_tab != CurrentTab.Settings; + // in-out property <> + out property name; + height: 30px; + spacing: 4px; + + Rectangle { + scan_button := Button { + height: parent.height; + enabled: !scanning && lists_enabled; + visible: !scanning; + text: "Scan"; + clicked => { + root.scanning = true; + root.scan_starting(GuiState.active_tab); + } + } + + stop_button := Button { + height: parent.height; + visible: scanning; + enabled: scanning && !stop_requested && root.lists_enabled; + text: "Stop"; + clicked => { + root.scan_stopping(); + root.stop_requested = true; + } + } + } + + Rectangle { + horizontal-stretch: 0.5; + } + + delete_button := Button { + height: parent.height; + enabled: !scanning && lists_enabled; + text: "Delete"; + clicked => { + Callabler.delete_selected_items(); + } + } + + popup_item := PopupWindow { + height: root.height; + width: root.width; + close-on-click: true; + VerticalLayout { + for i[idx] in ["A","B","C"]: Rectangle { + background: red; + } + } + } + + select_button := Button { + visible: false; + height: parent.height; + enabled: !scanning && lists_enabled; + text: "Select"; + clicked => { + debug("Selected"); + popup_item.show(); + // Callabler.select_items(); + } + } + + Rectangle { + horizontal-stretch: 0.5; + } + + HorizontalLayout { + padding: 0px; + spacing: 0px; + VisibilityButton { + height: parent.height; + 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; + bottom_panel_visibility <=> bottom_panel_visibility; + text: "None"; + } + } +} diff --git a/krokiet/ui/bottom_panel.slint b/krokiet/ui/bottom_panel.slint new file mode 100644 index 0000000..f4872f5 --- /dev/null +++ b/krokiet/ui/bottom_panel.slint @@ -0,0 +1,126 @@ + +import {Button, StandardListView, VerticalBox, ScrollView, TextEdit} from "std-widgets.slint"; +import {Settings} from "settings.slint"; +import {BottomPanelVisibility} from "common.slint"; +import {Callabler} from "callabler.slint"; +import {GuiState} from "gui_state.slint"; + +component DirectoriesPanel inherits HorizontalLayout { + callback folder_choose_requested(bool); + callback show_manual_add_dialog(bool); + // Included directories + VerticalLayout { + horizontal-stretch: 0.0; + spacing: 5px; + Button { + text: "Add"; + clicked => { + folder_choose_requested(true); + } + } + + Button { + text: "Remove"; + clicked => { + Callabler.remove_item_directories(true, included-list.current-item); + } + } + + Button { + text: "Manual Add"; + clicked => { + show_manual_add_dialog(true); + } + } + + Rectangle { + vertical-stretch: 1.0; + } + } + + VerticalLayout { + horizontal-stretch: 1.0; + Rectangle { + Text { + text: "Included Directories"; + } + } + + included_list := StandardListView { + model: Settings.included-directories; + } + } + + // Excluded directories + VerticalLayout { + horizontal-stretch: 0.0; + spacing: 5px; + Button { + text: "Add"; + clicked => { + folder_choose_requested(false); + } + } + + Button { + text: "Remove"; + clicked => { + Callabler.remove_item_directories(false, excluded-list.current-item); + } + } + + Button { + text: "Manual Add"; + clicked => { + show_manual_add_dialog(false); + } + } + + Rectangle { + vertical-stretch: 1.0; + } + } + + VerticalLayout { + horizontal-stretch: 1.0; + Rectangle { + Text { + text: "Excluded Directories"; + } + } + + excluded_list := StandardListView { + model: Settings.excluded-directories; + } + } +} + +component TextErrorsPanel inherits TextEdit { + height: 20px; + read-only: true; + wrap: TextWrap.no-wrap; + text <=> GuiState.info_text; +} + +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 { + width: parent.width; + height: parent.height; + folder_choose_requested(included-directories) => { + root.folder_choose_requested(included-directories) + } + show_manual_add_dialog(included-directories) => { + root.show_manual_add_dialog(included-directories) + } + } + + if bottom-panel-visibility == BottomPanelVisibility.TextErrors: TextErrorsPanel { + width: parent.width; + height: parent.height; + } +} diff --git a/krokiet/ui/callabler.slint b/krokiet/ui/callabler.slint new file mode 100644 index 0000000..3d15168 --- /dev/null +++ b/krokiet/ui/callabler.slint @@ -0,0 +1,29 @@ +export global Callabler { + // Bottom panel operations + callback remove_item_directories(bool, int); + callback added_manual_directories(bool, string); + + // Right click or middle click opener + callback item_opened(string); + + callback delete_selected_items(); + // callback (); + + // Preview + callback load_image_preview(string); + + // Settings + callback changed_settings_preset(); + callback save_current_preset(); + callback load_current_preset(); + callback reset_current_preset(); + + // Translations + pure callback translate(string, [{key: string, value: string}]) -> string; + + // Only Slint + callback open_select_popup(); + + callback open_config_folder(); + callback open_cache_folder(); +} diff --git a/krokiet/ui/color_palette.slint b/krokiet/ui/color_palette.slint new file mode 100644 index 0000000..671c77a --- /dev/null +++ b/krokiet/ui/color_palette.slint @@ -0,0 +1,13 @@ +import { StyleMetrics } from "std-widgets.slint"; + +export global ColorPalette { + // Tabs at left side + in-out property tab_selected_color: StyleMetrics.dark-color-scheme ? #353535 : #5e5e5e; + in-out property tab_hovered_color: StyleMetrics.dark-color-scheme ? #49494926 : #80808014; + // ListView + in-out property list_view_normal_color: StyleMetrics.dark-color-scheme ? #222222 : #dddddd; + in-out property list_view_normal_header_color: StyleMetrics.dark-color-scheme ? #111111 : #888888; + in-out property list_view_normal_selected_header: StyleMetrics.dark-color-scheme ? #444444 : #cccccc; + // Popup + in-out property popup_background: StyleMetrics.dark-color-scheme ? #353535 : #5e5e5e; +} diff --git a/krokiet/ui/common.slint b/krokiet/ui/common.slint new file mode 100644 index 0000000..f9fa58d --- /dev/null +++ b/krokiet/ui/common.slint @@ -0,0 +1,30 @@ +export enum CurrentTab { + EmptyFolders, + EmptyFiles, + SimilarImages, + Settings +} + +export enum TypeOfOpenedItem { + CurrentItem, + ParentItem, +} + +export struct ProgressToSend { + current_progress: int, + all_progress: int, + step_name: string, +} + +export struct MainListModel { + checked: bool, + header_row: bool, + selected_row: bool, + val: [string] +} + +export enum BottomPanelVisibility { + NotVisible, + TextErrors, + Directories +} \ No newline at end of file diff --git a/krokiet/ui/gui_state.slint b/krokiet/ui/gui_state.slint new file mode 100644 index 0000000..97570ba --- /dev/null +++ b/krokiet/ui/gui_state.slint @@ -0,0 +1,19 @@ +import {CurrentTab} from "common.slint"; + +// State to show +export global GuiState { + in-out property app_width; + in-out property app_height; + + in-out property info_text: "Nothing to report"; + in-out property preview_visible; + in-out property preview_image; + + in-out property maximum_threads: 40; + + in-out property choosing_include_directories; + in-out property visible_tool_settings; + + in-out property available_subsettings: active_tab == CurrentTab.SimilarImages; + in-out property active_tab: CurrentTab.EmptyFiles; +} diff --git a/krokiet/ui/left_side_panel.slint b/krokiet/ui/left_side_panel.slint new file mode 100644 index 0000000..7521ef7 --- /dev/null +++ b/krokiet/ui/left_side_panel.slint @@ -0,0 +1,139 @@ +import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox} from "std-widgets.slint"; +import {CurrentTab} from "common.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; + +component TabItem { + in property scanning; + in property text; + in property curr_tab; + callback changed_current_tab(); + + Rectangle { + width: parent.width; + horizontal-stretch: 1.0; + background: touch-area.has-hover ? ColorPalette.tab-hovered-color : transparent; + touch_area := TouchArea { + clicked => { + if (GuiState.active_tab == root.curr-tab) { + return; + } + GuiState.active_tab = root.curr-tab; + changed_current_tab(); + } + } + } + + HorizontalLayout { + width: parent.width; + alignment: LayoutAlignment.end; + layout_rectangle := VerticalLayout { + empty_rectangle := Rectangle { } + + current_rectangle := Rectangle { + visible: (GuiState.active_tab == root.curr-tab); + border-radius: 2px; + width: 5px; + height: 0px; + background: ColorPalette.tab_selected_color; + animate height{ + duration: 150ms; + easing: ease; + } + } + empty_rectangle2 := Rectangle { } + } + } + + Text { + text: root.text; + width: parent.width; + horizontal-alignment: center; + } + + states [ + 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: { + current_rectangle.height: 0px; + } + ] +} + +export component LeftSidePanel { + in-out property scanning; + callback changed_current_tab(); + width: 120px; + VerticalLayout { + spacing: 20px; + Rectangle { + height: 100px; + Image { + width: root.width; + source: @image-url("../icons/logo.png"); + } + } + + VerticalLayout { + // spacing: 3px; + alignment: center; + out property element-size: 25px; + TabItem { + height: parent.element-size; + scanning: scanning; + text: "Empty Folders"; + curr_tab: CurrentTab.EmptyFolders; + changed_current_tab() => {root.changed_current_tab();} + } + + TabItem { + height: parent.element-size; + scanning: scanning; + text: "Empty Files"; + curr_tab: CurrentTab.EmptyFiles; + changed_current_tab() => {root.changed_current_tab();} + } + + TabItem { + height: parent.element-size; + scanning: scanning; + text: "Similar Images"; + curr_tab: CurrentTab.SimilarImages; + changed_current_tab() => {root.changed_current_tab();} + } + } + + Rectangle { + HorizontalLayout { + alignment: start; + Button { + enabled: GuiState.active_tab != CurrentTab.Settings && GuiState.available_subsettings; + min-width: 20px; + min-height: 20px; + max-height: self.width; + preferred-height: self.width; + icon: @image-url("../icons/settings.svg"); + clicked => { + GuiState.visible_tool_settings = !GuiState.visible-tool-settings; + } + } + } + HorizontalLayout { + alignment: end; + Button { + enabled: GuiState.active_tab != CurrentTab.Settings; + min-width: 20px; + min-height: 20px; + max-height: self.width; + preferred-height: self.width; + icon: @image-url("../icons/settings.svg"); + clicked => { + GuiState.active_tab = CurrentTab.Settings; + root.changed_current_tab(); + } + } + } + } + } +} diff --git a/krokiet/ui/main_lists.slint b/krokiet/ui/main_lists.slint new file mode 100644 index 0000000..fd68a2f --- /dev/null +++ b/krokiet/ui/main_lists.slint @@ -0,0 +1,83 @@ +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 {CurrentTab, TypeOfOpenedItem} from "common.slint"; +import {MainListModel} from "common.slint"; +import {SettingsList} from "settings_list.slint"; +import {GuiState} from "gui_state.slint"; + +export component MainList { + in-out property <[MainListModel]> empty_folder_model: [ + {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + ]; + in-out property <[MainListModel]> empty_files_model; + in-out property <[MainListModel]> similar_images_model; + callback changed_current_tab(); + callback released_key(string); + + empty_folders := SelectableTableView { + visible: GuiState.active_tab == CurrentTab.EmptyFolders; + min-width: 200px; + height: parent.height; + columns: ["Selection", "Folder Name", "Path", "Modification Date"]; + column-sizes: [35px, 100px, 350px, 150px]; + values <=> empty-folder-model; + parentPathIdx: 2; + fileNameIdx: 1; + } + + empty_files := SelectableTableView { + visible: GuiState.active_tab == CurrentTab.EmptyFiles; + min-width: 200px; + height: parent.height; + columns: ["Selection", "File Name", "Path", "Modification Date"]; + column-sizes: [35px, 100px, 350px, 150px]; + values <=> empty-files-model; + parentPathIdx: 2; + fileNameIdx: 1; + } + + similar_images := SelectableTableView { + visible: GuiState.active_tab == CurrentTab.SimilarImages; + min-width: 200px; + height: parent.height; + columns: ["Selection", "Similarity", "Size", "Dimensions", "File Name", "Path", "Modification Date"]; + column-sizes: [35px, 80px, 80px, 80px, 100px, 350px, 150px]; + values <=> similar-images-model; + parentPathIdx: 5; + fileNameIdx: 4; + } + + settings_list := SettingsList { + visible: GuiState.active_tab == CurrentTab.Settings; + } + + focus_item := FocusScope { + 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) => { + if (!self.visible || !self.has-focus) { + return accept; + } + if (GuiState.active_tab == CurrentTab.EmptyFiles) { + empty_files.released_key(event); + } else if (GuiState.active_tab == CurrentTab.EmptyFolders) { + empty-folders.released_key(event); + } else if (GuiState.active_tab == CurrentTab.SimilarImages) { + similar-images.released_key(event); + } else { + debug("Non handled key in main_lists.slint"); + } + accept + } + } + changed_current_tab() => { + empty_folders.deselect_selected_item(); + empty_files.deselect_selected_item(); + similar_images.deselect_selected_item(); + } +} diff --git a/krokiet/ui/main_window.slint b/krokiet/ui/main_window.slint new file mode 100644 index 0000000..c22e210 --- /dev/null +++ b/krokiet/ui/main_window.slint @@ -0,0 +1,156 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} 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, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; +import {PopupNewDirectories} from "popup_new_directories.slint"; +import { PopupSelect } from "popup_select.slint"; +import { ToolSettings } from "tool_settings.slint"; + +export {Settings, Callabler, GuiState} + +export component MainWindow inherits Window { + callback scan_stopping; + callback scan_starting(CurrentTab); + callback folder_choose_requested(bool); + callback scan_ended(string); + + min-width: 300px; + preferred-width: 800px; + min-height: 300px; + preferred-height: 600px; + + in-out property text_summary_text: ""; + in-out property stop_requested: false; + in-out property scanning: false; + in-out property progress_datas: { + current_progress: 15, + all_progress: 20, + step_name: "Cache", + }; + in-out property <[MainListModel]> empty_folder_model: [ + {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + ]; + in-out property <[MainListModel]> empty_files_model: [ + {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + ]; + in-out property <[MainListModel]> similar_images_model: []; + + VerticalBox { + HorizontalBox { + vertical-stretch: 1.0; + preferred-height: 300px; + LeftSidePanel { + horizontal-stretch: 0.0; + scanning <=> root.scanning; + changed_current_tab() => { + GuiState.preview_visible = false; + main_list.changed_current_tab(); + } + } + + VerticalLayout { + horizontal-stretch: 1.0; + min_width: 300px; + Rectangle { + vertical-stretch: 1.0; + main_list := MainList { + x: 0; + width: preview_or_tool_settings.visible ? parent.width / 2 : parent.width; + height: parent.height; + horizontal-stretch: 0.5; + empty_folder_model <=> root.empty_folder_model; + empty_files_model <=> root.empty_files_model; + similar_images_model <=> root.similar_images_model; + } + preview_or_tool_settings := Rectangle { + visible: (GuiState.preview_visible || tool_settings.visible) && GuiState.active_tab != CurrentTab.Settings; + height: parent.height; + x: parent.width / 2; + width: self.visible ? parent.width / 2 : 0; + Preview { + height: parent.height; + width: parent.width; + visible: GuiState.preview_visible && !tool_settings.visible; + source: GuiState.preview_image; + image-fit: ImageFit.contain; + } + tool_settings := ToolSettings { + height: parent.height; + width: parent.width; + visible: GuiState.visible_tool_settings && GuiState.available_subsettings; + } + } + } + + if root.scanning: Progress { + horizontal-stretch: 0.0; + progress_datas <=> root.progress_datas; + } + } + } + + action_buttons := ActionButtons { + vertical-stretch: 0.0; + scanning <=> root.scanning; + stop_requested <=> root.stop-requested; + scan_stopping => { + text_summary_text = "Stopping scan, please wait..."; + root.scan_stopping(); + } + scan_starting(item) => { + text_summary_text = "Searching..."; + root.scan_starting(item); + } + } + + text_summary := LineEdit { + text: text_summary_text; + read-only: true; + } + + bottom_panel := BottomPanel { + bottom-panel-visibility <=> action_buttons.bottom_panel_visibility; + vertical-stretch: 0.0; + folder_choose_requested(included_directories) => { + root.folder_choose_requested(included_directories) + } + show_manual_add_dialog(included_directories) => { + GuiState.choosing_include_directories = included_directories; + new_directory_popup_window.show_popup() + } + } + } + + new_directory_popup_window := PopupNewDirectories { + height: root.height; + width: root.width; + } + + // select_popup_window := PopupSelect { + // height: root.height; + // width: root.width; + // } + + + scan_ended(scan_text) => { + text_summary_text = scan_text; + root.scanning = false; + root.stop_requested = false; + } +} diff --git a/krokiet/ui/popup_new_directories.slint b/krokiet/ui/popup_new_directories.slint new file mode 100644 index 0000000..a920dbf --- /dev/null +++ b/krokiet/ui/popup_new_directories.slint @@ -0,0 +1,86 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} 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, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import { ColorPalette } from "color_palette.slint"; +import { GuiState } from "gui_state.slint"; +import { Preview } from "preview.slint"; + +export component PopupNewDirectories inherits Rectangle { + width: 400px; + height: 400px; + + callback show_popup(); + + popup_window := PopupWindow { + width: root.width; + height: root.height; + + property included_directories; + private property text_data; + close-on-click: false; + HorizontalLayout { + alignment: LayoutAlignment.center; + VerticalLayout { + alignment: LayoutAlignment.center; + Rectangle { + clip: true; + width: root.width - 20px; + height: root.height - 20px; + border-radius: 20px; + background: ColorPalette.popup_background; + VerticalLayout { + Text { + text: "Please add directories one per line"; + horizontal-alignment: TextHorizontalAlignment.center; + } + + TextEdit { + vertical-stretch: 1.0; + text <=> text-data; + } + + HorizontalLayout { + min-height: 20px; + Button { + enabled: text-data != ""; + text: "OK"; + clicked => { + Callabler.added_manual_directories(GuiState.choosing_include_directories, text_data); + debug("OK"); + popup_window.close(); + } + } + + Button { + text: "Cancel"; + clicked => { + debug("Cancel"); + popup_window.close(); + } + } + } + } + } + } + } + } + + // Button { + // text:"KKK"; + // clicked => { + // show-popup(); + // } + // } + + show_popup() => { + popup_window.show(); + } +} \ No newline at end of file diff --git a/krokiet/ui/popup_select.slint b/krokiet/ui/popup_select.slint new file mode 100644 index 0000000..394e9d4 --- /dev/null +++ b/krokiet/ui/popup_select.slint @@ -0,0 +1,74 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit} 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, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; + +export component PopupSelect inherits Rectangle { + callback show_popup(); + + popup_window := PopupWindow { + width: root.width; + height: root.height; + + property included_directories; + private property text_data; + close-on-click: false; + HorizontalLayout { + alignment: LayoutAlignment.center; + VerticalLayout { + alignment: LayoutAlignment.center; + Rectangle { + clip: true; + width: root.width - 20px; + height: root.height - 20px; + border-radius: 20px; + background: ColorPalette.popup_background; + VerticalLayout { + Text { + text: "Please add directories one per line"; + horizontal-alignment: TextHorizontalAlignment.center; + } + + TextEdit { + vertical-stretch: 1.0; + text <=> text-data; + } + + HorizontalLayout { + min-height: 20px; + Button { + enabled: text-data != ""; + text: "OK"; + clicked => { + Callabler.added_manual_directories(GuiState.choosing_include_directories, text_data); + popup_window.close(); + } + } + + Button { + text: "Cancel"; + clicked => { + popup_window.close(); + } + } + } + } + } + } + } + } + + show_popup() => { + popup_window.show(); + } +} \ No newline at end of file diff --git a/krokiet/ui/preview.slint b/krokiet/ui/preview.slint new file mode 100644 index 0000000..a7b4ead --- /dev/null +++ b/krokiet/ui/preview.slint @@ -0,0 +1,3 @@ +export component Preview inherits Image { + +} \ No newline at end of file diff --git a/krokiet/ui/progress.slint b/krokiet/ui/progress.slint new file mode 100644 index 0000000..2c874f5 --- /dev/null +++ b/krokiet/ui/progress.slint @@ -0,0 +1,68 @@ +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, ProgressToSend} from "common.slint"; +import { ProgressIndicator } from "std-widgets.slint"; + +export component Progress { + in-out property progress_datas; + preferred-width: 400px; + preferred-height: 40px; + VerticalLayout { + Text { + text: progress-datas.step-name; + horizontal-alignment: TextHorizontalAlignment.center; + } + + HorizontalLayout { + spacing: 5px; + VerticalLayout { + spacing: 5px; + Text { + vertical-alignment: TextVerticalAlignment.center; + text: "Current Stage:"; + } + + Text { + vertical-alignment: TextVerticalAlignment.center; + text: "All Stages:"; + } + } + + VerticalLayout { + spacing: 5px; + VerticalLayout { + alignment: LayoutAlignment.center; + ProgressIndicator { + visible: progress_datas.current-progress >= -0.001; + height: 8px; + progress: progress_datas.current-progress / 100.0; + } + } + + VerticalLayout { + alignment: LayoutAlignment.center; + ProgressIndicator { + height: 8px; + progress: progress_datas.all-progress / 100.0; + } + } + } + + VerticalLayout { + spacing: 5px; + Text { + visible: progress_datas.current-progress >= -0.001; + vertical-alignment: TextVerticalAlignment.center; + text: progress_datas.current-progress + "%"; + } + + Text { + vertical-alignment: TextVerticalAlignment.center; + text: progress_datas.all-progress + "%"; + } + } + } + } +} diff --git a/krokiet/ui/selectable_tree_view.slint b/krokiet/ui/selectable_tree_view.slint new file mode 100644 index 0000000..6d3b4c1 --- /dev/null +++ b/krokiet/ui/selectable_tree_view.slint @@ -0,0 +1,205 @@ +import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, ScrollView} from "std-widgets.slint"; +import {TypeOfOpenedItem} from "common.slint"; +import {ColorPalette} from "color_palette.slint"; +import {MainListModel} from "common.slint"; +import {Callabler} from "callabler.slint"; +import {GuiState} from "gui_state.slint"; + +export component SelectableTableView inherits Rectangle { + callback item_opened(string); + in property <[string]> columns; + in-out property <[MainListModel]> values: [ + {checked: false, selected_row: false, header_row: true, val: ["kropkarz", "/Xd1", "24.10.2023"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: false, selected_row: false, header_row: false, val: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"]} , + {checked: true, selected_row: false, header_row: false, val: ["lokkaler", "/Xd1/Vide2", "01.23.1911"]} + ]; + in-out property <[length]> column_sizes: [30px, 80px, 150px, 160px]; + 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; + in-out property selected_item: -1; + out property list_view_width: max(self.width - 20px, column_sizes[0] + column_sizes[1] + column_sizes[2] + column_sizes[3] + column_sizes[4] + column_sizes[5] + column_sizes[6] + column_sizes[7] + column_sizes[8] + column_sizes[9] + column_sizes[10] + column_sizes[11]); + + VerticalBox { + padding: 0px; + ScrollView { + height: 30px; + viewport-x <=> list_view.viewport-x; + vertical-stretch: 0; + + HorizontalLayout { + spacing: 5px; + for title [idx] in root.columns: HorizontalLayout { + width: root.column-sizes[idx]; + Text { + overflow: elide; + text: title; + } + + Rectangle { + width: 1px; + background: gray; + TouchArea { + width: 5px; + x: (parent.width - self.width) / 2; + property cached; + pointer-event(event) => { + if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { + self.cached = root.column_sizes[idx]; + } + } + moved => { + if (self.pressed) { + root.column_sizes[idx] += (self.mouse-x - self.pressed-x); + if (root.column_sizes[idx] < 20px) { + root.column_sizes[idx] = 20px; + } + } + } + mouse-cursor: ew-resize; + } + } + } + } + } + + list_view := ListView { + padding: 0px; + min-width: 100px; + for r [idx] in root.values: Rectangle { + width: list_view_width; + + 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)); + touch_area := TouchArea { + clicked => { + if (!r.header_row) { + r.selected_row = !r.selected_row; + if (root.selected-item == -1) { + root.selected-item = idx; + } else { + if (r.selected_row == true) { + root.values[root.selected-item].selected_row = false; + root.selected-item = idx; + } else { + root.selected-item = -1; + } + } + + if (root.selected_item != -1) { + Callabler.load_image_preview(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]); + } else { + GuiState.preview_visible = false; + } + } + } + pointer-event(event) => { + // TODO this should be clicked by double-click + if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) { + Callabler.item_opened(r.val[root.parentPathIdx - 1]) + } else if (event.button == PointerEventButton.middle && event.kind == PointerEventKind.up) { + Callabler.item_opened(r.val[root.parentPathIdx - 1] + "/" + r.val[root.fileNameIdx - 1]) + } + } + } + + HorizontalLayout { + CheckBox { + visible: !r.header-row; + checked: r.checked && !r.header-row; + width: root.column-sizes[0]; + toggled => { + r.checked = self.checked; + } + } + + HorizontalLayout { + spacing: 5px; + for f [idx] in r.val: Text { + width: root.column-sizes[idx + 1]; + text: f; + font-size: 12px; + vertical-alignment: center; + overflow: elide; + } + } + } + } + } + } + + public function deselect_selected_item() { + if (root.selected_item != -1) { + root.values[root.selected-item].selected_row = false; + root.selected-item = -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) { + root.values[root.selected_item].checked = !root.values[root.selected_item].checked; + } + } else if (event.text == Key.DownArrow) { + if (root.selected_item != -1) { + if (root.values.length - 1 == root.selected_item) { + // Last element, so unselect it + root.values[root.selected_item].selected_row = false; + root.selected_item = -1; + } else { + // Select next item, if next item is header row, then select second + // This should be safe, because header row should never be last item + root.values[root.selected_item].selected_row = false; + if (root.values[root.selected_item + 1].header_row) { + root.selected_item += 2; + } else { + root.selected_item += 1; + } + root.values[root.selected_item].selected_row = true; + } + } else { + // Select last item if nothing is selected + if (root.values.length > 0) { + if (root.values[0].header_row) { + root.selected_item = 1; + } else { + root.selected_item = 0; + } + root.values[root.selected_item].selected_row = true; + } + } + } else if (event.text == Key.UpArrow) { + if (root.selected_item != -1) { + if (root.selected_item == 0) { + // First element, so unselect it + root.values[root.selected_item].selected_row = false; + root.selected_item = -1; + } else { + root.values[root.selected_item].selected_row = false; + // Select previous item, if previous item is header row, then select second previous item + // This is safe, because if there is non header row upper, then can be easily selected, + // but otherwise is done -2 which for 1 (smallest possible item to set with header row) gives -1, so gives + // this non selected row + if (root.values[root.selected_item - 1].header_row) { + root.selected_item -= 2; + } else { + root.selected_item -= 1; + } + if (root.selected_item != -1) { + root.values[root.selected_item].selected_row = true; + } + } + } else { + // Select last item if nothing is selected + if (root.values.length > 0) { + root.selected_item = root.values.length - 1; + root.values[root.selected_item].selected_row = true; + } + } + } + } +} diff --git a/krokiet/ui/settings.slint b/krokiet/ui/settings.slint new file mode 100644 index 0000000..9a646f5 --- /dev/null +++ b/krokiet/ui/settings.slint @@ -0,0 +1,47 @@ +export global Settings { + in-out property settings_preset_idx: 0; + in-out property <[string]> settings_presets: ["Preset 1", "Preset 2"]; + + in-out property <[StandardListViewItem]> included_directories: [{text: "ABCD"}, {text: "BCDA"}]; + in-out property <[StandardListViewItem]> excluded_directories: [{text: "ABCD"}, {text: "BCDA"}, {text: "CDFFF"}]; + + // Settings + in-out property excluded_items: "Excluded items"; + in-out property allowed_extensions: "Allowed extensions"; + in-out property minimum_file_size: 0; + in-out property maximum_file_size: 0; + in-out property recursive_search: true; + in-out property use_cache: false; + in-out property save_as_json: false; + in-out property move_to_trash: false; + in-out property ignore_other_filesystems: false; + in-out property thread_number: 4; + + in-out property duplicate_image_preview; + in-out property duplicate_hide_hard_links; + in-out property duplicate_use_prehash; + in-out property duplicate_minimal_hash_cache_size; + in-out property duplicate_minimal_prehash_cache_size; + in-out property duplicate_delete_outdated_entries; + + in-out property similar_images_show_image_preview; + in-out property similar_images_delete_outdated_entries; + + // in-out property similar_videos_show_video_preview; // TODO - maybe someday + in-out property similar_videos_delete_outdated_entries; + + in-out property similar_music_delete_outdated_entries; + + + // Allowed subsettings + // Duplicate + in-out property <[string]> similar_images_sub_available_hash_size: ["8", "16", "32", "64"]; + in-out property similar_images_sub_hash_size_index: 0; + in-out property <[string]> similar_images_sub_available_resize_algorithm: ["Lanczos3", "Nearest", "Triangle", "Gaussian", "CatmullRom"]; + in-out property similar_images_sub_resize_algorithm_index: 0; + in-out property <[string]> similar_images_sub_available_hash_type: ["Gradient", "Mean", "VertGradient", "BlockHash", "DoubleGradient"]; + in-out property similar_images_sub_hash_type_index: 0; + in-out property similar_images_sub_max_similarity: 40; + in-out property similar_images_sub_current_similarity: 20; + in-out property similar_images_sub_ignore_same_size; +} diff --git a/krokiet/ui/settings_list.slint b/krokiet/ui/settings_list.slint new file mode 100644 index 0000000..c85c17b --- /dev/null +++ b/krokiet/ui/settings_list.slint @@ -0,0 +1,320 @@ +import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, ScrollView, LineEdit, SpinBox, ComboBox, TextEdit, Slider} from "std-widgets.slint"; +import { Settings } from "settings.slint"; +import { Callabler } from "callabler.slint"; +import { GuiState } from "gui_state.slint"; + +// TODO use Spinbox instead LineEdit {} to be able to set only numbers + +global SettingsSize { + out property item_height: 30px; +} + +component TextComponent inherits HorizontalLayout { + in-out property model; + in property name; + spacing: 5px; + Text { + horizontal-stretch: 0.0; + vertical-alignment: TextVerticalAlignment.center; + text: name; + } + LineEdit { + horizontal-stretch: 1.0; + height: SettingsSize.item_height; + text <=> model; + } +} + +component CheckBoxComponent inherits HorizontalLayout { + in-out property model; + in property name; + spacing: 5px; + CheckBox { + horizontal-stretch: 1.0; + height: SettingsSize.item_height; + checked <=> model; + text: name; + } + Rectangle {} +} + +component ThreadSliderComponent inherits HorizontalLayout { + in-out property minimum_number; + in-out property maximum_number; + in-out property name; + spacing: 5px; + + callback changed <=> slider.changed; + + Text { + text <=> name; + vertical-alignment: TextVerticalAlignment.center; + height: SettingsSize.item_height; + } + slider := Slider { + enabled: true; + height: SettingsSize.item_height; + minimum: minimum_number; + maximum <=> maximum_number; + value <=> Settings.thread_number; + } + Text { + height: SettingsSize.item_height; + vertical-alignment: TextVerticalAlignment.center; + text: round(slider.value) == 0 ? ("All (" + GuiState.maximum_threads + "/" + GuiState.maximum_threads + ")") : (round(slider.value) + "/" + GuiState.maximum_threads); + } +} + +component MinMaxSizeComponent inherits HorizontalLayout { + spacing: 20px; + Text { + horizontal-stretch: 0.0; + text:"Items Size(Bytes)"; + vertical-alignment: TextVerticalAlignment.center; + } + HorizontalLayout { + spacing: 5px; + horizontal-stretch: 1.0; + Text { + text:"Min:"; + vertical-alignment: TextVerticalAlignment.center; + } + LineEdit { + height: SettingsSize.item_height; + text <=> Settings.minimum_file_size; + } + Text { + text:"Max:"; + vertical-alignment: TextVerticalAlignment.center; + } + LineEdit { + height: SettingsSize.item_height; + text <=> Settings.maximum_file_size; + } + } +} + +component Presets inherits Rectangle { + property edit_name; + property current_index; + if !edit_name: HorizontalLayout { + spacing: 5px; + Text { + text : "Current Preset:"; + vertical-alignment: TextVerticalAlignment.center; + } + combo_box := ComboBox { + current-index <=> Settings.settings_preset_idx; + model: Settings.settings_presets; + selected(item) => { + Settings.settings_preset_idx = self.current_index; + Callabler.changed_settings_preset(); + } + } + Button { + text: "Edit name"; + clicked => { + root.edit_name = !root.edit_name; + } + } + } + if edit_name : HorizontalLayout{ + spacing: 5px; + Text { + text: "Choose name for prefix " + (Settings.settings_preset_idx + 1); + vertical-alignment: TextVerticalAlignment.center; + } + current_name := LineEdit { + text: Settings.settings_presets[Settings.settings_preset_idx]; + } + Button { + text: "Save"; + clicked => { + Settings.settings_presets[Settings.settings_preset_idx] = current_name.text; + edit_name = false; + } + } + } +} + +// component Language inherits HorizontalLayout { +// spacing: 5px; +// Text { +// text: Callabler.translate("settings_language", []); +// vertical-alignment: TextVerticalAlignment.center; +// } +// ComboBox { +// model: ["English"]; +// } +// } +component HeaderText inherits Text { + font-size: 15px; + height: SettingsSize.item_height; + horizontal-alignment: TextHorizontalAlignment.center; + vertical-alignment: TextVerticalAlignment.center; +} + +component ConfigCacheButtons inherits HorizontalLayout { + spacing: 20px; + Button { + text: "Open config folder"; + clicked => { + Callabler.open_config_folder(); + } + } + Button { + text: "Open cache folder"; + clicked => { + Callabler.open_cache_folder(); + } + } +} + +export component SettingsList inherits VerticalLayout { + preferred-height: 300px; + preferred-width: 400px; + + in-out property restart_required; + + Text { + text: "Settings"; + height: SettingsSize.item_height; + horizontal-alignment: TextHorizontalAlignment.center; + font-size: 20px; + } + ScrollView { + VerticalLayout { + padding-right: 15px; + padding-bottom: 10px; + spacing: 5px; + Presets{ + height: SettingsSize.item_height; + } + // TODO Maybe someday + // Language { + // height: SettingsSize.item_height; + // } + HeaderText { + text: "General settings"; + } + TextComponent { + name: "Excluded item:"; + model <=> Settings.excluded_items; + } + TextComponent { + name: "Allowed extensions:"; + model <=> Settings.allowed_extensions; + } + MinMaxSizeComponent { + + } + CheckBoxComponent { + name: "Recursive"; + model <=> Settings.recursive_search; + } + CheckBoxComponent { + name: "Use Cache"; + model <=> Settings.use_cache; + } + CheckBoxComponent { + name: "Also save cache as JSON file"; + model <=> Settings.save_as_json; + } + CheckBoxComponent { + name: "Move deleted files to trash"; + model <=> Settings.move_to_trash; + } + CheckBoxComponent { + name: "Ignore other filesystems (only Linux)"; + model <=> Settings.ignore_other_filesystems; + } + ThreadSliderComponent { + name: "Thread number"; + maximum_number <=> GuiState.maximum_threads; + changed => { + restart_required = true; + } + } + if restart_required: Text { + text: "---You need to restart app to apply changes in thread number---"; + horizontal-alignment: TextHorizontalAlignment.center; + } + HeaderText { + text: "Duplicate tool"; + } + CheckBoxComponent { + name: "Image preview"; + model <=> Settings.duplicate_image_preview; + } + CheckBoxComponent { + name: "Hide hard links"; + model <=> Settings.duplicate_hide_hard_links; + } + TextComponent { + name: "Minimal size of cached files - Hash (KB)"; + model <=> Settings.duplicate_minimal_hash_cache_size; + } + CheckBoxComponent { + name: "Use prehash"; + model <=> Settings.duplicate_use_prehash; + } + TextComponent { + name: "Minimal size of cached files - Prehash (KB)"; + model <=> Settings.duplicate_minimal_prehash_cache_size; + } + CheckBoxComponent { + name: "Delete outdated entries"; + model <=> Settings.duplicate_delete_outdated_entries; + } + HeaderText { + text: "Similar Images tool"; + } + CheckBoxComponent { + name: "Image preview"; + model <=> Settings.similar_images_show_image_preview; + } + CheckBoxComponent { + name: "Delete outdated entries"; + model <=> Settings.similar_images_delete_outdated_entries; + } + HeaderText { + text: "Similar Videos tool"; + } + CheckBoxComponent { + name: "Delete outdated entries"; + model <=> Settings.similar_videos_delete_outdated_entries; + } + HeaderText { + text: "Similar Music tool"; + } + CheckBoxComponent { + name: "Delete outdated entries"; + model <=> Settings.similar_music_delete_outdated_entries; + } + ConfigCacheButtons { + + } + } + } + HorizontalLayout { + spacing: 5px; + Button { + text: "Save"; + clicked => { + Callabler.save_current_preset(); + } + } + Button { + text: "Load"; + clicked => { + Callabler.load_current_preset(); + } + } + Button { + text: "Reset"; + clicked => { + Callabler.reset_current_preset(); + } + } + } +} \ No newline at end of file diff --git a/krokiet/ui/tool_settings.slint b/krokiet/ui/tool_settings.slint new file mode 100644 index 0000000..17e3083 --- /dev/null +++ b/krokiet/ui/tool_settings.slint @@ -0,0 +1,99 @@ +import { Button, VerticalBox ,TextEdit, HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, LineEdit, ScrollView, ComboBox, Slider} 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, ProgressToSend} from "common.slint"; +import { ActionButtons } from "action_buttons.slint"; +import { Progress } from "progress.slint"; +import {MainListModel} from "common.slint"; +import {Settings} from "settings.slint"; +import {Callabler} from "callabler.slint"; +import { BottomPanel } from "bottom_panel.slint"; +import {ColorPalette} from "color_palette.slint"; +import {GuiState} from "gui_state.slint"; +import { Preview } from "preview.slint"; +import {PopupNewDirectories} from "popup_new_directories.slint"; +import { PopupSelect } from "popup_select.slint"; + +component ComboBoxWrapper inherits HorizontalLayout { + in-out property text; + in-out property <[string]> model; + in-out property current_index; + spacing: 5px; + Text { + text <=> root.text; + vertical_alignment: TextVerticalAlignment.center; + } + ComboBox { + model: root.model; + current_index <=> root.current_index; + } +} + +component CheckBoxWrapper inherits CheckBox { + +} + +component SubsettingsHeader inherits Text { + text: "Subsettings"; + font-size: 15px; +} + +component SliderWrapper inherits HorizontalLayout { + in-out property maximum; + in-out property value; + in-out property text; + in-out property end_text; + in-out property end_text_size; + spacing: 5px; + Text { + text: root.text; + } + Slider { + min-width: 30px; + minimum: 0; + maximum <=> root.maximum; + value <=> root.value; + } + Text { + text: root.end_text; + width: root.end_text_size; + } +} + +export component ToolSettings { + ScrollView { + if GuiState.active_tab == CurrentTab.SimilarImages: VerticalLayout { + spacing: 5px; + padding: 10px; + SubsettingsHeader { } + ComboBoxWrapper { + text: "Hash size"; + model: Settings.similar_images_sub_available_hash_size; + current_index: Settings.similar_images_sub_hash_size_index; + } + ComboBoxWrapper { + text: "Resize Algorithm"; + model: Settings.similar_images_sub_available_resize_algorithm; + current_index: Settings.similar_images_sub_resize_algorithm_index; + } + ComboBoxWrapper { + text: "Hash type"; + model: Settings.similar_images_sub_available_hash_type; + current_index: Settings.similar_images_sub_hash_type_index; + } + CheckBoxWrapper { + text: "Ignore same size"; + checked: Settings.similar_images_sub_ignore_same_size; + } + SliderWrapper { + text: "Max difference"; + end_text: "(" + round(Settings.similar_images_sub_current_similarity) + "/" + round(Settings.similar_images_sub_max_similarity) + ")"; + end_text_size: 40px; + maximum <=> Settings.similar_images_sub_max_similarity; + value <=> Settings.similar_images_sub_current_similarity; + } + Rectangle {} + } + } +} \ No newline at end of file