diff --git a/build-config/tests/list-context-menu.test.mjs b/build-config/tests/list-context-menu.test.mjs new file mode 100644 index 0000000000..9e425df688 --- /dev/null +++ b/build-config/tests/list-context-menu.test.mjs @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict' + +import { shouldCopyListTextOnContextMenu } from '../../src/renderer/utils/listContextMenu.mjs' + +const run = (name, fn) => { + try { + fn() + console.log(`PASS ${name}`) + } catch (error) { + console.error(`FAIL ${name}`) + throw error + } +} + +run('does not hijack row menu when right-clicking selectable text without an active selection', () => { + assert.equal(shouldCopyListTextOnContextMenu({ + isSelectTextTarget: true, + selectionText: '', + }), false) +}) + +run('keeps text copy behavior when right-clicking selected text', () => { + assert.equal(shouldCopyListTextOnContextMenu({ + isSelectTextTarget: true, + selectionText: 'Song Name', + }), true) +}) + +run('ignores non-select targets', () => { + assert.equal(shouldCopyListTextOnContextMenu({ + isSelectTextTarget: false, + selectionText: 'Song Name', + }), false) +}) diff --git a/src/renderer/components/material/OnlineList/index.vue b/src/renderer/components/material/OnlineList/index.vue index fcdf52728d..4ffe8f3e78 100644 --- a/src/renderer/components/material/OnlineList/index.vue +++ b/src/renderer/components/material/OnlineList/index.vue @@ -102,6 +102,7 @@ import { clipboardWriteText } from '@common/utils/electron' import { assertApiSupport } from '@renderer/store/utils' import { ref } from '@common/utils/vueTools' +import { shouldCopyListTextOnContextMenu, formatListSelectionText } from '@renderer/utils/listContextMenu.mjs' import useList from './useList' import useMenu from './useMenu' import usePlay from './usePlay' @@ -218,14 +219,17 @@ export default { menuClick(action, index) } const handleListRightClick = (event) => { - if (!event.target.classList.contains('select')) return + const selectionText = window.getSelection().toString() + if (!shouldCopyListTextOnContextMenu({ + isSelectTextTarget: event.target.classList.contains('select'), + selectionText, + })) return event.stopImmediatePropagation() let classList = dom_listContent.value.classList classList.add('copying') window.requestAnimationFrame(() => { - let str = window.getSelection().toString() classList.remove('copying') - str = str.split(/\n\n/).map(s => s.replace(/\n/g, ' ')).join('\n').trim() + let str = formatListSelectionText(window.getSelection().toString()) if (!str.length) return clipboardWriteText(str) }) diff --git a/src/renderer/utils/listContextMenu.mjs b/src/renderer/utils/listContextMenu.mjs new file mode 100644 index 0000000000..e557477ef1 --- /dev/null +++ b/src/renderer/utils/listContextMenu.mjs @@ -0,0 +1,14 @@ +export const shouldCopyListTextOnContextMenu = ({ + isSelectTextTarget, + selectionText, +}) => { + return isSelectTextTarget && !!selectionText.trim() +} + +export const formatListSelectionText = (selectionText) => { + return selectionText + .split(/\n\n/) + .map(text => text.replace(/\n/g, ' ')) + .join('\n') + .trim() +} diff --git a/src/renderer/views/List/MusicList/index.vue b/src/renderer/views/List/MusicList/index.vue index 9681972deb..468a544a8a 100644 --- a/src/renderer/views/List/MusicList/index.vue +++ b/src/renderer/views/List/MusicList/index.vue @@ -106,6 +106,7 @@