diff --git a/cspell.jsonc b/cspell.jsonc
index d5c622c..5345052 100644
--- a/cspell.jsonc
+++ b/cspell.jsonc
@@ -21,6 +21,7 @@
"tauri",
"thiserror",
"Unhide",
+ "unlisten",
"unoptimized",
"walkdir",
"Warhammer",
diff --git a/frontend/src/components/form.tsx b/frontend/src/components/form.tsx
index 326e20c..ff39e89 100644
--- a/frontend/src/components/form.tsx
+++ b/frontend/src/components/form.tsx
@@ -10,6 +10,7 @@ import Checkbox from "@mui/material/Checkbox";
import ClearAllIcon from "@mui/icons-material/ClearAll";
import ConvertButton from "./buttons/convert_btn";
import Grid from "@mui/material/Unstable_Grid2";
+import SlideshowIcon from "@mui/icons-material/Slideshow";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import toast from "react-hot-toast";
import type { SubmitHandler } from "react-hook-form";
@@ -23,6 +24,8 @@ import {
} from "@/components/lists/select_log_level";
import { SelectPathButton } from "@/components/buttons/path_selector";
import { UnhideDarBtn } from "@/components/buttons/unhide_dar_btn";
+import { listen } from "@tauri-apps/api/event";
+import LinearWithValueLabel from "./progress_bar";
type FormProps = {
src: string;
@@ -35,6 +38,8 @@ type FormProps = {
logLevel: LogLevel;
runParallel: boolean;
hideDar: boolean;
+ showProgress: boolean;
+ progress: number;
};
export function ConvertForm() {
@@ -49,10 +54,12 @@ export function ConvertForm() {
modAuthor: localStorage.getItem("modAuthor") ?? "",
mappingPath: localStorage.getItem("mappingPath") ?? "",
mapping1personPath: localStorage.getItem("mapping1personPath") ?? "",
- runParallel: localStorage.getItem("runParallel") === "true",
- hideDar: localStorage.getItem("hideDar") === "true",
loading: false as boolean,
logLevel: selectLogLevel(localStorage.getItem("logLevel") ?? "error"),
+ runParallel: localStorage.getItem("runParallel") === "true",
+ hideDar: localStorage.getItem("hideDar") === "true",
+ showProgress: localStorage.getItem("showProgress") === "true",
+ progress: 0,
} satisfies FormProps,
});
@@ -85,10 +92,25 @@ export function ConvertForm() {
mapping1personPath,
runParallel,
hideDar,
+ showProgress,
}) => {
setLoading(true);
+ let unlisten: (() => void) | null = null;
try {
+ setValue("progress", 0);
+ let maxNum = 0;
+ let prog = 0;
+
+ unlisten = await listen<{ index: number }>("show-progress", (event) => {
+ if (maxNum === 0) {
+ maxNum = event.payload.index;
+ } else {
+ prog = event.payload.index;
+ }
+ setValue("progress", (prog * 100) / maxNum);
+ });
+
const completeInfo = await convertDar2oar({
src,
dist,
@@ -98,11 +120,16 @@ export function ConvertForm() {
mapping1personPath,
runParallel,
hideDar,
+ showProgress,
});
toast.success(completeInfo);
+ setValue("progress", 100);
} catch (err) {
toast.error(`${err}`);
} finally {
+ if (unlisten) {
+ unlisten();
+ }
setLoading(false);
}
};
@@ -349,33 +376,37 @@ export function ConvertForm() {
(
- Use multi-threading.
-
- In most cases, it slows down by tens of ms, but may be
- effective when there is more weight on CPU processing with
- fewer files to copy and more logic parsing of
- "_condition.txt"
-
+ <>
+ Display detail progress
+
+ However, conversion may be delayed by 5~10 seconds or
+ more.
+
+ >
}
>
{
- localStorage.setItem("runParallel", `${!value}`);
- setValue("runParallel", !value);
+ setValue("showProgress", !value);
+ localStorage.setItem("showProgress", `${!value}`);
}}
checked={value}
- aria-label="Run Parallel"
+ aria-label="Show Progress"
/>
}
- label="Run Parallel"
+ label={
+
+
+ ProgressBar
+
+ }
/>
)}
@@ -403,6 +434,7 @@ export function ConvertForm() {
{
localStorage.setItem("hideDar", `${!value}`);
+ setValue("hideDar", !value);
}}
checked={value}
aria-label="Hide DAR"
@@ -431,14 +463,51 @@ export function ConvertForm() {
-
- (
-
- )}
- />
+
+
+ (
+
+ Use multi-threading.
+
+ In most cases, it slows down by tens of ms, but may be
+ effective when there is more weight on CPU processing with
+ fewer files to copy and more logic parsing of
+ "_condition.txt"
+
+ }
+ >
+ {
+ localStorage.setItem("runParallel", `${!value}`);
+ setValue("runParallel", !value);
+ }}
+ checked={value}
+ aria-label="Run Parallel"
+ />
+ }
+ label="Run Parallel"
+ />
+
+ )}
+ />
+
+
+
+ (
+
+ )}
+ />
+
)}
/>
+
+ (
+
+ )}
+ />
);
diff --git a/frontend/src/components/progress_bar.tsx b/frontend/src/components/progress_bar.tsx
new file mode 100644
index 0000000..8276924
--- /dev/null
+++ b/frontend/src/components/progress_bar.tsx
@@ -0,0 +1,35 @@
+import * as React from "react";
+import LinearProgress, {
+ LinearProgressProps,
+} from "@mui/material/LinearProgress";
+import Typography from "@mui/material/Typography";
+import Box from "@mui/material/Box";
+
+function LinearProgressWithLabel(
+ props: LinearProgressProps & { value: number },
+) {
+ return (
+
+
+
+
+
+ {`${Math.round(
+ props.value,
+ )}%`}
+
+
+ );
+}
+
+type Props = {
+ progress: number;
+};
+
+export default function LinearWithValueLabel({ progress }: Readonly) {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/src/tauri_cmd/index.ts b/frontend/src/tauri_cmd/index.ts
index 9db7dcd..d978db1 100644
--- a/frontend/src/tauri_cmd/index.ts
+++ b/frontend/src/tauri_cmd/index.ts
@@ -2,6 +2,7 @@ import { invoke } from "@tauri-apps/api";
import { open } from "@tauri-apps/api/dialog";
import { appLogDir } from "@tauri-apps/api/path";
import { open as openShell } from "@tauri-apps/api/shell";
+import { selectLogLevel } from "@/components/lists/select_log_level";
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error";
@@ -14,6 +15,7 @@ type ConverterArgs = {
mapping1personPath?: string;
runParallel?: boolean;
hideDar?: boolean;
+ showProgress?: boolean;
};
/**
@@ -21,28 +23,29 @@ type ConverterArgs = {
* # Throw Error
*/
export async function convertDar2oar(props: ConverterArgs): Promise {
- const darDir = props.src === "" ? undefined : props.src;
- const oarDir = props.dist === "" ? undefined : props.dist;
- const modName = props.modName === "" ? undefined : props.modName;
- const modAuthor = props.modAuthor === "" ? undefined : props.modAuthor;
- const mapping1personPath =
- props.mapping1personPath === "" ? undefined : props.mapping1personPath;
- const mappingPath = props.mappingPath === "" ? undefined : props.mappingPath;
- const runParallel = props.runParallel ?? false;
- const hideDar = props.hideDar ?? false;
-
- return invoke("convert_dar2oar", {
+ const args = {
options: {
- darDir,
- oarDir,
- modName,
- modAuthor,
- mappingPath,
- mapping1personPath,
- runParallel,
- hideDar,
+ darDir: props.src === "" ? undefined : props.src,
+ oarDir: props.dist === "" ? undefined : props.dist,
+ modName: props.modName === "" ? undefined : props.modName,
+ modAuthor: props.modAuthor === "" ? undefined : props.modAuthor,
+ mappingPath: props.mappingPath === "" ? undefined : props.mappingPath,
+ mapping1personPath:
+ props.mapping1personPath === "" ? undefined : props.mapping1personPath,
+ runParallel: props.runParallel ?? false,
+ hideDar: props.hideDar ?? false,
},
- });
+ };
+
+ let logLevel = selectLogLevel(localStorage.getItem("logLevel") ?? "");
+ changeLogLevel(logLevel);
+
+ const showProgress = props.showProgress ?? false;
+ if (showProgress) {
+ return invoke("convert_dar2oar_with_progress", args);
+ } else {
+ return invoke("convert_dar2oar", args);
+ }
}
export async function changeLogLevel(logLevel?: LogLevel): Promise {
diff --git a/frontend/src/utils/styles.ts b/frontend/src/utils/styles.ts
index 4ced4eb..df4b53e 100644
--- a/frontend/src/utils/styles.ts
+++ b/frontend/src/utils/styles.ts
@@ -52,6 +52,10 @@ label.Mui-focused {
.MuiLoadingButton-root:hover {
background-color: var(--hover-convert-btn-color);
+}
+
+.MuiLinearProgress-bar {
+ background-color: var(--theme-color);
}`;
}
diff --git a/src-tauri/src/cmd.rs b/src-tauri/src/cmd.rs
index 109fea9..aeeb43a 100644
--- a/src-tauri/src/cmd.rs
+++ b/src-tauri/src/cmd.rs
@@ -3,6 +3,7 @@ use dar2oar_core::{
convert_dar_to_oar,
fs::{async_closure::AsyncClosure, parallel, remove_oar, unhide_dar, ConvertOptions},
};
+use tauri::Window;
/// early return with Err() and write log error.
macro_rules! bail {
@@ -31,6 +32,39 @@ pub(crate) async fn convert_dar2oar(options: GuiConverterOptions<'_>) -> Result<
}
}
+#[tauri::command]
+pub(crate) async fn convert_dar2oar_with_progress(
+ window: Window,
+ options: GuiConverterOptions<'_>,
+) -> Result {
+ tracing::debug!("options: {:?}", &options);
+ let run_parallel = options.run_parallel.unwrap_or_default();
+
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+ struct Payload {
+ index: usize,
+ }
+ let sender = move |index: usize| {
+ if let Err(err) = window.emit("show-progress", Payload { index }) {
+ tracing::error!("{}", err);
+ };
+ async move {}
+ };
+
+ let config = ConvertOptions::async_from(options).await;
+ let res = match run_parallel {
+ true => parallel::convert_dar_to_oar(config, sender).await,
+ false => convert_dar_to_oar(config, sender).await,
+ };
+ match res {
+ Ok(complete_msg) => {
+ tracing::info!("{}", complete_msg);
+ Ok(complete_msg.to_string())
+ }
+ Err(err) => bail!(err),
+ }
+}
+
#[tauri::command]
pub(crate) async fn change_log_level(log_level: Option<&str>) -> Result<(), String> {
tracing::debug!("Selected log level: {:?}", log_level);
diff --git a/src-tauri/src/convert_option.rs b/src-tauri/src/convert_option.rs
index 0d4efde..99b4ff7 100644
--- a/src-tauri/src/convert_option.rs
+++ b/src-tauri/src/convert_option.rs
@@ -13,6 +13,7 @@ pub(crate) struct GuiConverterOptions<'a> {
pub(crate) mapping_1person_path: Option<&'a str>,
pub(crate) run_parallel: Option,
pub(crate) hide_dar: Option,
+ pub(crate) show_progress: Option,
}
#[async_trait::async_trait]
@@ -32,6 +33,7 @@ impl<'a> AsyncFrom> for ConvertOptions<'a, &'a str> {
mapping_1person_path,
run_parallel: _,
hide_dar,
+ show_progress: _,
} = options;
let oar_dir = oar_dir.and_then(|dist| match dist.is_empty() {
diff --git a/src-tauri/src/runner.rs b/src-tauri/src/runner.rs
index 51083e0..c84a015 100644
--- a/src-tauri/src/runner.rs
+++ b/src-tauri/src/runner.rs
@@ -12,6 +12,7 @@ pub fn run_tauri() -> anyhow::Result<()> {
.invoke_handler(tauri::generate_handler![
crate::cmd::change_log_level,
crate::cmd::convert_dar2oar,
+ crate::cmd::convert_dar2oar_with_progress,
crate::cmd::remove_oar_dir,
crate::cmd::restore_dar_dir,
])
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index e40617a..1c94dfd 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -73,7 +73,7 @@
"windows": [
{
"fullscreen": false,
- "height": 880,
+ "height": 900,
"resizable": true,
"theme": "Dark",
"title": "DAR to OAR Converter",