mirror of
https://github.com/itsRevela/Revelations-Launcher.git
synced 2026-07-07 09:18:06 +00:00
feat(title-image): allow removing a custom title image
Previously once a title image was set on a custom edition there was no way to fall back to the default MenuTitle.png short of deleting the instance. Add a round-trip remove path: - Rust: remove_instance_title_image command deletes title_image.png from the instance stub dir if present, registered in the invoke_handler. - TauriService.removeInstanceTitleImage wraps the command. - useGameManager.removeTitleImage calls the command and resets the edition's titleImage back to /images/MenuTitle.png. - The Versions-view title-image button now detects a custom image (titleImage starts with data:) and swaps the icon, tooltip, and click handler between set/remove, for both keyboard and mouse paths.
This commit is contained in:
@@ -565,6 +565,18 @@ fn set_instance_title_image(app: AppHandle, instance_id: String) -> Result<Strin
|
||||
Ok(format!("data:image/png;base64,{}", b64))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[allow(non_snake_case)]
|
||||
fn remove_instance_title_image(app: AppHandle, instance_id: String) -> Result<(), String> {
|
||||
let safe_id = sanitize_path_component(&instance_id)?;
|
||||
let stub_dir = get_app_dir(&app).join("instances").join(&safe_id);
|
||||
let title_path = stub_dir.join("title_image.png");
|
||||
if title_path.exists() {
|
||||
fs::remove_file(&title_path).map_err(|e| e.to_string())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[allow(non_snake_case)]
|
||||
fn get_instance_title_image(app: AppHandle, instance_id: String) -> Result<String, String> {
|
||||
@@ -1609,7 +1621,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_gamepad::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_drpc::init())
|
||||
.invoke_handler(tauri::generate_handler![setup_macos_runtime, launch_game, stop_game, check_game_installed, save_config, load_config, download_and_install, open_instance_folder, cancel_download, get_available_runners, get_external_palettes, import_theme, download_runner, delete_instance, update_tray_icon, sync_dlc, fetch_skin, check_for_game_update, open_skins_folder, save_skin_file, import_instance_folder, set_instance_title_image, get_instance_title_image])
|
||||
.invoke_handler(tauri::generate_handler![setup_macos_runtime, launch_game, stop_game, check_game_installed, save_config, load_config, download_and_install, open_instance_folder, cancel_download, get_available_runners, get_external_palettes, import_theme, download_runner, delete_instance, update_tray_icon, sync_dlc, fetch_skin, check_for_game_update, open_skins_folder, save_skin_file, import_instance_folder, set_instance_title_image, get_instance_title_image, remove_instance_title_image])
|
||||
.setup(|app| {
|
||||
let config = load_config(app.handle().clone());
|
||||
let visible = config.enable_tray_icon.unwrap_or(true);
|
||||
|
||||
@@ -9,7 +9,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
const { setActiveView } = useUI();
|
||||
const { profile: selectedProfile, setProfile: setSelectedProfile } = useConfig();
|
||||
const { playClickSound, playBackSound, playSfx } = useAudio();
|
||||
const { editions, installs: installedVersions, toggleInstall, handleUninstall: onUninstall, deleteCustomEdition: onDeleteEdition, addCustomEdition: onAddEdition, updateCustomEdition: onUpdateEdition, downloadingId, updateAvailable, importInstance, setTitleImage } = useGame();
|
||||
const { editions, installs: installedVersions, toggleInstall, handleUninstall: onUninstall, deleteCustomEdition: onDeleteEdition, addCustomEdition: onAddEdition, updateCustomEdition: onUpdateEdition, downloadingId, updateAvailable, importInstance, setTitleImage, removeTitleImage } = useGame();
|
||||
|
||||
const [focusRow, setFocusRow] = useState<number>(0);
|
||||
const [focusCol, setFocusCol] = useState<number>(0);
|
||||
@@ -101,8 +101,14 @@ const VersionsView = memo(function VersionsView() {
|
||||
}
|
||||
} else if (focusCol === 4) {
|
||||
if (isCustom) {
|
||||
playClickSound();
|
||||
setTitleImage(edition.id);
|
||||
const hasCustomImage = typeof edition.titleImage === "string" && edition.titleImage.startsWith("data:");
|
||||
if (hasCustomImage) {
|
||||
playBackSound();
|
||||
removeTitleImage(edition.id);
|
||||
} else {
|
||||
playClickSound();
|
||||
setTitleImage(edition.id);
|
||||
}
|
||||
}
|
||||
} else if (focusCol === 5) {
|
||||
if (isCustom) {
|
||||
@@ -437,6 +443,9 @@ const VersionsView = memo(function VersionsView() {
|
||||
)}
|
||||
{isCustom && (
|
||||
<>
|
||||
{(() => {
|
||||
const hasCustomImage = typeof edition.titleImage === "string" && edition.titleImage.startsWith("data:");
|
||||
return (
|
||||
<button
|
||||
data-row={i}
|
||||
data-col={isInstalled ? 4 : 2}
|
||||
@@ -447,8 +456,13 @@ const VersionsView = memo(function VersionsView() {
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playClickSound();
|
||||
setTitleImage(edition.id);
|
||||
if (hasCustomImage) {
|
||||
playBackSound();
|
||||
removeTitleImage(edition.id);
|
||||
} else {
|
||||
playClickSound();
|
||||
setTitleImage(edition.id);
|
||||
}
|
||||
}}
|
||||
className="w-8 h-8 flex items-center justify-center outline-none border-none transition-all"
|
||||
style={{
|
||||
@@ -459,24 +473,45 @@ const VersionsView = memo(function VersionsView() {
|
||||
backgroundSize: "100% 100%",
|
||||
imageRendering: "pixelated",
|
||||
}}
|
||||
title="Set title image"
|
||||
title={hasCustomImage ? "Remove title image" : "Set title image"}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="square"
|
||||
className="text-white drop-shadow-md"
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
</svg>
|
||||
{hasCustomImage ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="square"
|
||||
className="text-white drop-shadow-md"
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
<line x1="4" y1="4" x2="20" y2="20" stroke="#ff5555" strokeWidth="3"></line>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="square"
|
||||
className="text-white drop-shadow-md"
|
||||
>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
||||
<polyline points="21 15 16 10 5 21"></polyline>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})()}
|
||||
<button
|
||||
data-row={i}
|
||||
data-col={isInstalled ? 5 : 3}
|
||||
|
||||
@@ -192,6 +192,17 @@ export function useGameManager({ profile, setProfile, customEditions, setCustomE
|
||||
}
|
||||
}, [customEditions, setCustomEditions]);
|
||||
|
||||
const removeTitleImage = useCallback(async (id: string) => {
|
||||
try {
|
||||
await TauriService.removeInstanceTitleImage(id);
|
||||
setCustomEditions(customEditions.map((e) =>
|
||||
e.id === id ? { ...e, titleImage: "/images/MenuTitle.png" } : e
|
||||
));
|
||||
} catch {
|
||||
// failed
|
||||
}
|
||||
}, [customEditions, setCustomEditions]);
|
||||
|
||||
// Load custom title images on startup
|
||||
useEffect(() => {
|
||||
const loadTitleImages = async () => {
|
||||
@@ -233,5 +244,6 @@ export function useGameManager({ profile, setProfile, customEditions, setCustomE
|
||||
updateAvailable,
|
||||
importInstance,
|
||||
setTitleImage,
|
||||
removeTitleImage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -173,6 +173,10 @@ export class TauriService {
|
||||
return invoke('get_instance_title_image', { instanceId });
|
||||
}
|
||||
|
||||
static async removeInstanceTitleImage(instanceId: string): Promise<void> {
|
||||
return invoke('remove_instance_title_image', { instanceId });
|
||||
}
|
||||
|
||||
static async importInstanceFolder(instanceId: string, url: string): Promise<string> {
|
||||
return invoke('import_instance_folder', { instanceId, url });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user