Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions src-tauri/src/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use std::{
fs,
io::Read,
path::{Path, PathBuf},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};

use super::{
Expand Down Expand Up @@ -726,9 +730,35 @@ fn replace_mouse_action(content: &str, action: &str, values: &[String]) -> Resul
}

#[tauri::command]
pub(super) fn get_key_bindings(
pub(super) async fn get_key_bindings(
game_path: String,
language: String,
cancel: Option<bool>,
) -> Result<KeyBindingCatalog, String> {
let cancelled = Arc::new(AtomicBool::new(false));
let worker_cancelled = Arc::clone(&cancelled);
let worker = tauri::async_runtime::spawn_blocking(move || {
get_key_bindings_impl(game_path, language, &worker_cancelled)
});
// 默认允许取消:IPC 请求被取消(页面卸载、WebView 销毁)时 Tauri 会 drop
// 本 future,guard 随即通知 worker 在下一个检查点退出,结果随之丢弃。
// 传 cancel: true 可显式禁用(如页面内的手动刷新)。
struct CancelOnDrop(Arc<AtomicBool>);
impl Drop for CancelOnDrop {
fn drop(&mut self) {
self.0.store(true, Ordering::Relaxed);
}
}
let _guard = (!cancel.unwrap_or(false)).then(|| CancelOnDrop(cancelled));
worker
.await
.map_err(|error| format!("读取按键配置失败:{error}"))?
}

fn get_key_bindings_impl(
game_path: String,
language: String,
cancelled: &AtomicBool,
) -> Result<KeyBindingCatalog, String> {
let normalized = normalize_game_path_impl(&game_path);
let game_path = PathBuf::from(normalized);
Expand Down Expand Up @@ -782,6 +812,9 @@ pub(super) fn get_key_bindings(
config_files.sort_by_key(|entry| entry.file_name().to_string_lossy().to_ascii_lowercase());

for config in config_files {
if cancelled.load(Ordering::Relaxed) {
return Err("已取消读取按键配置".to_string());
}
let file_name = config.file_name().to_string_lossy().to_string();
let source = config_source_name(&file_name);
let Ok(content) = fs::read_to_string(config.path()) else {
Expand Down Expand Up @@ -973,8 +1006,12 @@ mod tests {
if !path.join("Saves").join("settings.celeste").is_file() {
return;
}
let catalog = get_key_bindings(path.to_string_lossy().into_owned(), "zh-CN".to_string())
.expect("local key bindings should be readable");
let catalog = get_key_bindings_impl(
path.to_string_lossy().into_owned(),
"zh-CN".to_string(),
&AtomicBool::new(false),
)
.expect("local key bindings should be readable");
assert!(
catalog
.entries
Expand Down
26 changes: 22 additions & 4 deletions src/celemod-ui/src/routes/KeyBindings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,22 +236,40 @@ export const KeyBindings = () => {
const [selectedConflict, setSelectedConflict] = useState("");
const [showDisabled, setShowDisabled] = useState(false);
const [saving, setSaving] = useState("");
const requestId = useRef(0);

const refresh = useCallback(() => {
if (!gamePath) return;
const currentRequest = ++requestId.current;
setLoading(true);
setError("");
void callRemote<KeyBindingCatalog>(
"get_key_bindings",
gamePath,
currentLang,
)
.then(setCatalog)
.catch((reason) => setError(String(reason)))
.finally(() => setLoading(false));
.then((data) => {
if (currentRequest !== requestId.current) return;
setCatalog(data);
})
.catch((reason) => {
if (currentRequest !== requestId.current) return;
setError(String(reason));
})
.finally(() => {
if (currentRequest === requestId.current) setLoading(false);
});
}, [currentLang, gamePath]);

useEffect(refresh, [refresh]);
useEffect(() => {
const requestScope = requestId;
refresh();
// 卸载时作废旧请求:页面切换中途到达的响应不再写入状态,
// 下次挂载会以新序号重新加载。
return () => {
requestScope.current += 1;
};
}, [refresh]);

const consideredEntries = useMemo(
() =>
Expand Down
2 changes: 1 addition & 1 deletion src/celemod-ui/src/tauri/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const parameterNames: Record<string, string[]> = {
get_mod_cache_status: [],
get_database_path: [],
start_miaonet_oauth: ["gamePath", "onEvent"],
get_key_bindings: ["gamePath", "language"],
get_key_bindings: ["gamePath", "language", "cancel"],
update_key_binding: ["gamePath", "request"],
};

Expand Down
Loading