forked from akanyan/STARTLINER
feat: shortcuts
This commit is contained in:
BIN
res/icon-chunithm.ico
Normal file
BIN
res/icon-chunithm.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 81 KiB |
BIN
res/icon-ongeki.ico
Normal file
BIN
res/icon-ongeki.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 87 KiB |
2
rust/Cargo.lock
generated
2
rust/Cargo.lock
generated
@ -803,7 +803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -53,5 +53,5 @@ tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
|
|||||||
tauri-plugin-updater = "2"
|
tauri-plugin-updater = "2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winsafe = { version = "0.0.23", features = ["user"] }
|
winsafe = { version = "0.0.23", features = ["user", "ole", "shell"] }
|
||||||
displayz = "^0.2.0"
|
displayz = "^0.2.0"
|
||||||
|
@ -397,6 +397,13 @@ pub async fn load_segatools_ini(state: State<'_, Mutex<AppData>>, path: PathBuf)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn create_shortcut(app: AppHandle, profile_meta: ProfileMeta) -> Result<(), String> {
|
||||||
|
log::debug!("invoke: create_shortcut({:?})", profile_meta);
|
||||||
|
|
||||||
|
util::create_shortcut(app, &profile_meta).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
|
pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
|
||||||
log::debug!("invoke: list_platform_capabilities");
|
log::debug!("invoke: list_platform_capabilities");
|
||||||
|
@ -199,6 +199,7 @@ pub async fn run(_args: Vec<String>) {
|
|||||||
cmd::sync_current_profile,
|
cmd::sync_current_profile,
|
||||||
cmd::save_current_profile,
|
cmd::save_current_profile,
|
||||||
cmd::load_segatools_ini,
|
cmd::load_segatools_ini,
|
||||||
|
cmd::create_shortcut,
|
||||||
|
|
||||||
cmd::get_global_config,
|
cmd::get_global_config,
|
||||||
cmd::set_global_config,
|
cmd::set_global_config,
|
||||||
|
@ -20,6 +20,13 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Game::Ongeki => "O.N.G.E.K.I.",
|
||||||
|
Game::Chunithm => "CHUNITHM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hook_exe(&self) -> &'static str {
|
pub fn hook_exe(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Game::Ongeki => "mu3hook.dll",
|
Game::Ongeki => "mu3hook.dll",
|
||||||
|
@ -173,4 +173,44 @@ pub async fn remove_dir_all(path: impl AsRef<Path>) -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
Err(anyhow!("invalid remove_dir_all target: not in a data directory"))
|
Err(anyhow!("invalid remove_dir_all target: not in a data directory"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn create_shortcut(
|
||||||
|
apph: AppHandle,
|
||||||
|
meta: &crate::profiles::ProfileMeta
|
||||||
|
) -> Result<()> {
|
||||||
|
use winsafe::{co, prelude::{ole_IPersistFile, ole_IUnknown, shell_IShellLink}, CoCreateInstance, CoInitializeEx, IPersistFile};
|
||||||
|
let _com_guard = CoInitializeEx(
|
||||||
|
co::COINIT::APARTMENTTHREADED
|
||||||
|
| co::COINIT::DISABLE_OLE1DDE,
|
||||||
|
)?;
|
||||||
|
let obj = CoCreateInstance::<winsafe::IShellLink>(
|
||||||
|
&co::CLSID::ShellLink,
|
||||||
|
None,
|
||||||
|
co::CLSCTX::INPROC_SERVER,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let target_dir = apph.path().cache_dir()?.join(NAME);
|
||||||
|
let target_path = target_dir.join("startliner.exe");
|
||||||
|
let lnk_path = apph.path().desktop_dir()?.join(format!("{}-{}.lnk", &meta.game, &meta.name));
|
||||||
|
|
||||||
|
obj.SetPath(target_path.to_str().ok_or_else(|| anyhow!("Illegal target path"))?)?;
|
||||||
|
obj.SetDescription(&format!("{} – {}", &meta.game.print(), &meta.name))?;
|
||||||
|
obj.SetArguments(&format!("--start --game {} --profile {}", &meta.game, &meta.name))?;
|
||||||
|
obj.SetIconLocation(
|
||||||
|
target_dir.join(format!("icon-{}.ico", &meta.game)).to_str().ok_or_else(|| anyhow!("Illegal icon path"))?,
|
||||||
|
0
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match meta.game {
|
||||||
|
Game::Ongeki => std::fs::write(target_dir.join("icon-ongeki.ico"), include_bytes!("../../res/icon-ongeki.ico")),
|
||||||
|
Game::Chunithm => std::fs::write(target_dir.join("icon-chunithm.ico"), include_bytes!("../../res/icon-chunithm.ico"))
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let file = obj.QueryInterface::<IPersistFile>()?;
|
||||||
|
|
||||||
|
file.Save(Some(lnk_path.to_str().ok_or_else(|| anyhow!("Illegal shortcut path"))?), true)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
|
import { useConfirm } from 'primevue/useconfirm';
|
||||||
import * as path from '@tauri-apps/api/path';
|
import * as path from '@tauri-apps/api/path';
|
||||||
import { invoke } from '../invoke';
|
import { invoke } from '../invoke';
|
||||||
import { useGeneralStore, usePrfStore } from '../stores';
|
import { useGeneralStore, usePrfStore } from '../stores';
|
||||||
@ -9,6 +10,8 @@ import { ProfileMeta } from '../types';
|
|||||||
|
|
||||||
const general = useGeneralStore();
|
const general = useGeneralStore();
|
||||||
const prf = usePrfStore();
|
const prf = usePrfStore();
|
||||||
|
const confirmDialog = useConfirm();
|
||||||
|
|
||||||
const isEditing = ref(false);
|
const isEditing = ref(false);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -54,6 +57,14 @@ const deleteProfile = async () => {
|
|||||||
await prf.reloadList();
|
await prf.reloadList();
|
||||||
await prf.reload();
|
await prf.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const promptDeleteProfile = async () => {
|
||||||
|
confirmDialog.require({
|
||||||
|
message: `Are you sure you want to delete ${props.p?.game}-${props.p?.name}?`,
|
||||||
|
header: 'Delete profile',
|
||||||
|
accept: deleteProfile,
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -90,7 +101,7 @@ const deleteProfile = async () => {
|
|||||||
size="small"
|
size="small"
|
||||||
class="self-center ml-2"
|
class="self-center ml-2"
|
||||||
style="width: 2rem; height: 2rem"
|
style="width: 2rem; height: 2rem"
|
||||||
@click="deleteProfile"
|
@click="promptDeleteProfile"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
rounded
|
rounded
|
||||||
|
@ -85,6 +85,15 @@ listen('launch-end', () => {
|
|||||||
getCurrentWindow().setFocus();
|
getCurrentWindow().setFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const createShortcut = async () => {
|
||||||
|
const current = prf.current;
|
||||||
|
if (current !== null) {
|
||||||
|
await invoke('create_shortcut', {
|
||||||
|
profileMeta: current.meta,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
label: 'Refresh and start',
|
label: 'Refresh and start',
|
||||||
@ -96,6 +105,11 @@ const menuItems = [
|
|||||||
icon: 'pi pi-exclamation-circle',
|
icon: 'pi pi-exclamation-circle',
|
||||||
command: async () => await startline(true, false),
|
command: async () => await startline(true, false),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Create shortcut',
|
||||||
|
icon: 'pi pi-link',
|
||||||
|
command: createShortcut,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const menu = ref();
|
const menu = ref();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user