make shortcuts more declarative

This commit is contained in:
Alexander Drozdov
2021-09-17 12:59:40 +03:00
parent 534d17fec4
commit 723de34ee6
4 changed files with 164 additions and 146 deletions
+7 -6
View File
@@ -20,14 +20,15 @@ let isMouseInside = false
export function showWidget (opts: {
clipboard: string
hotkeyPressPosition: Point
lockedMode: boolean
pressPosition: Point
eventName: string
}) {
checkPressPosition = opts.hotkeyPressPosition
checkPressPosition = opts.pressPosition
const isLokedMode = (opts.eventName === 'price-check-locked')
overlayWindow!.webContents.send(ipc.PRICE_CHECK, { clipboard: opts.clipboard, position: checkPressPosition, lockedMode: opts.lockedMode } as ipc.IpcPriceCheck)
overlayWindow!.webContents.send(ipc.PRICE_CHECK, { clipboard: opts.clipboard, position: checkPressPosition, lockedMode: isLokedMode } as ipc.IpcPriceCheck)
const poeBounds = PoeWindow.bounds!
const poeBounds = PoeWindow.bounds
activeAreaRect = {
x: getOffsetX(checkPressPosition, poeBounds),
y: poeBounds.y,
@@ -39,7 +40,7 @@ export function showWidget (opts: {
isClickedAfterLock = false
isMouseInside = false
if (opts.lockedMode) {
if (isLokedMode) {
lockWindow(true)
}
}
+150 -136
View File
@@ -1,9 +1,9 @@
import { screen, Point, clipboard, globalShortcut, Notification, ipcMain } from 'electron'
import { screen, clipboard, globalShortcut, Notification, ipcMain } from 'electron'
import robotjs from 'robotjs'
import { uIOhook, UiohookKey, UiohookWheelEvent } from 'uiohook-napi'
import { pollClipboard } from './poll-clipboard'
import { priceCheckConfig, showWidget as showPriceCheck } from './price-check'
import { KeyToElectron, mergeTwoHotkeys } from '@/ipc/KeyToCode'
import { isModKey, KeyToElectron, mergeTwoHotkeys } from '@/ipc/KeyToCode'
import { config } from './config'
import { PoeWindow } from './PoeWindow'
import { logger } from './logger'
@@ -12,36 +12,158 @@ import * as ipc from '@/ipc/ipc-event'
import { typeInChat } from './game-chat'
import { gameConfig } from './game-config'
export let hotkeyPressPosition: Point | undefined
export const UiohookToName = Object.fromEntries(Object.entries(UiohookKey).map(([k, v]) => ([v, k])))
function priceCheck (lockedMode: boolean) {
logger.info('Price check', { source: 'shortcuts', lockedMode })
pollClipboard()
.then(clipboard =>
showPriceCheck({ clipboard, hotkeyPressPosition: hotkeyPressPosition!, lockedMode })
)
.catch(() => { /* nothing bad */ })
hotkeyPressPosition = screen.getCursorScreenPoint()
// if (process.platform === 'win32') {
// hotkeyPressPosition = screen.dipToScreenPoint(hotkeyPressPosition)
// }
if (!lockedMode) {
pressKeysToCopyItemText(priceCheckConfig().hotkeyHold)
} else {
pressKeysToCopyItemText()
export interface ShortcutAction {
shortcut: string
keepModKeys?: true
action: {
type: 'copy-item'
eventName: string
focusOverlay?: boolean
} | {
type: 'trigger-event'
eventName: string
} | {
type: 'toggle-overlay'
} | {
type: 'paste-in-chat'
text: string
send: boolean
} | {
type: 'test-only'
}
}
function pressKeysToCopyItemText (skipModKey?: string) {
let keys = mergeTwoHotkeys('Ctrl + C', gameConfig?.highlightKey || 'Alt').split(' + ')
keys = keys.filter(key => key !== 'C')
if (skipModKey) {
keys = keys.filter(key => key !== skipModKey)
function shortcutsfromConfig () {
const actions: ShortcutAction[] = []
const priceCheckCfg = priceCheckConfig()
if (priceCheckCfg.hotkey) {
actions.push({
shortcut: `${priceCheckCfg.hotkeyHold} + ${priceCheckCfg.hotkey}`,
action: { type: 'copy-item', eventName: 'price-check-quick' },
keepModKeys: true
})
}
if (priceCheckCfg.hotkeyLocked) {
actions.push({
shortcut: priceCheckCfg.hotkeyLocked,
action: { type: 'copy-item', eventName: 'price-check-locked' }
})
}
actions.push({
shortcut: config.get('overlayKey'),
action: { type: 'toggle-overlay' },
keepModKeys: true
})
if (config.get('wikiKey')) {
actions.push({
shortcut: config.get('wikiKey')!,
action: { type: 'copy-item', eventName: ipc.OPEN_WIKI }
})
}
if (config.get('craftOfExileKey')) {
actions.push({
shortcut: config.get('craftOfExileKey')!,
action: { type: 'copy-item', eventName: ipc.OPEN_COE }
})
}
if (config.get('itemCheckKey')) {
actions.push({
shortcut: config.get('itemCheckKey')!,
action: { type: 'copy-item', eventName: ipc.ITEM_CHECK, focusOverlay: true }
})
}
if (config.get('delveGridKey')) {
actions.push({
shortcut: config.get('delveGridKey')!,
action: { type: 'trigger-event', eventName: ipc.TOGGLE_DELVE_GRID },
keepModKeys: true
})
}
for (const command of config.get('commands')) {
if (command.hotkey) {
actions.push({
shortcut: command.hotkey,
action: { type: 'paste-in-chat', text: command.text, send: command.send }
})
}
}
return actions
}
function registerGlobal () {
const toRegister = shortcutsfromConfig()
for (const entry of toRegister) {
const isOk = globalShortcut.register(shortcutToElectron(entry.shortcut), () => {
if (entry.keepModKeys) {
const nonModKey = entry.shortcut.split(' + ').filter(key => !isModKey(key))[0]
robotjs.keyToggle(nonModKey, 'up')
} else {
entry.shortcut.split(' + ').reverse().forEach(key => { robotjs.keyToggle(key, 'up') })
}
if (entry.action.type === 'toggle-overlay') {
toggleOverlayState()
} else if (entry.action.type === 'paste-in-chat') {
typeInChat(entry.action.text, entry.action.send)
} else if (entry.action.type === 'trigger-event') {
overlayWindow!.webContents.send(entry.action.eventName)
} else if (entry.action.type === 'copy-item') {
const { action } = entry
const pressPosition = screen.getCursorScreenPoint()
// if (process.platform === 'win32') {
// pressPosition = screen.dipToScreenPoint(pressPosition)
// }
pollClipboard()
.then(clipboard => {
if (action.eventName.startsWith('price-check')) {
showPriceCheck({ clipboard, pressPosition, eventName: action.eventName })
} else {
overlayWindow!.webContents.send(action.eventName, { clipboard, position: pressPosition })
if (action.focusOverlay) {
assertOverlayActive()
}
}
}).catch(() => {})
if (!entry.keepModKeys) {
pressKeysToCopyItemText()
} else {
pressKeysToCopyItemText(entry.shortcut.split(' + ').filter(key => isModKey(key)))
}
}
})
if (!isOk) {
logger.error('Cannot register shortcut, because it is already registered by another application.', { source: 'shortcuts', shortcut: entry.shortcut })
new Notification({
title: 'Awakened PoE Trade',
body: `Cannot register shortcut ${entry.shortcut}, because it is already registered by another application.`
}).show()
}
if (entry.action.type === 'test-only') {
globalShortcut.unregister(shortcutToElectron(entry.shortcut))
}
}
logger.verbose('Registered Global', { source: 'shortcuts', total: toRegister.length })
}
function unregisterGlobal () {
globalShortcut.unregisterAll()
logger.verbose('Unregistered Global', { source: 'shortcuts' })
}
function pressKeysToCopyItemText (pressedModKeys: string[] = []) {
let keys = mergeTwoHotkeys('Ctrl + C', gameConfig?.highlightKey || 'Alt').split(' + ')
keys = keys.filter(key => key !== 'C' && !pressedModKeys.includes(key))
for (const key of keys) {
robotjs.keyToggle(key, 'down')
@@ -56,86 +178,6 @@ function pressKeysToCopyItemText (skipModKey?: string) {
}
}
function itemCheck () {
logger.info('Item check', { source: 'shortcuts' })
pollClipboard()
.then(clipboard => {
overlayWindow!.webContents.send(ipc.ITEM_CHECK, { clipboard, position: hotkeyPressPosition! } as ipc.IpcItemCheck)
assertOverlayActive()
})
.catch(() => {})
hotkeyPressPosition = screen.getCursorScreenPoint()
pressKeysToCopyItemText()
}
function registerGlobal () {
const priceCheckCfg = priceCheckConfig()
const register = [
shortcutCallback(
priceCheckCfg.hotkey && `${priceCheckCfg.hotkeyHold} + ${priceCheckCfg.hotkey}`,
() => priceCheck(false),
{ doNotResetModKey: true }
),
shortcutCallback(
priceCheckCfg.hotkeyLocked,
() => priceCheck(true)
),
shortcutCallback(
config.get('overlayKey'),
toggleOverlayState,
{ doNotResetModKey: true }
),
shortcutCallback(
config.get('wikiKey'),
() => {
pollClipboard().then(openWiki).catch(() => {})
pressKeysToCopyItemText()
}
),
shortcutCallback(
config.get('craftOfExileKey'),
() => {
pollClipboard().then(openCraftOfExile).catch(() => {})
pressKeysToCopyItemText()
}
),
shortcutCallback(
config.get('itemCheckKey'),
itemCheck
),
shortcutCallback(
config.get('delveGridKey'),
toggleDelveGrid,
{ doNotResetModKey: true }
),
...config.get('commands')
.map(command =>
shortcutCallback(command.hotkey, () => typeInChat(command.text, command.send))
)
].filter(a => Boolean(a.shortcut))
register.forEach(a => {
const success = globalShortcut.register(shortcutToElectron(a.shortcut!), a.cb)
if (!success) {
logger.error('Cannot register shortcut, because it is already registered by another application.', { source: 'shortcuts', shortcut: a.shortcut })
new Notification({
title: 'Awakened PoE Trade',
body: `Cannot register shortcut ${a.shortcut}, because it is already registered by another application.`
}).show()
}
})
logger.verbose('Registered Global', { source: 'shortcuts', total: register.length })
}
function unregisterGlobal () {
globalShortcut.unregisterAll()
logger.verbose('Unregistered Global', { source: 'shortcuts' })
}
export function setupShortcuts () {
// A value of zero causes the thread to relinquish the remainder of its
// time slice to any other thread that is ready to run. If there are no other
@@ -207,18 +249,6 @@ function stashSearch (text: string) {
}
}
function openWiki (clipboard: string) {
overlayWindow!.webContents.send(ipc.OPEN_WIKI, clipboard)
}
function openCraftOfExile (clipboard: string) {
overlayWindow!.webContents.send(ipc.OPEN_COE, clipboard)
}
function toggleDelveGrid () {
overlayWindow!.webContents.send(ipc.TOGGLE_DELVE_GRID)
}
function eventToString (e: { keycode: number, ctrlKey: boolean, altKey: boolean, shiftKey: boolean }) {
const { ctrlKey, shiftKey, altKey } = e
@@ -227,7 +257,8 @@ function eventToString (e: { keycode: number, ctrlKey: boolean, altKey: boolean,
if (code === 'Shift' || code === 'Alt' || code === 'Ctrl') return code
if (shiftKey && altKey) code = `Shift + Alt + ${code}`
if (ctrlKey && shiftKey && altKey) code = `Ctrl + Shift + Alt + ${code}`
else if (shiftKey && altKey) code = `Shift + Alt + ${code}`
else if (ctrlKey && shiftKey) code = `Ctrl + Shift + ${code}`
else if (ctrlKey && altKey) code = `Ctrl + Alt + ${code}`
else if (altKey) code = `Alt + ${code}`
@@ -237,23 +268,6 @@ function eventToString (e: { keycode: number, ctrlKey: boolean, altKey: boolean,
return code
}
function shortcutCallback<T extends Function> (shortcut: string | null, cb: T, opts?: { doNotResetModKey?: boolean }) {
return {
shortcut,
cb: function () {
if (!shortcut) throw new Error('Never: callback called on null shortcut')
if (opts?.doNotResetModKey) {
const nonModKey = shortcut.split(' + ').reverse()[0]
robotjs.keyToggle(nonModKey, 'up')
} else {
shortcut.split(' + ').reverse().forEach(key => { robotjs.keyToggle(key, 'up') })
}
cb()
}
}
}
function shortcutToElectron (shortcut: string) {
return shortcut
.split(' + ')
+2 -2
View File
@@ -6,7 +6,7 @@ import { handleLine } from '../client-log/client-log'
export function registerOtherServices () {
MainProcess.addEventListener(ipc.OPEN_WIKI, (e) => {
openWiki((e as CustomEvent<string>).detail)
openWiki((e as CustomEvent<{ clipboard: string }>).detail.clipboard)
})
MainProcess.addEventListener(ipc.CLIENT_LOG_UPDATE, (e) => {
@@ -14,6 +14,6 @@ export function registerOtherServices () {
})
MainProcess.addEventListener(ipc.OPEN_COE, (e) => {
openCOE((e as CustomEvent<string>).detail)
openCOE((e as CustomEvent<{ clipboard: string }>).detail.clipboard)
})
}
+5 -2
View File
@@ -27,10 +27,10 @@
</template>
<script lang="ts">
import { defineComponent, shallowRef, computed, Component, PropType, nextTick } from 'vue'
import { defineComponent, shallowRef, computed, Component, PropType, nextTick, inject } from 'vue'
import { useI18n } from 'vue-i18n'
import { Config } from '@/web/Config'
import { Widget } from '../overlay/interfaces'
import type { Widget, WidgetManager } from '../overlay/interfaces'
import { MainProcess } from '@/ipc/main-process-bindings'
import SettingsHotkeys from './hotkeys.vue'
import SettingsChat from './chat.vue'
@@ -47,6 +47,7 @@ export default defineComponent({
}
},
setup (props) {
const wm = inject<WidgetManager>('wm')!
const { t } = useI18n()
nextTick(() => {
@@ -94,9 +95,11 @@ export default defineComponent({
return {
t,
save () {
wm.hide(props.config.wmId)
MainProcess.closeSettingsWindow(JSON.parse(JSON.stringify(Config.store)))
},
cancel () {
wm.hide(props.config.wmId)
MainProcess.closeSettingsWindow()
},
menusItems,