@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "scrcpy-mask",
|
||||
"private": true,
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@ -12,6 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": ">=2.0.0-beta.8",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.1",
|
||||
"@tauri-apps/plugin-http": "2.0.0-beta.3",
|
||||
"@tauri-apps/plugin-process": "2.0.0-beta.2",
|
||||
"@tauri-apps/plugin-shell": "2.0.0-beta.3",
|
||||
|
BIN
public/favicon.ico
Executable file → Normal file
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 6.3 KiB |
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "scrcpy-mask"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["AkiChase"]
|
||||
edition = "2021"
|
||||
@ -21,3 +21,4 @@ tokio = { version = "1.36.0", features = ["rt-multi-thread", "net", "macros", "i
|
||||
tauri-plugin-process = "2.0.0-beta"
|
||||
tauri-plugin-shell = "2.0.0-beta"
|
||||
tauri-plugin-http = "2.0.0-beta"
|
||||
tauri-plugin-clipboard-manager = "2.1.0-beta.2"
|
||||
|
@ -34,11 +34,16 @@
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{ "url": "https://api.github.com/repos/AkiChase/scrcpy-mask/*" }
|
||||
{
|
||||
"url": "https://api.github.com/repos/AkiChase/scrcpy-mask/*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"http:allow-fetch",
|
||||
"app:default",
|
||||
"app:allow-version"
|
||||
"app:allow-version",
|
||||
"clipboard-manager:default",
|
||||
"clipboard-manager:allow-read-text",
|
||||
"clipboard-manager:allow-write-text"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 742 B |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 4.3 KiB |
@ -1,5 +1,6 @@
|
||||
[
|
||||
{"list":[{"key":{"down":"KeyS","left":"KeyA","right":"KeyD","up":"KeyW"},"note":"方向轮盘","offset":175,"pointerId":1,"posX":183,"posY":566,"type":"SteeringWheel"},{"key":"KeyQ","note":"技能1","pointerId":2,"posX":951,"posY":636,"range":30,"type":"DirectionalSkill"},{"key":"KeyE","note":"技能2","pointerId":2,"posX":1025,"posY":500,"range":30,"type":"DirectionalSkill"},{"key":"AltLeft","note":"技能3","pointerId":2,"posX":1160,"posY":420,"range":30,"type":"DirectionalSkill"},{"key":"Space","note":"取消技能","pointerId":2,"posX":1160,"posY":140,"type":"CancelSkill"},{"key":"KeyB","note":"回城","pointerId":3,"posX":650,"posY":650,"time":80,"type":"Tap"},{"key":"KeyC","note":"回复","pointerId":3,"posX":740,"posY":650,"time":80,"type":"Tap"},{"key":"KeyF","note":"召唤师技能","pointerId":2,"posX":838,"posY":647,"range":50,"type":"DirectionalSkill"},{"key":"M2","note":"攻击","pointerId":3,"posX":1174,"posY":618,"time":80,"type":"Tap"},{"key":"Digit1","note":"技能1升级","pointerId":3,"posX":880,"posY":560,"time":80,"type":"Tap"},{"key":"Digit2","note":"技能2升级","pointerId":3,"posX":960,"posY":430,"time":80,"type":"Tap"},{"key":"Digit3","note":"技能3升级","pointerId":3,"posX":1090,"posY":350,"time":80,"type":"Tap"},{"key":"Digit5","note":"快速购买1","pointerId":3,"posX":133,"posY":289,"time":80,"type":"Tap"},{"key":"Digit6","note":"快速购买2","pointerId":3,"posX":130,"posY":370,"time":80,"type":"Tap"},{"key":"M3","note":"观察","pointerId":4,"posX":1000,"posY":200,"scale":1,"type":"Observation"},{"key":"Tab","macro":{"down":[{"args":["default",5,1185,40,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1220,100,80],"type":"touch"}]},"note":"战绩面板","posX":1185,"posY":40,"type":"Macro"},{"key":"ShiftLeft","macro":{"down":[{"args":["default",5,40,300,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1200,60,80],"type":"touch"}]},"note":"商店","posX":44,"posY":302,"type":"Macro"},{"key":"KeyZ","macro":{"down":[{"args":["default",5,250,230,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,640,150,80],"type":"touch"}]},"note":"地图","posX":250,"posY":230,"type":"Macro"},{"key":"Backquote","macro":{"down":[{"args":["default",5,40,300,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,510,630,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,1165,575,80],"type":"touch"},{"args":["default",5,1200,65,80],"type":"touch"},{"args":[200],"type":"sleep"},{"args":["default",5,125,300,80],"type":"touch"}],"loop":null,"up":null},"note":"换装2","posX":236,"posY":66,"type":"Macro"},{"directional":false,"key":"ControlLeft","note":"","pointerId":2,"posX":839,"posY":647,"rangeOrTime":80,"type":"TriggerWhenPressedSkill"},{"directional":false,"key":"M4","note":"","pointerId":2,"posX":952,"posY":636,"rangeOrTime":80,"type":"TriggerWhenPressedSkill"},{"directional":false,"key":"WheelDown","note":"","pointerId":2,"posX":1155,"posY":280,"rangeOrTime":80,"type":"TriggerWhenPressedSkill"},{"key":"Enter","macro":{"down":[{"args":["default",5,1245,280,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,1160,560,80],"type":"touch"},{"args":[],"type":"key-input-mode"}],"loop":null,"up":null},"note":"聊天","posX":1244,"posY":281,"type":"Macro"}],"relativeSize":{"h":720,"w":1280},"title":"AVD-王者荣耀-三技能-导入默认"},
|
||||
{"list":[{"key":{"down":"KeyS","left":"KeyA","right":"KeyD","up":"KeyW"},"note":"方向轮盘","offset":175,"pointerId":1,"posX":183,"posY":566,"type":"SteeringWheel"},{"key":"KeyQ","note":"技能1","pointerId":2,"posX":952,"posY":636,"range":50,"type":"DirectionalSkill"},{"key":"AltLeft","note":"技能2","pointerId":2,"posX":979,"posY":526,"range":50,"type":"DirectionalSkill"},{"key":"KeyE","note":"技能3","pointerId":2,"posX":1074,"posY":438,"range":50,"type":"DirectionalSkill"},{"key":"Space","note":"取消技能","pointerId":2,"posX":1160,"posY":140,"type":"CancelSkill"},{"key":"KeyB","note":"回城","pointerId":3,"posX":644,"posY":650,"time":80,"type":"Tap"},{"key":"KeyC","note":"回复","pointerId":3,"posX":742,"posY":650,"time":80,"type":"Tap"},{"key":"KeyF","note":"召唤师技能","pointerId":2,"posX":838,"posY":647,"range":50,"type":"DirectionalSkill"},{"key":"M2","note":"攻击","pointerId":3,"posX":1179,"posY":621,"time":80,"type":"Tap"},{"key":"Digit1","note":"技能1升级","pointerId":3,"posX":895,"posY":564,"time":80,"type":"Tap"},{"key":"Digit2","note":"技能2升级","pointerId":3,"posX":922,"posY":456,"time":80,"type":"Tap"},{"key":"Digit3","note":"技能3升级","pointerId":3,"posX":1015,"posY":376,"time":80,"type":"Tap"},{"key":"Digit5","note":"快速购买1","pointerId":3,"posX":133,"posY":289,"time":80,"type":"Tap"},{"key":"Digit6","note":"快速购买2","pointerId":3,"posX":130,"posY":370,"time":80,"type":"Tap"},{"key":"M3","note":"观察","pointerId":4,"posX":1000,"posY":200,"scale":1,"type":"Observation"},{"key":"Tab","macro":{"down":[{"args":["default",5,1185,40,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1220,100,80],"type":"touch"}]},"note":"战绩面板","posX":1185,"posY":40,"type":"Macro"},{"key":"ShiftLeft","macro":{"down":[{"args":["default",5,40,300,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1200,60,80],"type":"touch"}]},"note":"商店","posX":44,"posY":302,"type":"Macro"},{"key":"KeyZ","macro":{"down":[{"args":["default",5,250,230,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,640,150,80],"type":"touch"}]},"note":"地图","posX":250,"posY":230,"type":"Macro"},{"key":"Backquote","macro":{"down":[{"args":["default",5,40,300,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,510,630,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,1165,575,80],"type":"touch"},{"args":["default",5,1200,65,80],"type":"touch"},{"args":[200],"type":"sleep"},{"args":["default",5,125,300,80],"type":"touch"}],"loop":null,"up":null},"note":"换装2","posX":236,"posY":66,"type":"Macro"},{"key":"WheelDown","note":"","pointerId":2,"posX":1189,"posY":422,"range":50,"type":"DirectionalSkill"},{"directional":false,"key":"M4","note":"","pointerId":2,"posX":951,"posY":636,"rangeOrTime":80,"type":"TriggerWhenPressedSkill"},{"type":"Macro","key":"Enter","note":"聊天","posX":1250,"posY":307,"macro":{"down":[{"args":["default",5,1245,280,80],"type":"touch"},{"args":[150],"type":"sleep"},{"args":["default",5,1160,560,80],"type":"touch"},{"args":[],"type":"key-input-mode"}],"loop":null,"up":null}},{"type":"TriggerWhenPressedSkill","key":"WheelUp","note":"装备技能","posX":1157,"posY":276,"pointerId":2,"directional":false,"rangeOrTime":80}],"relativeSize":{"h":720,"w":1280},"title":"AVD-王者荣耀-四技能-导入默认"},
|
||||
{"list":[{"key":"Backquote","note":"准星键","pointerId":0,"posX":640,"posY":361,"scaleX":1,"scaleY":1,"type":"Sight"},{"key":{"down":"KeyS","left":"KeyA","right":"KeyD","up":"KeyW"},"note":"","offset":150,"pointerId":1,"posX":208,"posY":542,"type":"SteeringWheel"},{"key":"KeyC","note":"","pointerId":3,"posX":1085,"posY":668,"time":80,"type":"Tap"},{"key":"KeyZ","note":"","pointerId":3,"posX":1207,"posY":648,"time":80,"type":"Tap"},{"key":"Space","note":"","pointerId":3,"posX":1230,"posY":503,"time":80,"type":"Tap"},{"key":"M3","note":"","pointerId":4,"posX":992,"posY":229,"scale":0.6,"type":"Observation"},{"key":"Digit1","note":"","pointerId":3,"posX":543,"posY":642,"time":80,"type":"Tap"},{"key":"Digit2","note":"","pointerId":3,"posX":729,"posY":644,"time":80,"type":"Tap"},{"key":"ShiftLeft","note":"","pointerId":3,"posX":1075,"posY":194,"time":80,"type":"Tap"},{"key":"KeyF","note":"","pointerId":3,"posX":861,"posY":310,"time":80,"type":"Tap"},{"key":"KeyG","note":"","pointerId":3,"posX":865,"posY":388,"time":80,"type":"Tap"},{"key":"KeyR","note":"","pointerId":3,"posX":976,"posY":674,"time":80,"type":"Tap"},{"key":"KeyE","note":"","pointerId":3,"posX":872,"posY":495,"time":80,"type":"Tap"},{"key":"KeyB","note":"","pointerId":3,"posX":543,"posY":593,"time":80,"type":"Tap"},{"key":"KeyN","note":"","pointerId":3,"posX":721,"posY":589,"time":80,"type":"Tap"},{"drag":false,"note":"","pointerId":2,"posX":1097,"posY":549,"scaleX":0.5,"scaleY":0.5,"type":"Fire"},{"key":"M2","note":"","pointerId":3,"posX":1227,"posY":376,"time":80,"type":"Tap"},{"key":"Tab","macro":{"down":[{"args":["default",5,100,650,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1250,40,80],"type":"touch"}]},"note":"背包宏","posX":95,"posY":656,"type":"Macro"},{"key":"M1","note":"地图","pointerId":3,"posX":1244,"posY":43,"time":80,"type":"Tap"},{"key":"KeyV","note":"","pointerId":3,"posX":1156,"posY":300,"time":80,"type":"Tap"},{"key":"M4","note":"","pointerId":3,"posX":862,"posY":180,"time":80,"type":"Tap"},{"key":"KeyT","note":"","pointerId":3,"posX":880,"posY":674,"time":80,"type":"Tap"},{"key":"KeyQ","note":"","pointerId":3,"posX":400,"posY":679,"time":80,"type":"Tap"},{"key":"Digit4","note":"","pointerId":3,"posX":766,"posY":251,"time":80,"type":"Tap"},{"key":"Digit5","note":"","pointerId":3,"posX":770,"posY":320,"time":80,"type":"Tap"},{"key":"Digit3","note":"","pointerId":3,"posX":788,"posY":591,"time":80,"type":"Tap"}],"relativeSize":{"h":720,"w":1280},"title":"AVD-和平精英-导入默认"}
|
||||
{"list":[{"key":"Backquote","note":"准星键","pointerId":0,"posX":640,"posY":361,"scaleX":1,"scaleY":1,"type":"Sight"},{"key":{"down":"KeyS","left":"KeyA","right":"KeyD","up":"KeyW"},"note":"","offset":150,"pointerId":1,"posX":208,"posY":542,"type":"SteeringWheel"},{"key":"KeyC","note":"","pointerId":3,"posX":1085,"posY":668,"time":80,"type":"Tap"},{"key":"KeyZ","note":"","pointerId":3,"posX":1207,"posY":648,"time":80,"type":"Tap"},{"key":"Space","note":"","pointerId":3,"posX":1230,"posY":503,"time":80,"type":"Tap"},{"key":"M3","note":"","pointerId":4,"posX":992,"posY":229,"scale":0.6,"type":"Observation"},{"key":"Digit1","note":"","pointerId":3,"posX":543,"posY":642,"time":80,"type":"Tap"},{"key":"Digit2","note":"","pointerId":3,"posX":729,"posY":644,"time":80,"type":"Tap"},{"key":"ShiftLeft","note":"","pointerId":3,"posX":1075,"posY":194,"time":80,"type":"Tap"},{"key":"KeyF","note":"","pointerId":3,"posX":861,"posY":310,"time":80,"type":"Tap"},{"key":"KeyG","note":"","pointerId":3,"posX":865,"posY":388,"time":80,"type":"Tap"},{"key":"KeyR","note":"","pointerId":3,"posX":976,"posY":674,"time":80,"type":"Tap"},{"key":"KeyE","note":"","pointerId":3,"posX":872,"posY":495,"time":80,"type":"Tap"},{"key":"KeyB","note":"","pointerId":3,"posX":543,"posY":593,"time":80,"type":"Tap"},{"key":"KeyN","note":"","pointerId":3,"posX":721,"posY":589,"time":80,"type":"Tap"},{"drag":false,"note":"","pointerId":2,"posX":1097,"posY":549,"scaleX":0.5,"scaleY":0.5,"type":"Fire"},{"key":"M2","note":"","pointerId":3,"posX":1227,"posY":376,"time":80,"type":"Tap"},{"key":"Tab","macro":{"down":[{"args":["default",5,100,650,80],"type":"touch"}],"loop":null,"up":[{"args":["default",5,1250,40,80],"type":"touch"}]},"note":"背包宏","posX":95,"posY":656,"type":"Macro"},{"key":"M1","note":"地图","pointerId":3,"posX":1244,"posY":43,"time":80,"type":"Tap"},{"key":"KeyV","note":"","pointerId":3,"posX":1156,"posY":300,"time":80,"type":"Tap"},{"key":"M4","note":"","pointerId":3,"posX":862,"posY":180,"time":80,"type":"Tap"},{"key":"KeyT","note":"","pointerId":3,"posX":880,"posY":674,"time":80,"type":"Tap"},{"key":"KeyQ","note":"","pointerId":3,"posX":400,"posY":679,"time":80,"type":"Tap"},{"key":"Digit4","note":"","pointerId":3,"posX":766,"posY":251,"time":80,"type":"Tap"},{"key":"Digit5","note":"","pointerId":3,"posX":770,"posY":320,"time":80,"type":"Tap"},{"key":"Digit3","note":"","pointerId":3,"posX":788,"posY":591,"time":80,"type":"Tap"}],"relativeSize":{"h":720,"w":1280},"title":"AVD-和平精英-导入默认"},
|
||||
{"list":[{"key":{"down":"KeyS","left":"KeyA","right":"KeyD","up":"KeyW"},"note":"","offset":150,"pointerId":1,"posX":204,"posY":531,"type":"SteeringWheel"},{"key":"KeyE","note":"","pointerId":3,"posX":1036,"posY":660,"time":80,"type":"Tap"},{"key":"KeyL","note":"","pointerId":2,"posX":1166,"posY":328,"type":"DirectionlessSkill"},{"key":"KeyI","note":"","pointerId":3,"posX":947,"posY":657,"time":80,"type":"Tap"},{"key":"KeyU","note":"","pointerId":3,"posX":970,"posY":542,"time":80,"type":"Tap"},{"key":"KeyF","note":"","pointerId":3,"posX":1056,"posY":458,"time":80,"type":"Tap"},{"key":"Digit1","note":"","pointerId":3,"posX":560,"posY":666,"time":80,"type":"Tap"},{"key":"Digit2","note":"","pointerId":3,"posX":639,"posY":666,"time":80,"type":"Tap"},{"key":"KeyZ","note":"","pointerId":3,"posX":1163,"posY":433,"time":80,"type":"Tap"},{"key":"Escape","note":"","pointerId":3,"posX":1198,"posY":43,"time":80,"type":"Tap"},{"key":"Enter","macro":{"down":[{"args":[],"type":"key-input-mode"}],"loop":null,"up":null},"note":"","posX":1086,"posY":85,"type":"Macro"},{"key":"KeyK","note":"","pointerId":3,"posX":1188,"posY":528,"time":80,"type":"Tap"},{"key":"Space","note":"","pointerId":3,"posX":732,"posY":653,"time":80,"type":"Tap"},{"key":"Semicolon","note":"","pointerId":3,"posX":1200,"posY":234,"time":80,"type":"Tap"},{"key":"KeyN","note":"","pointerId":3,"posX":1054,"posY":232,"time":80,"type":"Tap"},{"key":"KeyO","note":"","pointerId":3,"posX":1133,"posY":232,"time":80,"type":"Tap"},{"key":"ShiftLeft","note":"","pointerId":2,"posX":991,"posY":233,"type":"DirectionlessSkill"},{"key":"KeyC","note":"","pointerId":2,"posX":1131,"posY":628,"type":"DirectionlessSkill"},{"key":"KeyQ","note":"","pointerId":2,"posX":838,"posY":656,"type":"DirectionlessSkill"},{"key":"KeyJ","note":"","pointerId":3,"posX":1132,"posY":628,"time":80,"type":"Tap"},{"intervalBetweenPos":100,"key":"KeyH","note":"","pointerId":3,"pos":[{"x":1055,"y":357},{"x":1059,"y":430}],"posX":1053,"posY":393,"type":"Swipe"},{"type":"Swipe","key":"BracketLeft","note":"","posX":987,"posY":354,"pointerId":3,"pos":[{"x":1055,"y":356},{"x":942,"y":356}],"intervalBetweenPos":100},{"type":"Swipe","key":"Backslash","note":"","posX":1055,"posY":306,"pointerId":3,"pos":[{"x":1052,"y":356},{"x":1052,"y":288}],"intervalBetweenPos":100},{"type":"Swipe","key":"BracketRight","note":"","posX":1094,"posY":353,"pointerId":3,"pos":[{"x":1050,"y":356},{"x":1117,"y":354}],"intervalBetweenPos":100}],"relativeSize":{"h":720,"w":1280},"title":"AVD-DNF-狂战-导入默认"}
|
||||
]
|
||||
|
@ -4,5 +4,5 @@ pub mod client;
|
||||
pub mod control_msg;
|
||||
pub mod resource;
|
||||
pub mod scrcpy_mask_cmd;
|
||||
pub mod socket;
|
||||
pub mod share;
|
||||
pub mod socket;
|
||||
|
@ -181,6 +181,7 @@ fn set_adb_path(adb_path: String, app: tauri::AppHandle) -> Result<(), String> {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"productName": "scrcpy-mask",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"identifier": "com.akichase.mask",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
|
@ -58,4 +58,9 @@ onMounted(async () => {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.n-message {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -45,6 +45,7 @@ import { useGlobalStore } from "../store/global";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { closeExternalControl, connectExternalControl } from "../websocket";
|
||||
import { LogicalSize, getCurrent } from "@tauri-apps/api/window";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
|
||||
const { t } = useI18n();
|
||||
const dialog = useDialog();
|
||||
@ -73,15 +74,8 @@ onMounted(async () => {
|
||||
case "ClipboardChanged":
|
||||
if (payload.clipboard === lastClipboard) break;
|
||||
lastClipboard = payload.clipboard;
|
||||
navigator.clipboard
|
||||
.writeText(payload.clipboard)
|
||||
.then(() => {
|
||||
message.info(t("pages.Device.clipboard.deviceSync.success"));
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
message.error(t("pages.Device.clipboard.deviceSync.failed"));
|
||||
});
|
||||
writeText(payload.clipboard);
|
||||
console.log(payload);
|
||||
break;
|
||||
case "ClipboardSetAck":
|
||||
break;
|
||||
|
@ -50,6 +50,7 @@ async function maximizeOrRestore() {
|
||||
align-items: center;
|
||||
border-radius: 0 10px 0 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
.n-button-group{
|
||||
flex-shrink: 0;
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
listenToEvent,
|
||||
unlistenToEvent,
|
||||
} from "../hotkey";
|
||||
import { KeyMappingConfig, KeySteeringWheel } from "../keyMappingConfig";
|
||||
import { KeySteeringWheel } from "../keyMappingConfig";
|
||||
import ScreenStream from "./ScreenStream.vue";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
@ -19,6 +19,7 @@ import { getCurrent, PhysicalSize } from "@tauri-apps/api/window";
|
||||
import { Store } from "@tauri-apps/plugin-store";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { checkAdbAvailable } from "../invoke";
|
||||
import { loadLocalStorage } from "../storeLoader";
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useGlobalStore();
|
||||
@ -103,79 +104,7 @@ function genClientId() {
|
||||
|
||||
async function loadLocalStore() {
|
||||
const localStore = new Store("store.bin");
|
||||
// loading keyMappingConfigList from local store
|
||||
let keyMappingConfigList = await localStore.get<KeyMappingConfig[]>(
|
||||
"keyMappingConfigList"
|
||||
);
|
||||
if (keyMappingConfigList === null || keyMappingConfigList.length === 0) {
|
||||
// add empty key mapping config
|
||||
// unable to get mask element when app is not ready
|
||||
// so we use the stored mask area to get relative size
|
||||
const maskArea = await localStore.get<{
|
||||
posX: number;
|
||||
posY: number;
|
||||
sizeW: number;
|
||||
sizeH: number;
|
||||
}>("maskArea");
|
||||
let relativeSize = { w: 800, h: 600 };
|
||||
if (maskArea !== null) {
|
||||
relativeSize = {
|
||||
w: maskArea.sizeW,
|
||||
h: maskArea.sizeH,
|
||||
};
|
||||
}
|
||||
keyMappingConfigList = [
|
||||
{
|
||||
relativeSize,
|
||||
title: t("pages.Mask.blankConfig"),
|
||||
list: [],
|
||||
},
|
||||
];
|
||||
await localStore.set("keyMappingConfigList", keyMappingConfigList);
|
||||
}
|
||||
store.keyMappingConfigList = keyMappingConfigList;
|
||||
|
||||
// loading curKeyMappingIndex from local store
|
||||
let curKeyMappingIndex = await localStore.get<number>("curKeyMappingIndex");
|
||||
if (
|
||||
curKeyMappingIndex === null ||
|
||||
curKeyMappingIndex >= keyMappingConfigList.length
|
||||
) {
|
||||
curKeyMappingIndex = 0;
|
||||
localStore.set("curKeyMappingIndex", curKeyMappingIndex);
|
||||
}
|
||||
store.curKeyMappingIndex = curKeyMappingIndex;
|
||||
|
||||
// loading maskButton from local store
|
||||
let maskButton = await localStore.get<{
|
||||
show: boolean;
|
||||
transparency: number;
|
||||
}>("maskButton");
|
||||
store.maskButton = maskButton ?? {
|
||||
show: true,
|
||||
transparency: 0.5,
|
||||
};
|
||||
|
||||
// loading checkUpdateAtStart from local store
|
||||
const checkUpdateAtStart = await localStore.get<boolean>(
|
||||
"checkUpdateAtStart"
|
||||
);
|
||||
store.checkUpdateAtStart = checkUpdateAtStart ?? true;
|
||||
|
||||
// loading rotation from local store
|
||||
const rotation = await localStore.get<{
|
||||
enable: boolean;
|
||||
verticalLength: number;
|
||||
horizontalLength: number;
|
||||
}>("rotation");
|
||||
if (rotation) store.rotation = rotation;
|
||||
|
||||
// loading screenStream from local store
|
||||
const screenStream = await localStore.get<{
|
||||
enable: boolean;
|
||||
address: string;
|
||||
}>("screenStream");
|
||||
if (screenStream) store.screenStream = screenStream;
|
||||
await loadLocalStorage(localStore, store, t);
|
||||
}
|
||||
|
||||
async function cleanAfterimage() {
|
||||
|
@ -66,10 +66,12 @@ onBeforeUnmount(() => {
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
img {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -8,6 +8,7 @@ import KeySkill from "./KeySkill.vue";
|
||||
import KeyObservation from "./KeyObservation.vue";
|
||||
import KeySight from "./KeySight.vue";
|
||||
import KeyFire from "./KeyFire.vue";
|
||||
import KeySwipe from "./KeySwipe.vue";
|
||||
import ScreenStream from "../ScreenStream.vue";
|
||||
|
||||
import {
|
||||
@ -17,6 +18,7 @@ import {
|
||||
KeyTap,
|
||||
KeyMacro,
|
||||
KeyMapping,
|
||||
KeySwipe as KeyMappingKeySwipe,
|
||||
KeySight as KeyMappingKeySight,
|
||||
KeyFire as KeyMappingKeyFire,
|
||||
} from "../../keyMappingConfig";
|
||||
@ -43,6 +45,10 @@ const addButtonOptions: DropdownOption[] = [
|
||||
label: () => t("pages.KeyBoard.addButton.SteeringWheel"),
|
||||
key: "SteeringWheel",
|
||||
},
|
||||
{
|
||||
label: () => t("pages.KeyBoard.addButton.Swipe"),
|
||||
key: "Swipe",
|
||||
},
|
||||
{
|
||||
label: () => t("pages.KeyBoard.addButton.Skill"),
|
||||
key: "DirectionalSkill",
|
||||
@ -72,6 +78,7 @@ const addButtonOptions: DropdownOption[] = [
|
||||
function onAddButtonSelect(
|
||||
type:
|
||||
| "Tap"
|
||||
| "Swipe"
|
||||
| "SteeringWheel"
|
||||
| "DirectionalSkill"
|
||||
| "CancelSkill"
|
||||
@ -92,6 +99,12 @@ function onAddButtonSelect(
|
||||
if (type === "Tap") {
|
||||
keyMapping.pointerId = 3;
|
||||
(keyMapping as KeyTap).time = 80;
|
||||
} else if (type === "Swipe") {
|
||||
keyMapping.pointerId = 3;
|
||||
(keyMapping as KeyMappingKeySwipe).pos = [
|
||||
{ x: keyMapping.posX, y: keyMapping.posY },
|
||||
];
|
||||
(keyMapping as KeyMappingKeySwipe).intervalBetweenPos = 100;
|
||||
} else if (type === "SteeringWheel") {
|
||||
keyMapping.pointerId = 1;
|
||||
(keyMapping as unknown as KeyMappingSteeringWheel).key = {
|
||||
@ -139,6 +152,7 @@ function onAddButtonSelect(
|
||||
} else return;
|
||||
keyboardStore.edited = true;
|
||||
store.editKeyMappingList.push(keyMapping as KeyMapping);
|
||||
keyboardStore.activeButtonIndex = store.editKeyMappingList.length - 1;
|
||||
}
|
||||
|
||||
function isKeyUnique(curKey: string): boolean {
|
||||
@ -172,6 +186,7 @@ function setCurButtonKey(curKey: string) {
|
||||
keyboardStore.showButtonSettingFlag ||
|
||||
keyboardStore.activeButtonIndex >= store.editKeyMappingList.length ||
|
||||
keyboardStore.showButtonSettingFlag ||
|
||||
keyboardStore.editSwipePointsFlag ||
|
||||
keyboardStore.showButtonAddFlag
|
||||
)
|
||||
return;
|
||||
@ -362,6 +377,10 @@ onBeforeRouteLeave(() => {
|
||||
v-else-if="store.editKeyMappingList[index].type === 'Observation'"
|
||||
:index="index"
|
||||
/>
|
||||
<KeySwipe
|
||||
v-else-if="store.editKeyMappingList[index].type === 'Swipe'"
|
||||
:index="index"
|
||||
/>
|
||||
<KeySight
|
||||
v-else-if="store.editKeyMappingList[index].type === 'Sight'"
|
||||
:index="index"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Settings, CloseCircle } from "@vicons/ionicons5";
|
||||
import { Settings, CloseCircle, ReturnUpBack } from "@vicons/ionicons5";
|
||||
import {
|
||||
NButton,
|
||||
NIcon,
|
||||
@ -19,6 +19,7 @@ import { loadDefaultKeyconfig } from "../../invoke";
|
||||
import { KeyMappingConfig } from "../../keyMappingConfig";
|
||||
import { useKeyboardStore } from "../../store/keyboard";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useGlobalStore();
|
||||
@ -121,14 +122,18 @@ function dragHandler(downEvent: MouseEvent) {
|
||||
localStore.set("keySettingPos", keySettingPos.value);
|
||||
} else {
|
||||
// click up
|
||||
keyboardStore.activeButtonIndex = -1;
|
||||
keyboardStore.activeSteeringWheelButtonKeyIndex = -1;
|
||||
keyboardStore.showSettingFlag = !keyboardStore.showSettingFlag;
|
||||
if (
|
||||
keyboardStore.showSettingFlag &&
|
||||
store.keyMappingConfigList.length === 1
|
||||
) {
|
||||
message.info(t("pages.KeyBoard.KeySetting.onlyOneConfig"));
|
||||
if (keyboardStore.editSwipePointsFlag) {
|
||||
keyboardStore.editSwipePointsFlag = false;
|
||||
} else {
|
||||
keyboardStore.activeButtonIndex = -1;
|
||||
keyboardStore.activeSteeringWheelButtonKeyIndex = -1;
|
||||
keyboardStore.showSettingFlag = !keyboardStore.showSettingFlag;
|
||||
if (
|
||||
keyboardStore.showSettingFlag &&
|
||||
store.keyMappingConfigList.length === 1
|
||||
) {
|
||||
message.info(t("pages.KeyBoard.KeySetting.onlyOneConfig"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -251,8 +256,7 @@ function renameKeyMappingConfig() {
|
||||
function exportKeyMappingConfig() {
|
||||
const config = store.keyMappingConfigList[store.curKeyMappingIndex];
|
||||
const data = JSON.stringify(config, null, 2);
|
||||
navigator.clipboard
|
||||
.writeText(data)
|
||||
writeText(data)
|
||||
.then(() => {
|
||||
message.success(t("pages.KeyBoard.KeySetting.exportSuccess"));
|
||||
})
|
||||
@ -374,7 +378,10 @@ function resetKeyMappingConfig() {
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><Settings /></NIcon>
|
||||
<NIcon>
|
||||
<ReturnUpBack v-if="keyboardStore.editSwipePointsFlag" />
|
||||
<Settings v-else />
|
||||
</NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
<div
|
||||
|
355
src/components/keyboard/KeySwipe.vue
Normal file
@ -0,0 +1,355 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { useGlobalStore } from "../../store/global";
|
||||
import {
|
||||
NIcon,
|
||||
NButton,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NH4,
|
||||
NInputNumber,
|
||||
useMessage,
|
||||
} from "naive-ui";
|
||||
import { Analytics, CloseCircle, Settings } from "@vicons/ionicons5";
|
||||
import { useKeyboardStore } from "../../store/keyboard";
|
||||
import { KeySwipe } from "../../keyMappingConfig";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{
|
||||
index: number;
|
||||
}>();
|
||||
|
||||
const keyboardStore = useKeyboardStore();
|
||||
const message = useMessage();
|
||||
const store = useGlobalStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const elementRef = ref<HTMLElement | null>(null);
|
||||
|
||||
const isActive = computed(
|
||||
() => props.index === keyboardStore.activeButtonIndex
|
||||
);
|
||||
const keyMapping = computed(
|
||||
() => store.editKeyMappingList[props.index] as KeySwipe
|
||||
);
|
||||
|
||||
const trackPoints = computed(() => {
|
||||
let s = "";
|
||||
if (isActive.value) {
|
||||
for (const point of keyMapping.value.pos) {
|
||||
s += `${point.x},${point.y} `;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
||||
function dragHandler(downEvent: MouseEvent) {
|
||||
keyboardStore.activeButtonIndex = props.index;
|
||||
keyboardStore.showButtonSettingFlag = false;
|
||||
const oldX = keyMapping.value.posX;
|
||||
const oldY = keyMapping.value.posY;
|
||||
const element = elementRef.value;
|
||||
if (element) {
|
||||
const keyboardElement = document.getElementById(
|
||||
"keyboardElement"
|
||||
) as HTMLElement;
|
||||
const maxX = keyboardElement.clientWidth - 30;
|
||||
const maxY = keyboardElement.clientHeight - 30;
|
||||
|
||||
const x = downEvent.clientX;
|
||||
const y = downEvent.clientY;
|
||||
const moveHandler = (moveEvent: MouseEvent) => {
|
||||
let newX = oldX + moveEvent.clientX - x;
|
||||
let newY = oldY + moveEvent.clientY - y;
|
||||
newX = Math.max(30, Math.min(newX, maxX));
|
||||
newY = Math.max(30, Math.min(newY, maxY));
|
||||
keyMapping.value.posX = newX;
|
||||
keyMapping.value.posY = newY;
|
||||
};
|
||||
window.addEventListener("mousemove", moveHandler);
|
||||
const upHandler = () => {
|
||||
window.removeEventListener("mousemove", moveHandler);
|
||||
window.removeEventListener("mouseup", upHandler);
|
||||
if (oldX !== keyMapping.value.posX || oldY !== keyMapping.value.posY) {
|
||||
keyboardStore.edited = true;
|
||||
}
|
||||
};
|
||||
window.addEventListener("mouseup", upHandler);
|
||||
}
|
||||
}
|
||||
|
||||
function delCurKeyMapping() {
|
||||
keyboardStore.edited = true;
|
||||
keyboardStore.activeButtonIndex = -1;
|
||||
store.editKeyMappingList.splice(props.index, 1);
|
||||
}
|
||||
|
||||
const settingPosX = ref(0);
|
||||
const settingPosY = ref(0);
|
||||
function showSetting() {
|
||||
const keyboardElement = document.getElementById(
|
||||
"keyboardElement"
|
||||
) as HTMLElement;
|
||||
const maxWidth = keyboardElement.clientWidth - 150;
|
||||
const maxHeight = keyboardElement.clientHeight - 380;
|
||||
|
||||
settingPosX.value = Math.min(keyMapping.value.posX + 40, maxWidth);
|
||||
settingPosY.value = Math.min(keyMapping.value.posY - 40, maxHeight);
|
||||
keyboardStore.showButtonSettingFlag = true;
|
||||
}
|
||||
|
||||
function editSwipePoints() {
|
||||
message.info(t("pages.KeyBoard.Swipe.editTips"));
|
||||
keyboardStore.showButtonSettingFlag = false;
|
||||
keyboardStore.editSwipePointsFlag = true;
|
||||
keyboardStore.edited = true;
|
||||
}
|
||||
|
||||
function swipePointDragHandlue(downEvent: MouseEvent, index: number) {
|
||||
if (downEvent.button === 2) {
|
||||
// del point
|
||||
keyMapping.value.pos.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
if (downEvent.button !== 0) return;
|
||||
|
||||
const oldX = keyMapping.value.pos[index].x;
|
||||
const oldY = keyMapping.value.pos[index].y;
|
||||
const keyboardElement = document.getElementById(
|
||||
"keyboardElement"
|
||||
) as HTMLElement;
|
||||
const maxX = keyboardElement.clientWidth;
|
||||
const maxY = keyboardElement.clientHeight;
|
||||
|
||||
const x = downEvent.clientX;
|
||||
const y = downEvent.clientY;
|
||||
const moveHandler = (moveEvent: MouseEvent) => {
|
||||
let newX = oldX + moveEvent.clientX - x;
|
||||
let newY = oldY + moveEvent.clientY - y;
|
||||
newX = Math.max(0, Math.min(newX, maxX));
|
||||
newY = Math.max(0, Math.min(newY, maxY));
|
||||
keyMapping.value.pos[index].x = newX;
|
||||
keyMapping.value.pos[index].y = newY;
|
||||
};
|
||||
const upHandler = () => {
|
||||
window.removeEventListener("mousemove", moveHandler);
|
||||
window.removeEventListener("mouseup", upHandler);
|
||||
};
|
||||
window.addEventListener("mousemove", moveHandler);
|
||||
window.addEventListener("mouseup", upHandler);
|
||||
}
|
||||
|
||||
function swipeTrackClickHandler(event: MouseEvent) {
|
||||
if (event.button !== 0) return;
|
||||
console.log(event.target, event.currentTarget);
|
||||
if (event.target !== event.currentTarget) return;
|
||||
keyMapping.value.pos.push({ x: event.clientX - 70, y: event.clientY - 30 });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{ active: isActive }"
|
||||
:style="{
|
||||
left: `${keyMapping.posX - 30}px`,
|
||||
top: `${keyMapping.posY - 30}px`,
|
||||
}"
|
||||
@mousedown="dragHandler"
|
||||
class="key-swipe"
|
||||
ref="elementRef"
|
||||
>
|
||||
<NIcon size="30"><Analytics /></NIcon>
|
||||
<span>{{ keyMapping.key }}</span>
|
||||
<NButton
|
||||
class="key-close-btn"
|
||||
text
|
||||
@click="delCurKeyMapping"
|
||||
:type="isActive ? 'primary' : 'info'"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon size="15">
|
||||
<CloseCircle />
|
||||
</NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
<NButton
|
||||
class="key-setting-btn"
|
||||
text
|
||||
@click="showSetting"
|
||||
:type="isActive ? 'primary' : 'info'"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon size="15">
|
||||
<Settings />
|
||||
</NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
<div
|
||||
class="key-setting"
|
||||
v-if="isActive && keyboardStore.showButtonSettingFlag"
|
||||
:style="{
|
||||
left: `${settingPosX}px`,
|
||||
top: `${settingPosY}px`,
|
||||
}"
|
||||
>
|
||||
<NH4 prefix="bar">{{ $t("pages.KeyBoard.Swipe.swipe") }}</NH4>
|
||||
<NFormItem :label="$t('pages.KeyBoard.Swipe.pos')">
|
||||
<NButton type="success" @click="editSwipePoints">{{
|
||||
$t("pages.KeyBoard.Swipe.editPos")
|
||||
}}</NButton>
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('pages.KeyBoard.Swipe.interval')">
|
||||
<NInputNumber
|
||||
v-model:value="keyMapping.intervalBetweenPos"
|
||||
:placeholder="$t('pages.KeyBoard.Swipe.intervalPlaceholder')"
|
||||
@update:value="keyboardStore.edited = true"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('pages.KeyBoard.setting.pointerID')">
|
||||
<NInputNumber
|
||||
v-model:value="keyMapping.pointerId"
|
||||
:min="0"
|
||||
:placeholder="$t('pages.KeyBoard.setting.pointerIDPlaceholder')"
|
||||
@update:value="keyboardStore.edited = true"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem :label="$t('pages.KeyBoard.setting.note')">
|
||||
<NInput
|
||||
v-model:value="keyMapping.note"
|
||||
:placeholder="$t('pages.KeyBoard.setting.notePlaceholder')"
|
||||
@update:value="keyboardStore.edited = true"
|
||||
/>
|
||||
</NFormItem>
|
||||
</div>
|
||||
|
||||
<template v-if="isActive">
|
||||
<div
|
||||
v-if="isActive"
|
||||
class="track"
|
||||
:class="{ 'edit-track': keyboardStore.editSwipePointsFlag }"
|
||||
>
|
||||
<svg @click="swipeTrackClickHandler">
|
||||
<polyline :points="trackPoints" />
|
||||
<circle
|
||||
v-for="(pos, i) in keyMapping.pos"
|
||||
:cx="pos.x"
|
||||
:cy="pos.y"
|
||||
r="5"
|
||||
@mousedown="(e) => swipePointDragHandlue(e, i)"
|
||||
/>
|
||||
<text v-for="(pos, i) in keyMapping.pos" :x="pos.x + 5" :y="pos.y - 5">
|
||||
{{ i }}
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.track {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
|
||||
svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
polyline {
|
||||
fill: none;
|
||||
stroke: var(--primary-hover-color);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
circle {
|
||||
cursor: pointer;
|
||||
fill: var(--primary-color);
|
||||
|
||||
&:hover {
|
||||
fill: var(--primary-pressed-color);
|
||||
}
|
||||
}
|
||||
|
||||
text {
|
||||
cursor: default;
|
||||
fill: var(--primary-pressed-color);
|
||||
font-size: 15px;
|
||||
text-anchor: end-alignment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.edit-track {
|
||||
z-index: 4;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.key-setting {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
width: 150px;
|
||||
height: 380px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid var(--light-color);
|
||||
background-color: var(--bg-color);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.key-swipe {
|
||||
position: absolute;
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--blue-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
|
||||
.n-icon {
|
||||
color: var(--blue-color);
|
||||
}
|
||||
|
||||
&:not(.active):hover {
|
||||
border: 2px solid var(--light-color);
|
||||
color: var(--light-color);
|
||||
|
||||
.n-icon {
|
||||
color: var(--light-color);
|
||||
}
|
||||
}
|
||||
|
||||
.key-close-btn {
|
||||
position: absolute;
|
||||
left: 65px;
|
||||
bottom: 45px;
|
||||
}
|
||||
|
||||
.key-setting-btn {
|
||||
position: absolute;
|
||||
left: 65px;
|
||||
top: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 2px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
z-index: 2;
|
||||
|
||||
.n-icon {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,23 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { Store } from "@tauri-apps/plugin-store";
|
||||
import { Refresh, TrashBinOutline } from "@vicons/ionicons5";
|
||||
import {
|
||||
NH4,
|
||||
NP,
|
||||
NButton,
|
||||
NFlex,
|
||||
NList,
|
||||
NListItem,
|
||||
NModal,
|
||||
NInput,
|
||||
useDialog,
|
||||
NCard,
|
||||
NIcon,
|
||||
NSelect,
|
||||
NInputGroup,
|
||||
useMessage,
|
||||
NFlex,
|
||||
NCheckbox,
|
||||
NTooltip,
|
||||
} from "naive-ui";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { onMounted, ref } from "vue";
|
||||
import i18n from "../../i18n";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@ -28,12 +21,6 @@ const { t } = useI18n();
|
||||
const localStore = new Store("store.bin");
|
||||
const store = useGlobalStore();
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
|
||||
const localStoreEntries = ref<[string, unknown][]>([]);
|
||||
const showDataModal = ref(false);
|
||||
const dataModalInputVal = ref("");
|
||||
let curDataIndex = -1;
|
||||
|
||||
const languageOptions = [
|
||||
{ label: "简体中文", value: "zh-CN" },
|
||||
@ -45,52 +32,10 @@ const curLanguage = ref("en-US");
|
||||
const adbPath = ref("");
|
||||
|
||||
onMounted(async () => {
|
||||
refreshLocalData();
|
||||
curLanguage.value = (await localStore.get<string>("language")) ?? "en-US";
|
||||
adbPath.value = (await localStore.get<string>("adbPath")) ?? "";
|
||||
});
|
||||
|
||||
async function refreshLocalData() {
|
||||
localStoreEntries.value = await localStore.entries();
|
||||
}
|
||||
|
||||
function showLocalStore(index: number) {
|
||||
curDataIndex = index;
|
||||
dataModalInputVal.value = JSON.stringify(
|
||||
localStoreEntries.value[index][1],
|
||||
null,
|
||||
2
|
||||
);
|
||||
showDataModal.value = true;
|
||||
}
|
||||
|
||||
function delLocalStore(key?: string) {
|
||||
if (key) {
|
||||
dialog.warning({
|
||||
title: t("pages.Setting.Basic.delLocalStore.dialog.title"),
|
||||
content: t("pages.Setting.Basic.delLocalStore.dialog.delKey", [key]),
|
||||
positiveText: t("pages.Setting.Basic.delLocalStore.dialog.positiveText"),
|
||||
negativeText: t("pages.Setting.Basic.delLocalStore.dialog.negativeText"),
|
||||
onPositiveClick: () => {
|
||||
localStore.delete(key);
|
||||
localStoreEntries.value.splice(curDataIndex, 1);
|
||||
showDataModal.value = false;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dialog.warning({
|
||||
title: t("pages.Setting.Basic.delLocalStore.dialog.title"),
|
||||
content: t("pages.Setting.Basic.delLocalStore.dialog.delAll"),
|
||||
positiveText: t("pages.Setting.Basic.delLocalStore.dialog.positiveText"),
|
||||
negativeText: t("pages.Setting.Basic.delLocalStore.dialog.negativeText"),
|
||||
onPositiveClick: () => {
|
||||
localStore.clear();
|
||||
relaunch();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function changeLanguage(language: "zh-CN" | "en-US") {
|
||||
if (language === curLanguage.value) return;
|
||||
curLanguage.value = language;
|
||||
@ -106,6 +51,10 @@ async function adjustAdbPath() {
|
||||
adbPath.value = (await localStore.get<string>("adbPath")) ?? "";
|
||||
store.hideLoading();
|
||||
}
|
||||
|
||||
function changeClipboardSync() {
|
||||
localStore.set("clipboardSync", store.clipboardSync);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -128,72 +77,32 @@ async function adjustAdbPath() {
|
||||
$t("pages.Setting.Basic.adbPath.set")
|
||||
}}</NButton>
|
||||
</NInputGroup>
|
||||
<NFlex justify="space-between">
|
||||
<NH4 prefix="bar">{{ $t("pages.Setting.Basic.localStore") }}</NH4>
|
||||
<NFlex>
|
||||
<NButton
|
||||
tertiary
|
||||
circle
|
||||
type="primary"
|
||||
@click="delLocalStore()"
|
||||
style="margin-right: 20px"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><TrashBinOutline /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
<NButton
|
||||
tertiary
|
||||
circle
|
||||
type="primary"
|
||||
@click="refreshLocalData()"
|
||||
style="margin-right: 20px"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><Refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</NFlex>
|
||||
<NH4 prefix="bar">剪切板同步</NH4>
|
||||
<NFlex vertical>
|
||||
<NCheckbox
|
||||
v-model:checked="store.clipboardSync.syncFromDevice"
|
||||
@update:checked="changeClipboardSync"
|
||||
>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>从设备同步</template>
|
||||
设备剪切板发生变化时自动同步更新电脑剪切板
|
||||
</NTooltip>
|
||||
</NCheckbox>
|
||||
<NCheckbox
|
||||
v-model:checked="store.clipboardSync.pasteFromPC"
|
||||
@update:checked="changeClipboardSync"
|
||||
>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>粘贴时同步</template>
|
||||
在按键输入模式下,按下 Ctrl + V 可将电脑剪切板内容同步粘贴到设备
|
||||
</NTooltip>
|
||||
</NCheckbox>
|
||||
</NFlex>
|
||||
<NP>{{ $t("pages.Setting.Basic.delLocalStore.warning") }}</NP>
|
||||
<NList class="data-list" hoverable clickable>
|
||||
<NListItem v-for="(entrie, index) in localStoreEntries">
|
||||
<div @click="showLocalStore(index)">
|
||||
{{ entrie[0] }}
|
||||
</div>
|
||||
</NListItem>
|
||||
</NList>
|
||||
</div>
|
||||
<NModal v-model:show="showDataModal">
|
||||
<NCard
|
||||
style="width: 50%; height: 80%"
|
||||
:title="localStoreEntries[curDataIndex][0]"
|
||||
>
|
||||
<NFlex vertical style="height: 100%">
|
||||
<NInput
|
||||
type="textarea"
|
||||
style="flex-grow: 1"
|
||||
:value="dataModalInputVal"
|
||||
round
|
||||
readonly
|
||||
/>
|
||||
<NButton
|
||||
type="success"
|
||||
round
|
||||
@click="delLocalStore(localStoreEntries[curDataIndex][0])"
|
||||
>{{ $t("pages.Setting.Basic.delCurData") }}</NButton
|
||||
>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.setting-page {
|
||||
padding: 10px 25px;
|
||||
|
||||
.data-list {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
149
src/components/setting/Data.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<script setup lang="ts">
|
||||
import { Store } from "@tauri-apps/plugin-store";
|
||||
import { Refresh, TrashBinOutline } from "@vicons/ionicons5";
|
||||
import {
|
||||
NH4,
|
||||
NP,
|
||||
NButton,
|
||||
NFlex,
|
||||
NList,
|
||||
NListItem,
|
||||
NModal,
|
||||
NInput,
|
||||
useDialog,
|
||||
NCard,
|
||||
NIcon,
|
||||
} from "naive-ui";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const localStore = new Store("store.bin");
|
||||
const dialog = useDialog();
|
||||
|
||||
const localStoreEntries = ref<[string, unknown][]>([]);
|
||||
const showDataModal = ref(false);
|
||||
const dataModalInputVal = ref("");
|
||||
let curDataIndex = -1;
|
||||
|
||||
onMounted(async () => {
|
||||
refreshLocalData();
|
||||
});
|
||||
|
||||
async function refreshLocalData() {
|
||||
localStoreEntries.value = await localStore.entries();
|
||||
}
|
||||
|
||||
function showLocalStore(index: number) {
|
||||
curDataIndex = index;
|
||||
dataModalInputVal.value = JSON.stringify(
|
||||
localStoreEntries.value[index][1],
|
||||
null,
|
||||
2
|
||||
);
|
||||
showDataModal.value = true;
|
||||
}
|
||||
|
||||
function delLocalStore(key?: string) {
|
||||
if (key) {
|
||||
dialog.warning({
|
||||
title: t("pages.Setting.Data.delLocalStore.dialog.title"),
|
||||
content: t("pages.Setting.Data.delLocalStore.dialog.delKey", [key]),
|
||||
positiveText: t("pages.Setting.Data.delLocalStore.dialog.positiveText"),
|
||||
negativeText: t("pages.Setting.Data.delLocalStore.dialog.negativeText"),
|
||||
onPositiveClick: () => {
|
||||
localStore.delete(key);
|
||||
localStoreEntries.value.splice(curDataIndex, 1);
|
||||
showDataModal.value = false;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dialog.warning({
|
||||
title: t("pages.Setting.Data.delLocalStore.dialog.title"),
|
||||
content: t("pages.Setting.Data.delLocalStore.dialog.delAll"),
|
||||
positiveText: t("pages.Setting.Data.delLocalStore.dialog.positiveText"),
|
||||
negativeText: t("pages.Setting.Data.delLocalStore.dialog.negativeText"),
|
||||
onPositiveClick: () => {
|
||||
localStore.clear();
|
||||
relaunch();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="setting-page">
|
||||
<NFlex justify="space-between">
|
||||
<NH4 prefix="bar">{{ $t("pages.Setting.Data.localStore") }}</NH4>
|
||||
<NFlex>
|
||||
<NButton
|
||||
tertiary
|
||||
circle
|
||||
type="primary"
|
||||
@click="delLocalStore()"
|
||||
style="margin-right: 20px"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><TrashBinOutline /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
<NButton
|
||||
tertiary
|
||||
circle
|
||||
type="primary"
|
||||
@click="refreshLocalData()"
|
||||
style="margin-right: 20px"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon><Refresh /></NIcon>
|
||||
</template>
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
<NP>{{ $t("pages.Setting.Data.delLocalStore.warning") }}</NP>
|
||||
<NList class="data-list" hoverable clickable>
|
||||
<NListItem
|
||||
v-for="(entrie, index) in localStoreEntries"
|
||||
@click="showLocalStore(index)"
|
||||
>
|
||||
<div>
|
||||
{{ entrie[0] }}
|
||||
</div>
|
||||
</NListItem>
|
||||
</NList>
|
||||
</div>
|
||||
<NModal v-model:show="showDataModal">
|
||||
<NCard
|
||||
style="width: 50%; height: 80%"
|
||||
:title="localStoreEntries[curDataIndex][0]"
|
||||
>
|
||||
<NFlex vertical style="height: 100%">
|
||||
<NInput
|
||||
type="textarea"
|
||||
style="flex-grow: 1"
|
||||
:value="dataModalInputVal"
|
||||
round
|
||||
readonly
|
||||
/>
|
||||
<NButton
|
||||
type="success"
|
||||
round
|
||||
@click="delLocalStore(localStoreEntries[curDataIndex][0])"
|
||||
>{{ $t("pages.Setting.Data.delCurData") }}</NButton
|
||||
>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.setting-page {
|
||||
padding: 10px 25px;
|
||||
|
||||
.data-list {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import Basic from "./Basic.vue";
|
||||
import Mask from "./Mask.vue";
|
||||
import Data from "./Data.vue";
|
||||
import About from "./About.vue";
|
||||
import { NTabs, NTabPane, NScrollbar, NSpin } from "naive-ui";
|
||||
import { useGlobalStore } from "../../store/global";
|
||||
@ -22,6 +23,11 @@ const store = useGlobalStore();
|
||||
<Mask />
|
||||
</NScrollbar>
|
||||
</NTabPane>
|
||||
<NTabPane :tab="$t('pages.Setting.tabs.data')" name="data">
|
||||
<NScrollbar>
|
||||
<Data />
|
||||
</NScrollbar>
|
||||
</NTabPane>
|
||||
<NTabPane :tab="$t('pages.Setting.tabs.about')" name="about">
|
||||
<NScrollbar>
|
||||
<About />
|
||||
|
@ -163,13 +163,13 @@ interface InjectKeycode {
|
||||
metastate: AndroidMetastate;
|
||||
}
|
||||
|
||||
enum ScCopyKey {
|
||||
export enum ScCopyKey {
|
||||
SC_COPY_KEY_NONE,
|
||||
SC_COPY_KEY_COPY,
|
||||
SC_COPY_KEY_CUT,
|
||||
}
|
||||
|
||||
enum ScScreenPowerMode {
|
||||
export enum ScScreenPowerMode {
|
||||
// see <https://android.googlesource.com/platform/frameworks/base.git/+/pie-release-2/core/java/android/view/SurfaceControl.java#305>
|
||||
SC_SCREEN_POWER_MODE_OFF = 0,
|
||||
SC_SCREEN_POWER_MODE_NORMAL = 2,
|
||||
|
@ -17,12 +17,14 @@ import {
|
||||
KeyObservation,
|
||||
KeySight,
|
||||
KeySteeringWheel,
|
||||
KeySwipe,
|
||||
KeyTap,
|
||||
KeyTriggerWhenDoublePressedSkill,
|
||||
KeyTriggerWhenPressedSkill,
|
||||
} from "./keyMappingConfig";
|
||||
import { useGlobalStore } from "./store/global";
|
||||
import { LogicalPosition, getCurrent } from "@tauri-apps/api/window";
|
||||
import { readText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { KeyToCodeMap } from "./frontcommand/KeyToCodeMap";
|
||||
import {
|
||||
@ -30,7 +32,7 @@ import {
|
||||
AndroidMetastate,
|
||||
} from "./frontcommand/android";
|
||||
import { UIEventsCode } from "./frontcommand/UIEventsCode";
|
||||
import { sendInjectKeycode } from "./frontcommand/controlMsg";
|
||||
import { sendInjectKeycode, sendSetClipboard } from "./frontcommand/controlMsg";
|
||||
|
||||
function clientxToPosx(clientx: number) {
|
||||
return clientx < 70
|
||||
@ -947,6 +949,40 @@ function addSightShortcuts(
|
||||
});
|
||||
}
|
||||
|
||||
function addSwipeShortcuts(
|
||||
key: string,
|
||||
relativeSize: { w: number; h: number },
|
||||
// pos relative to the mask
|
||||
pos: { x: number; y: number }[],
|
||||
pointerId: number,
|
||||
intervalBetweenPos: number
|
||||
) {
|
||||
const newPosList = pos.map((posObj) => {
|
||||
return {
|
||||
x: Math.round((posObj.x / relativeSize.w) * store.screenSizeW),
|
||||
y: Math.round((posObj.y / relativeSize.h) * store.screenSizeH),
|
||||
};
|
||||
});
|
||||
|
||||
addShortcut(
|
||||
key,
|
||||
async () => {
|
||||
await swipe({
|
||||
action: SwipeAction.Default,
|
||||
pointerId,
|
||||
screen: {
|
||||
w: store.screenSizeW,
|
||||
h: store.screenSizeH,
|
||||
},
|
||||
pos: newPosList,
|
||||
intervalBetweenPos,
|
||||
});
|
||||
},
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
function createMouseRangeBox(): HTMLElement {
|
||||
const box = document.createElement("div");
|
||||
box.id = "mouseRangeBox";
|
||||
@ -1046,6 +1082,8 @@ export class KeyInputHandler {
|
||||
let action: AndroidKeyEventAction;
|
||||
let repeatCount = 0;
|
||||
if (event.type === "keydown") {
|
||||
if (event.getModifierState("Control") && event.code === "KeyV") return;
|
||||
|
||||
action = AndroidKeyEventAction.AKEY_EVENT_ACTION_DOWN;
|
||||
if (event.repeat) {
|
||||
let count = KeyInputHandler.repeatCounter.get(keycode);
|
||||
@ -1058,6 +1096,18 @@ export class KeyInputHandler {
|
||||
KeyInputHandler.repeatCounter.set(keycode, count);
|
||||
}
|
||||
} else if (event.type === "keyup") {
|
||||
if (event.getModifierState("Control") && event.code === "KeyV") {
|
||||
(async () => {
|
||||
const text = await readText();
|
||||
await sendSetClipboard({
|
||||
sequence: Math.floor(Math.random() * 10000),
|
||||
text,
|
||||
paste: true,
|
||||
});
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
action = AndroidKeyEventAction.AKEY_EVENT_ACTION_UP;
|
||||
KeyInputHandler.repeatCounter.delete(keycode);
|
||||
} else {
|
||||
@ -1078,12 +1128,6 @@ export class KeyInputHandler {
|
||||
? AndroidMetastate.AMETA_NUM_LOCK_ON
|
||||
: 0);
|
||||
|
||||
// const controlMessage = new KeyCodeControlMessage(
|
||||
// action,
|
||||
// keyCode,
|
||||
// repeatCount,
|
||||
// metaState
|
||||
// );
|
||||
sendInjectKeycode({
|
||||
action,
|
||||
keycode,
|
||||
@ -1378,6 +1422,16 @@ function applyKeyMappingConfigShortcuts(
|
||||
item.pointerId
|
||||
);
|
||||
break;
|
||||
case "Swipe":
|
||||
asType<KeySwipe>(item);
|
||||
addSwipeShortcuts(
|
||||
item.key,
|
||||
relativeSize,
|
||||
item.pos,
|
||||
item.pointerId,
|
||||
item.intervalBetweenPos
|
||||
);
|
||||
break;
|
||||
case "TriggerWhenPressedSkill":
|
||||
asType<KeyTriggerWhenPressedSkill>(item);
|
||||
addTriggerWhenPressedSkillShortcuts(
|
||||
|
@ -36,13 +36,7 @@
|
||||
"wsConnect": "Control",
|
||||
"adbDeviceError": "Unable to get available devices",
|
||||
"adbConnectError": "Wireless connection failed",
|
||||
"rotation": "Device rotation {0}°",
|
||||
"clipboard": {
|
||||
"deviceSync": {
|
||||
"success": "Device clipboard synced",
|
||||
"failed": "Device clipboard sync failed"
|
||||
}
|
||||
}
|
||||
"rotation": "Device rotation {0}°"
|
||||
},
|
||||
"Mask": {
|
||||
"keyconfigException": "The key mapping config is abnormal, please delete this config",
|
||||
@ -62,14 +56,15 @@
|
||||
"positiveText": "To control"
|
||||
},
|
||||
"sightMode": "Mouse is locked, press {0} to unlock",
|
||||
"checkAdb": "adb is not available and the software cannot run normally. Please ensure that adb is installed on the system and added to the Path environment variable correctly: {0}",
|
||||
"checkAdb": "adb is not available and the software cannot run normally. Fill in the path of the adb file in the setup page, or add the folder where it is located to the Path environment variable.: {0}",
|
||||
"keyInputMode": "Has entered the keystroke input mode, close this message to exit."
|
||||
},
|
||||
"Setting": {
|
||||
"tabs": {
|
||||
"basic": "Basic settings",
|
||||
"mask": "Mask setting",
|
||||
"about": "About"
|
||||
"about": "About",
|
||||
"data": "Data management"
|
||||
},
|
||||
"Mask": {
|
||||
"areaFormMissing": {
|
||||
@ -105,6 +100,15 @@
|
||||
}
|
||||
},
|
||||
"Basic": {
|
||||
"language": "Language",
|
||||
"adbPath": {
|
||||
"placeholder": "adb path",
|
||||
"set": "Save",
|
||||
"setSuccess": "adb path set successfully",
|
||||
"title": "adb path"
|
||||
}
|
||||
},
|
||||
"Data": {
|
||||
"delLocalStore": {
|
||||
"dialog": {
|
||||
"title": "Warning",
|
||||
@ -115,15 +119,8 @@
|
||||
},
|
||||
"warning": "Deleting data may lead to unpredictable consequences, so please operate with caution. \nIf an exception occurs, please try clearing the data and restarting the software."
|
||||
},
|
||||
"language": "Language",
|
||||
"localStore": "Local data",
|
||||
"delCurData": "Delete current data",
|
||||
"adbPath": {
|
||||
"placeholder": "adb path",
|
||||
"set": "Save",
|
||||
"setSuccess": "adb path set successfully",
|
||||
"title": "adb path"
|
||||
}
|
||||
"delCurData": "Delete current data"
|
||||
},
|
||||
"About": {
|
||||
"introduction": "A Scrcpy client in Rust & Tarui aimed at providing mouse and key mapping to control Android device.",
|
||||
@ -154,7 +151,8 @@
|
||||
"Sight": "Front sight",
|
||||
"Fire": "Fire",
|
||||
"existFire": "Fire button already exists",
|
||||
"existSight": "Front sight button already exists"
|
||||
"existSight": "Front sight button already exists",
|
||||
"Swipe": "Swipe"
|
||||
},
|
||||
"buttonKeyRepeat": "Key repeat: {0}",
|
||||
"KeyCommon": {
|
||||
@ -256,6 +254,14 @@
|
||||
"scaleX": "Horizontal sensitivity",
|
||||
"scalePlaceholder": "Please enter sensitivity",
|
||||
"scaleY": "Vertical sensitivity"
|
||||
},
|
||||
"Swipe": {
|
||||
"swipe": "Swipe",
|
||||
"interval": "Swipe time interval",
|
||||
"intervalPlaceholder": "Enter the time interval between points",
|
||||
"pos": "Points",
|
||||
"editPos": "Edit",
|
||||
"editTips": "Left-click on a blank area to add a new coordinate point. \nLeft-click and drag to move a specific point, and right-click to delete the point."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -36,13 +36,7 @@
|
||||
"wsConnect": "控制",
|
||||
"adbDeviceError": "无法获取可用设备",
|
||||
"adbConnectError": "无线连接失败",
|
||||
"rotation": "设备旋转 {0}°",
|
||||
"clipboard": {
|
||||
"deviceSync": {
|
||||
"success": "已从设备同步剪切板",
|
||||
"failed": "从设备同步剪切板失败"
|
||||
}
|
||||
}
|
||||
"rotation": "设备旋转 {0}°"
|
||||
},
|
||||
"Mask": {
|
||||
"keyconfigException": "按键方案异常,请删除此方案",
|
||||
@ -62,14 +56,15 @@
|
||||
"positiveText": "去控制"
|
||||
},
|
||||
"sightMode": "鼠标已锁定, 按 {0} 键解锁",
|
||||
"checkAdb": "adb不可用,软件无法正常运行,请确保系统已安装adb,并正确添加到了Path环境变量中: {0}",
|
||||
"checkAdb": "adb不可用,软件无法正常运行,请确保系统已安装adb。请将adb文件路径填入软件设置界面,或者将其所在文件夹添加到Path环境变量中: {0}",
|
||||
"keyInputMode": "已进入按键输入模式,关闭本消息可退出"
|
||||
},
|
||||
"Setting": {
|
||||
"tabs": {
|
||||
"basic": "基本设置",
|
||||
"mask": "蒙版设置",
|
||||
"about": "关于"
|
||||
"about": "关于",
|
||||
"data": "数据管理"
|
||||
},
|
||||
"Mask": {
|
||||
"incorrectArea": "请正确输入蒙版的坐标和尺寸",
|
||||
@ -105,6 +100,15 @@
|
||||
}
|
||||
},
|
||||
"Basic": {
|
||||
"language": "语言",
|
||||
"adbPath": {
|
||||
"setSuccess": "adb 路径设置成功",
|
||||
"title": "adb 路径",
|
||||
"placeholder": "adb 路径",
|
||||
"set": "设置"
|
||||
}
|
||||
},
|
||||
"Data": {
|
||||
"delLocalStore": {
|
||||
"dialog": {
|
||||
"title": "警告",
|
||||
@ -115,15 +119,8 @@
|
||||
},
|
||||
"warning": "删除数据可能导致无法预料的后果,请慎重操作。若出现异常请尝试清空数据并重启软件。"
|
||||
},
|
||||
"language": "语言",
|
||||
"localStore": "本地数据",
|
||||
"delCurData": "删除当前数据",
|
||||
"adbPath": {
|
||||
"setSuccess": "adb 路径设置成功",
|
||||
"title": "adb 路径",
|
||||
"placeholder": "adb 路径",
|
||||
"set": "设置"
|
||||
}
|
||||
"delCurData": "删除当前数据"
|
||||
},
|
||||
"About": {
|
||||
"about": "关于",
|
||||
@ -147,7 +144,8 @@
|
||||
"Sight": "准星",
|
||||
"Fire": "开火",
|
||||
"existSight": "已存在准星按钮",
|
||||
"existFire": "已存在开火按钮"
|
||||
"existFire": "已存在开火按钮",
|
||||
"Swipe": "滑动"
|
||||
},
|
||||
"buttonKeyRepeat": "按键重复: {0}",
|
||||
"noSaveDialog": {
|
||||
@ -256,6 +254,14 @@
|
||||
"scaleX": "水平灵敏度",
|
||||
"scaleY": "垂直灵敏度",
|
||||
"scalePlaceholder": "请输入灵敏度"
|
||||
},
|
||||
"Swipe": {
|
||||
"swipe": "滑动",
|
||||
"interval": "滑动时间间隔",
|
||||
"intervalPlaceholder": "输入坐标点之间的时间间隔",
|
||||
"pos": "坐标点",
|
||||
"editPos": "编辑",
|
||||
"editTips": "左键点击空白区域添加新坐标点。左键拖拽移动特定坐标点,右键删除特定坐标点"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -65,6 +65,14 @@ export interface KeyTap extends KeyBase {
|
||||
time: number;
|
||||
}
|
||||
|
||||
export interface KeySwipe extends KeyBase {
|
||||
type: "Swipe";
|
||||
pointerId: number;
|
||||
key: string;
|
||||
pos: { x: number; y: number }[];
|
||||
intervalBetweenPos: number;
|
||||
}
|
||||
|
||||
export type KeyMacroList = Array<{
|
||||
type: "touch" | "sleep" | "swipe" | "key-input-mode";
|
||||
args: any[];
|
||||
@ -106,6 +114,7 @@ export type KeyMapping =
|
||||
| KeyMacro
|
||||
| KeyCancelSkill
|
||||
| KeyTap
|
||||
| KeySwipe
|
||||
| KeySight
|
||||
| KeyFire;
|
||||
|
||||
|
@ -100,6 +100,11 @@ export const useGlobalStore = defineStore("global", () => {
|
||||
horizontalLength: 800,
|
||||
});
|
||||
|
||||
const clipboardSync = ref({
|
||||
syncFromDevice: true,
|
||||
pasteFromPC: true,
|
||||
});
|
||||
|
||||
return {
|
||||
// persistent storage
|
||||
keyMappingConfigList,
|
||||
@ -109,6 +114,7 @@ export const useGlobalStore = defineStore("global", () => {
|
||||
externalControlled,
|
||||
screenStream,
|
||||
rotation,
|
||||
clipboardSync,
|
||||
// in-memory storage
|
||||
screenStreamClientId,
|
||||
maskSizeW,
|
||||
|
@ -6,6 +6,7 @@ export const useKeyboardStore = defineStore("keyboard", () => {
|
||||
const showSettingFlag = ref(false);
|
||||
const showButtonSettingFlag = ref(false);
|
||||
const showButtonAddFlag = ref(false);
|
||||
const editSwipePointsFlag = ref(false);
|
||||
const activeButtonIndex = ref(-1);
|
||||
const activeSteeringWheelButtonKeyIndex = ref(-1);
|
||||
const edited = ref(false);
|
||||
@ -15,6 +16,7 @@ export const useKeyboardStore = defineStore("keyboard", () => {
|
||||
showSettingFlag,
|
||||
showButtonSettingFlag,
|
||||
showButtonAddFlag,
|
||||
editSwipePointsFlag,
|
||||
activeButtonIndex,
|
||||
activeSteeringWheelButtonKeyIndex,
|
||||
edited,
|
||||
|
122
src/storeLoader.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { Store } from "@tauri-apps/plugin-store";
|
||||
import { KeyMappingConfig } from "./keyMappingConfig";
|
||||
import { useGlobalStore } from "./store/global";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
let localStore: Store;
|
||||
let store: ReturnType<typeof useGlobalStore>;
|
||||
let t: ReturnType<typeof useI18n>["t"];
|
||||
|
||||
async function loadKeyMappingConfigList() {
|
||||
// loading keyMappingConfigList from local store
|
||||
let keyMappingConfigList = await localStore.get<KeyMappingConfig[]>(
|
||||
"keyMappingConfigList"
|
||||
);
|
||||
if (keyMappingConfigList === null || keyMappingConfigList.length === 0) {
|
||||
// add empty key mapping config
|
||||
// unable to get mask element when app is not ready
|
||||
// so we use the stored mask area to get relative size
|
||||
const maskArea = await localStore.get<{
|
||||
posX: number;
|
||||
posY: number;
|
||||
sizeW: number;
|
||||
sizeH: number;
|
||||
}>("maskArea");
|
||||
let relativeSize = { w: 800, h: 600 };
|
||||
if (maskArea !== null) {
|
||||
relativeSize = {
|
||||
w: maskArea.sizeW,
|
||||
h: maskArea.sizeH,
|
||||
};
|
||||
}
|
||||
keyMappingConfigList = [
|
||||
{
|
||||
relativeSize,
|
||||
title: t("pages.Mask.blankConfig"),
|
||||
list: [],
|
||||
},
|
||||
];
|
||||
await localStore.set("keyMappingConfigList", keyMappingConfigList);
|
||||
}
|
||||
store.keyMappingConfigList = keyMappingConfigList;
|
||||
}
|
||||
|
||||
async function loadCurKeyMappingIndex() {
|
||||
// loading curKeyMappingIndex from local store
|
||||
let curKeyMappingIndex = await localStore.get<number>("curKeyMappingIndex");
|
||||
if (
|
||||
curKeyMappingIndex === null ||
|
||||
curKeyMappingIndex >= store.keyMappingConfigList.length
|
||||
) {
|
||||
curKeyMappingIndex = 0;
|
||||
localStore.set("curKeyMappingIndex", curKeyMappingIndex);
|
||||
}
|
||||
store.curKeyMappingIndex = curKeyMappingIndex;
|
||||
}
|
||||
|
||||
async function loadMaskButton() {
|
||||
// loading maskButton from local store
|
||||
let maskButton = await localStore.get<{
|
||||
show: boolean;
|
||||
transparency: number;
|
||||
}>("maskButton");
|
||||
store.maskButton = maskButton ?? {
|
||||
show: true,
|
||||
transparency: 0.5,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadCheckUpdateAtStart() {
|
||||
// loading checkUpdateAtStart from local store
|
||||
const checkUpdateAtStart = await localStore.get<boolean>(
|
||||
"checkUpdateAtStart"
|
||||
);
|
||||
store.checkUpdateAtStart = checkUpdateAtStart ?? true;
|
||||
}
|
||||
|
||||
async function loadRotation() {
|
||||
// loading rotation from local store
|
||||
const rotation = await localStore.get<{
|
||||
enable: boolean;
|
||||
verticalLength: number;
|
||||
horizontalLength: number;
|
||||
}>("rotation");
|
||||
if (rotation) store.rotation = rotation;
|
||||
}
|
||||
|
||||
async function loadScreenStream() {
|
||||
// loading screenStream from local store
|
||||
const screenStream = await localStore.get<{
|
||||
enable: boolean;
|
||||
address: string;
|
||||
}>("screenStream");
|
||||
if (screenStream) store.screenStream = screenStream;
|
||||
}
|
||||
|
||||
async function loadClipboardSync() {
|
||||
// loading clipboardSync from local store
|
||||
const clipboardSync = await localStore.get<{
|
||||
syncFromDevice: boolean;
|
||||
pasteFromPC: boolean;
|
||||
}>("clipboardSync");
|
||||
if (clipboardSync) store.clipboardSync = clipboardSync;
|
||||
console.log(store.clipboardSync);
|
||||
}
|
||||
|
||||
export async function loadLocalStorage(
|
||||
theLocalStore: Store,
|
||||
theStore: ReturnType<typeof useGlobalStore>,
|
||||
theT: ReturnType<typeof useI18n>["t"]
|
||||
) {
|
||||
localStore = theLocalStore;
|
||||
store = theStore;
|
||||
t = theT;
|
||||
|
||||
await loadKeyMappingConfigList();
|
||||
await loadCurKeyMappingIndex();
|
||||
await loadMaskButton();
|
||||
await loadCheckUpdateAtStart();
|
||||
await loadRotation();
|
||||
await loadScreenStream();
|
||||
await loadClipboardSync();
|
||||
}
|