Fix UE launch permission, move it to context menu, refresh History after Submit
- Launch now goes through a backend launch_file command (shell `start`), so
it works for any path — fixes "Not allowed to open path" from the opener
plugin's scoped open-path.
- Removed the toolbar Launch-Unreal button; it's now a right-click item on the
Working folder ("Launch Unreal Engine"), shown only when a .uproject exists.
- Reload History after a successful Submit so the new changelist shows without
a manual F5.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -610,6 +610,25 @@ async fn open_in_explorer(state: State<'_, AppState>, target: String) -> Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Open a local file with its associated application (e.g. launch Unreal for a
|
||||
/// `.uproject`). Uses the shell `start` so there are no path-scope restrictions.
|
||||
#[tauri::command]
|
||||
async fn launch_file(path: String) -> Result<(), String> {
|
||||
if !std::path::Path::new(&path).exists() {
|
||||
return Err(format!("File not found: {path}"));
|
||||
}
|
||||
let mut c = Command::new("cmd");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
c.creation_flags(0x0800_0000);
|
||||
}
|
||||
c.args(["/C", "start", "", &path])
|
||||
.spawn()
|
||||
.map_err(|e| format!("Could not launch: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Look for an Unreal `.uproject` inside the working-folder scope (resolved to a
|
||||
/// local dir via `p4 where`). Checks the folder itself and one level of subdirs.
|
||||
/// Returns the local path of the first match, or "" if the scope isn't a UE project.
|
||||
@ -751,6 +770,7 @@ pub fn run() {
|
||||
p4_undo_change,
|
||||
open_in_explorer,
|
||||
find_uproject,
|
||||
launch_file,
|
||||
p4_describe,
|
||||
read_file,
|
||||
read_depot,
|
||||
|
||||
11
src/App.tsx
11
src/App.tsx
@ -3,7 +3,6 @@ import type { MouseEvent as ReactMouseEvent, CSSProperties, ReactNode } from "re
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { getCurrentWebview } from "@tauri-apps/api/webview";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { openPath } from "@tauri-apps/plugin-opener";
|
||||
import ModelViewer from "./ModelViewer";
|
||||
import UpdateBanner from "./Updater";
|
||||
import "./App.css";
|
||||
@ -504,7 +503,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
|
||||
// launch Unreal by opening the detected .uproject (file association starts UE)
|
||||
async function launchUE() {
|
||||
if (!uproject) return;
|
||||
try { await openPath(uproject); flash(t("Launching Unreal…")); }
|
||||
try { await p4.launchFile(uproject); flash(t("Launching Unreal…")); }
|
||||
catch (e) { flash(String(e), true); }
|
||||
}
|
||||
|
||||
@ -564,6 +563,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
|
||||
if (errors.length) flash(errors.join(" · "), true);
|
||||
else flash(t("Sent to the server: {n} changelist(s).", { n }));
|
||||
await refresh();
|
||||
await loadHistory(); // the submit created a new submitted changelist — refresh History
|
||||
}
|
||||
|
||||
async function doRevert(file: OpenedFile) {
|
||||
@ -638,6 +638,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
|
||||
<div className="vhandle grouph" onMouseDown={(e) => startResize(e, "sync")} title={t("Drag to resize")} />
|
||||
<div className="tzone" onClick={() => browseTo("")} title={t("Choose the project working folder · right-click for more")}
|
||||
onContextMenu={(e) => openCtx(e, [
|
||||
...(uproject ? [{ label: t("Launch Unreal Engine"), act: launchUE }] : []),
|
||||
{ label: t("Open in Explorer"), act: () => reveal(activePath || String(info?.clientRoot || "")) },
|
||||
{ label: t("Choose another folder…"), act: () => browseTo("") },
|
||||
{ label: t("Copy path"), act: () => copyText(activePath || "//…", t("Path")) },
|
||||
@ -646,12 +647,6 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
|
||||
<span className="tzmeta"><span className="lbl">{t("Working folder")}</span><span className="val">{activePath || t("whole workspace (//…)")}</span></span>
|
||||
<span className="chev">{I.chev}</span>
|
||||
</div>
|
||||
{uproject && (
|
||||
<button className="ue-btn" onClick={launchUE} title={t("Open this project in Unreal Engine")}>
|
||||
<span className="ue-ic">{I.ue}</span>
|
||||
<span className="ue-lbl"><span className="ue-a">{t("Launch")}</span><span className="ue-b">Unreal</span></span>
|
||||
</button>
|
||||
)}
|
||||
{pending.length > 0 ? (
|
||||
<div className="tzone push" onClick={pushAll} title={t("Submit committed to the server (push)")}>
|
||||
<span className="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M12 20V8M6 14l6-6 6 6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" /></svg></span>
|
||||
|
||||
@ -68,6 +68,7 @@ const D: Record<string, Tr> = {
|
||||
"Working…": { ru: "Работаю…", de: "Arbeite…", fr: "En cours…", es: "Trabajando…" },
|
||||
"pull from server": { ru: "забрать с сервера (pull)", de: "vom Server holen (pull)", fr: "récupérer du serveur (pull)", es: "traer del servidor (pull)" },
|
||||
"Launch": { ru: "Запустить", de: "Starten", fr: "Lancer", es: "Iniciar" },
|
||||
"Launch Unreal Engine": { ru: "Запустить Unreal Engine", de: "Unreal Engine starten", fr: "Lancer Unreal Engine", es: "Iniciar Unreal Engine" },
|
||||
"Open this project in Unreal Engine": { ru: "Открыть проект в Unreal Engine", de: "Projekt in Unreal Engine öffnen", fr: "Ouvrir le projet dans Unreal Engine", es: "Abrir el proyecto en Unreal Engine" },
|
||||
"Launching Unreal…": { ru: "Запуск Unreal…", de: "Unreal wird gestartet…", fr: "Lancement d’Unreal…", es: "Iniciando Unreal…" },
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ export const p4 = {
|
||||
undoChange: (change: string) => invoke<string>("p4_undo_change", { change }),
|
||||
openInExplorer: (target: string) => invoke<void>("open_in_explorer", { target }),
|
||||
findUproject: (scope: string) => invoke<string>("find_uproject", { scope }),
|
||||
launchFile: (path: string) => invoke<void>("launch_file", { path }),
|
||||
readDepot: async (depot: string): Promise<ArrayBuffer> => {
|
||||
const r: unknown = await invoke("read_depot", { depot });
|
||||
if (r instanceof ArrayBuffer) return r;
|
||||
|
||||
Reference in New Issue
Block a user