diff --git a/src/i18n/lang/en-US/settings.ts b/src/i18n/lang/en-US/settings.ts index 3bfc93c..b7c7bf9 100644 --- a/src/i18n/lang/en-US/settings.ts +++ b/src/i18n/lang/en-US/settings.ts @@ -197,6 +197,8 @@ export default { shortcutConflict: 'Shortcut Conflict', inputPlaceholder: 'Click to input shortcut', resetShortcuts: 'Reset', + disableAll: 'Disable All', + enableAll: 'Enable All', togglePlay: 'Play/Pause', prevPlay: 'Previous', nextPlay: 'Next', @@ -204,12 +206,18 @@ export default { volumeDown: 'Volume Down', toggleFavorite: 'Favorite/Unfavorite', toggleWindow: 'Show/Hide Window', + scopeGlobal: 'Global', + scopeApp: 'App Only', + enabled: 'Enabled', + disabled: 'Disabled', messages: { resetSuccess: 'Shortcuts reset successfully, please save', conflict: 'Shortcut conflict, please reset', saveSuccess: 'Shortcuts saved successfully', saveError: 'Failed to save shortcuts', - cancelEdit: 'Edit cancelled' + cancelEdit: 'Edit cancelled', + disableAll: 'All shortcuts disabled, please save to apply', + enableAll: 'All shortcuts enabled, please save to apply' } } }; diff --git a/src/i18n/lang/zh-CN/settings.ts b/src/i18n/lang/zh-CN/settings.ts index 80c3b9b..def504f 100644 --- a/src/i18n/lang/zh-CN/settings.ts +++ b/src/i18n/lang/zh-CN/settings.ts @@ -197,6 +197,8 @@ export default { shortcutConflict: '快捷键冲突', inputPlaceholder: '点击输入快捷键', resetShortcuts: '恢复默认', + disableAll: '全部禁用', + enableAll: '全部启用', togglePlay: '播放/暂停', prevPlay: '上一首', nextPlay: '下一首', @@ -204,12 +206,18 @@ export default { volumeDown: '音量减少', toggleFavorite: '收藏/取消收藏', toggleWindow: '显示/隐藏窗口', + scopeGlobal: '全局', + scopeApp: '应用内', + enabled: '启用', + disabled: '禁用', messages: { resetSuccess: '已恢复默认快捷键,请记得保存', conflict: '存在冲突的快捷键,请重新设置', saveSuccess: '快捷键设置已保存', saveError: '保存快捷键失败,请重试', - cancelEdit: '已取消修改' + cancelEdit: '已取消修改', + disableAll: '已禁用所有快捷键,请记得保存', + enableAll: '已启用所有快捷键,请记得保存' } } }; diff --git a/src/main/modules/shortcuts.ts b/src/main/modules/shortcuts.ts index 0f0c2e0..728406a 100644 --- a/src/main/modules/shortcuts.ts +++ b/src/main/modules/shortcuts.ts @@ -7,66 +7,93 @@ ipcMain.on('get-platform', (event) => { event.returnValue = process.platform; }); +// 定义快捷键配置接口 +export interface ShortcutConfig { + key: string; + enabled: boolean; + scope: 'global' | 'app'; +} + +export interface ShortcutsConfig { + [key: string]: ShortcutConfig; +} + // 定义默认快捷键 -export const defaultShortcuts = { - togglePlay: 'CommandOrControl+Alt+P', - prevPlay: 'CommandOrControl+Alt+Left', - nextPlay: 'CommandOrControl+Alt+Right', - volumeUp: 'CommandOrControl+Alt+Up', - volumeDown: 'CommandOrControl+Alt+Down', - toggleFavorite: 'CommandOrControl+Alt+L', - toggleWindow: 'CommandOrControl+Alt+Shift+M' +export const defaultShortcuts: ShortcutsConfig = { + togglePlay: { key: 'CommandOrControl+Alt+P', enabled: true, scope: 'global' }, + prevPlay: { key: 'Alt+Left', enabled: true, scope: 'global' }, + nextPlay: { key: 'Alt+Right', enabled: true, scope: 'global' }, + volumeUp: { key: 'Alt+Up', enabled: true, scope: 'app' }, + volumeDown: { key: 'Alt+Down', enabled: true, scope: 'app' }, + toggleFavorite: { key: 'CommandOrControl+Alt+L', enabled: true, scope: 'app' }, + toggleWindow: { key: 'CommandOrControl+Alt+Shift+M', enabled: true, scope: 'global' } }; let mainWindowRef: Electron.BrowserWindow | null = null; // 注册快捷键 -export function registerShortcuts(mainWindow: Electron.BrowserWindow) { +export function registerShortcuts( + mainWindow: Electron.BrowserWindow, + shortcutsConfig?: ShortcutsConfig +) { mainWindowRef = mainWindow; const store = getStore(); - const shortcuts = store.get('shortcuts'); + const shortcuts = + shortcutsConfig || (store.get('shortcuts') as ShortcutsConfig) || defaultShortcuts; // 注销所有已注册的快捷键 globalShortcut.unregisterAll(); - // 显示/隐藏主窗口 - globalShortcut.register(shortcuts.toggleWindow, () => { - if (mainWindow.isVisible()) { - mainWindow.hide(); - } else { - mainWindow.show(); + // 对旧格式数据进行兼容处理 + if (shortcuts && typeof shortcuts.togglePlay === 'string') { + // 将 shortcuts 强制转换为 unknown,再转为 Record + const oldShortcuts = { ...shortcuts } as unknown as Record; + const newShortcuts: ShortcutsConfig = {}; + + Object.entries(oldShortcuts).forEach(([key, value]) => { + newShortcuts[key] = { + key: value, + enabled: true, + scope: ['volumeUp', 'volumeDown', 'toggleFavorite'].includes(key) ? 'app' : 'global' + }; + }); + + store.set('shortcuts', newShortcuts); + registerShortcuts(mainWindow, newShortcuts); + return; + } + + // 注册全局快捷键 + Object.entries(shortcuts).forEach(([action, config]) => { + const { key, enabled, scope } = config as ShortcutConfig; + + // 只注册启用且作用域为全局的快捷键 + if (!enabled || scope !== 'global') return; + + try { + switch (action) { + case 'toggleWindow': + globalShortcut.register(key, () => { + if (mainWindow.isVisible()) { + mainWindow.hide(); + } else { + mainWindow.show(); + } + }); + break; + default: + globalShortcut.register(key, () => { + mainWindow.webContents.send('global-shortcut', action); + }); + break; + } + } catch (error) { + console.error(`注册快捷键 ${key} 失败:`, error); } }); - // 播放/暂停 - globalShortcut.register(shortcuts.togglePlay, () => { - mainWindow.webContents.send('global-shortcut', 'togglePlay'); - }); - - // 上一首 - globalShortcut.register(shortcuts.prevPlay, () => { - mainWindow.webContents.send('global-shortcut', 'prevPlay'); - }); - - // 下一首 - globalShortcut.register(shortcuts.nextPlay, () => { - mainWindow.webContents.send('global-shortcut', 'nextPlay'); - }); - - // 音量增加 - globalShortcut.register(shortcuts.volumeUp, () => { - mainWindow.webContents.send('global-shortcut', 'volumeUp'); - }); - - // 音量减少 - globalShortcut.register(shortcuts.volumeDown, () => { - mainWindow.webContents.send('global-shortcut', 'volumeDown'); - }); - - // 收藏当前歌曲 - globalShortcut.register(shortcuts.toggleFavorite, () => { - mainWindow.webContents.send('global-shortcut', 'toggleFavorite'); - }); + // 通知渲染进程更新应用内快捷键 + mainWindow.webContents.send('update-app-shortcuts', shortcuts); } // 初始化快捷键 @@ -85,4 +112,11 @@ export function initializeShortcuts(mainWindow: Electron.BrowserWindow) { registerShortcuts(mainWindowRef); } }); + + // 监听快捷键更新事件 + ipcMain.on('update-shortcuts', (_, shortcutsConfig: ShortcutsConfig) => { + if (mainWindowRef) { + registerShortcuts(mainWindowRef, shortcutsConfig); + } + }); } diff --git a/src/renderer/App.vue b/src/renderer/App.vue index b50bba3..900f00b 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -25,6 +25,7 @@ import { isElectron, isLyricWindow } from '@/utils'; import { initAudioListeners } from './hooks/MusicHook'; import { isMobile } from './utils'; +import { useAppShortcuts } from './utils/appShortcuts'; import { initShortcut } from './utils/shortcut'; const { locale } = useI18n(); @@ -101,6 +102,9 @@ if (isElectron) { }); } +// 使用应用内快捷键 +useAppShortcuts(); + onMounted(async () => { if (isLyricWindow.value) { return; diff --git a/src/renderer/components/settings/ShortcutSettings.vue b/src/renderer/components/settings/ShortcutSettings.vue index 48930de..8de7f68 100644 --- a/src/renderer/components/settings/ShortcutSettings.vue +++ b/src/renderer/components/settings/ShortcutSettings.vue @@ -16,24 +16,54 @@
{{ getShortcutLabel(key) }}
-
- - - - {{ t('settings.shortcutSettings.shortcutConflict') }} - +
+
+ + + + {{ t('settings.shortcutSettings.shortcutConflict') }} + +
+
+ + + {{ + shortcut.enabled + ? t('settings.shortcutSettings.enabled') + : t('settings.shortcutSettings.disabled') + }} + + + + {{ + shortcut.scope === 'global' + ? t('settings.shortcutSettings.scopeGlobal') + : t('settings.shortcutSettings.scopeApp') + }} + +
@@ -46,6 +76,12 @@ {{ t('settings.shortcutSettings.resetShortcuts') }} + {{ + t('settings.shortcutSettings.disableAll') + }} + {{ + t('settings.shortcutSettings.enableAll') + }} {{ t('common.save') }} @@ -66,26 +102,37 @@ import { isElectron } from '@/utils'; const { t } = useI18n(); +interface ShortcutConfig { + key: string; + enabled: boolean; + scope: 'global' | 'app'; +} + interface Shortcuts { - togglePlay: string; - prevPlay: string; - nextPlay: string; - volumeUp: string; - volumeDown: string; - toggleFavorite: string; - toggleWindow: string; + togglePlay: ShortcutConfig; + prevPlay: ShortcutConfig; + nextPlay: ShortcutConfig; + volumeUp: ShortcutConfig; + volumeDown: ShortcutConfig; + toggleFavorite: ShortcutConfig; + toggleWindow: ShortcutConfig; } const defaultShortcuts: Shortcuts = { - togglePlay: 'CommandOrControl+Alt+P', - prevPlay: 'Alt+Left', - nextPlay: 'Alt+Right', - volumeUp: 'Alt+Up', - volumeDown: 'Alt+Down', - toggleFavorite: 'CommandOrControl+Alt+L', - toggleWindow: 'CommandOrControl+Alt+Shift+M' + togglePlay: { key: 'CommandOrControl+Alt+P', enabled: true, scope: 'global' }, + prevPlay: { key: 'Alt+Left', enabled: true, scope: 'global' }, + nextPlay: { key: 'Alt+Right', enabled: true, scope: 'global' }, + volumeUp: { key: 'Alt+Up', enabled: true, scope: 'app' }, + volumeDown: { key: 'Alt+Down', enabled: true, scope: 'app' }, + toggleFavorite: { key: 'CommandOrControl+Alt+L', enabled: true, scope: 'app' }, + toggleWindow: { key: 'CommandOrControl+Alt+Shift+M', enabled: true, scope: 'global' } }; +const scopeOptions = [ + { label: t('settings.shortcutSettings.scopeGlobal'), value: 'global' }, + { label: t('settings.shortcutSettings.scopeApp'), value: 'app' } +]; + const shortcuts = ref( isElectron ? window.electron.ipcRenderer.sendSync('get-store-value', 'shortcuts') || defaultShortcuts @@ -93,7 +140,7 @@ const shortcuts = ref( ); // 临时存储编辑中的快捷键 -const tempShortcuts = ref({ ...shortcuts.value }); +const tempShortcuts = ref(cloneDeep(shortcuts.value)); // 监听快捷键更新 if (isElectron) { @@ -101,7 +148,7 @@ if (isElectron) { const newShortcuts = window.electron.ipcRenderer.sendSync('get-store-value', 'shortcuts'); if (newShortcuts) { shortcuts.value = newShortcuts; - tempShortcuts.value = { ...newShortcuts }; + tempShortcuts.value = cloneDeep(newShortcuts); } }); } @@ -116,12 +163,27 @@ onMounted(() => { console.log('storedShortcuts', storedShortcuts); if (storedShortcuts) { shortcuts.value = storedShortcuts; - tempShortcuts.value = { ...storedShortcuts }; + tempShortcuts.value = cloneDeep(storedShortcuts); } else { shortcuts.value = { ...defaultShortcuts }; - tempShortcuts.value = { ...defaultShortcuts }; + tempShortcuts.value = cloneDeep(defaultShortcuts); window.electron.ipcRenderer.send('set-store-value', 'shortcuts', defaultShortcuts); } + + // 转换旧格式的快捷键数据到新格式 + if (storedShortcuts && typeof storedShortcuts.togglePlay === 'string') { + const convertedShortcuts = {} as Shortcuts; + Object.entries(storedShortcuts).forEach(([key, value]) => { + convertedShortcuts[key as keyof Shortcuts] = { + key: value as string, + enabled: true, + scope: ['volumeUp', 'volumeDown', 'toggleFavorite'].includes(key) ? 'app' : 'global' + }; + }); + shortcuts.value = convertedShortcuts; + tempShortcuts.value = cloneDeep(convertedShortcuts); + window.electron.ipcRenderer.send('set-store-value', 'shortcuts', convertedShortcuts); + } } }); @@ -144,13 +206,21 @@ const message = useMessage(); // 检查快捷键冲突 const duplicateKeys = computed(() => { const result: Record = {}; - const usedShortcuts = new Set(); + const usedShortcuts = new Map(); Object.entries(tempShortcuts.value).forEach(([key, shortcut]) => { - if (usedShortcuts.has(shortcut)) { - result[key] = true; + // 只检查启用的快捷键 + if (!shortcut.enabled) return; + + const conflictKey = usedShortcuts.get(shortcut.key); + if (conflictKey) { + // 只有相同作用域的快捷键才会被认为冲突 + const conflictScope = tempShortcuts.value[conflictKey as keyof Shortcuts].scope; + if (shortcut.scope === conflictScope) { + result[key] = true; + } } else { - usedShortcuts.add(shortcut); + usedShortcuts.set(shortcut.key, key); } }); @@ -161,6 +231,8 @@ const duplicateKeys = computed(() => { const hasConflict = computed(() => Object.keys(duplicateKeys.value).length > 0); const startRecording = (key: keyof Shortcuts) => { + if (!tempShortcuts.value[key].enabled) return; + isRecording.value = true; currentKey.value = key; // 禁用全局快捷键 @@ -220,12 +292,12 @@ const handleKeyDown = (e: KeyboardEvent, key: keyof Shortcuts) => { } if (!['Control', 'Alt', 'Shift', 'Meta', 'Command'].includes(keyName)) { - tempShortcuts.value[key] = [...modifiers, keyName].join('+'); + tempShortcuts.value[key].key = [...modifiers, keyName].join('+'); } }; const resetShortcuts = () => { - tempShortcuts.value = { ...defaultShortcuts }; + tempShortcuts.value = cloneDeep(defaultShortcuts); message.success(t('settings.shortcutSettings.messages.resetSuccess')); }; @@ -245,7 +317,7 @@ const saveShortcuts = () => { // 先保存到 store window.electron.ipcRenderer.send('set-store-value', 'shortcuts', shortcutsToSave); // 然后更新快捷键 - window.electron.ipcRenderer.send('update-shortcuts'); + window.electron.ipcRenderer.send('update-shortcuts', shortcutsToSave); message.success(t('settings.shortcutSettings.messages.saveSuccess')); } catch (error) { console.error('保存快捷键失败:', error); @@ -255,7 +327,7 @@ const saveShortcuts = () => { }; const cancelEdit = () => { - tempShortcuts.value = { ...shortcuts.value }; + tempShortcuts.value = cloneDeep(shortcuts.value); message.info(t('settings.shortcutSettings.messages.cancelEdit')); emit('update:show', false); }; @@ -309,7 +381,7 @@ watch(visible, (newVal) => { // 处理弹窗关闭后的事件 const handleAfterLeave = () => { // 重置临时数据 - tempShortcuts.value = { ...shortcuts.value }; + tempShortcuts.value = cloneDeep(shortcuts.value); }; // 处理取消按钮点击 @@ -324,6 +396,22 @@ const handleSave = () => { visible.value = false; emit('change', shortcuts.value); }; + +// 全部禁用快捷键 +const disableAllShortcuts = () => { + Object.keys(tempShortcuts.value).forEach((key) => { + tempShortcuts.value[key as keyof Shortcuts].enabled = false; + }); + message.info(t('settings.shortcutSettings.messages.disableAll')); +}; + +// 全部启用快捷键 +const enableAllShortcuts = () => { + Object.keys(tempShortcuts.value).forEach((key) => { + tempShortcuts.value[key as keyof Shortcuts].enabled = true; + }); + message.info(t('settings.shortcutSettings.messages.enableAll')); +};