修复输入框聚焦时 Cmd+V 粘贴图片失效:按剪贴板内容分流
根因:⌘V 在 QML 里有两条互斥路径。焦点不在输入框时走顶层
Shortcut{StandardKey.Paste} 粘贴图片;焦点在 promptArea 里时,
编辑控件通过 Qt 的 ShortcutOverride 吞掉 ⌘V 走内置文本粘贴,
那个全局 Shortcut 永远拿不到事件 —— 而剪贴板里是图片、文本粘贴
贴不出东西,表现为"粘贴无效"。
按钮直接调 pasteFromClipboardAction
绕开按键路由,所以一直有效。
修复(按剪贴板内容分流,而非靠焦点状态):
- bridges/imagegen.py 新增 clipboardHasImage():只读 formats()/urls(),
无副作用、不写临时文件、macOS 不碰会崩的 imageData()。
- promptArea 加 Keys.onPressed:matches(StandardKey.Paste) 且剪贴板有图
→ 粘图 + event.accepted;否则放行给 TextArea 做文本粘贴。
文本粘贴行为不变(Never break userspace)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Showing
2 changed files
with
40 additions
and
2 deletions
| ... | @@ -172,6 +172,32 @@ class ImageGenBridge(QObject): | ... | @@ -172,6 +172,32 @@ class ImageGenBridge(QObject): |
| 172 | self._tqm.cancel_task(task_id) | 172 | self._tqm.cancel_task(task_id) |
| 173 | self.busyChanged.emit() | 173 | self.busyChanged.emit() |
| 174 | 174 | ||
| 175 | @Slot(result=bool) | ||
| 176 | def clipboardHasImage(self) -> bool: | ||
| 177 | """剪贴板里是否有图片 —— 轻量、无副作用、macOS 安全。 | ||
| 178 | |||
| 179 | 只读 mimeData.formats() / urls(),不做反序列化、不写临时文件, | ||
| 180 | 所以即便在 macOS 上也不会触发 application/x-qt-image → NSImage 的 | ||
| 181 | native crash(那个风险只在 imageData()/loadFromData 时出现,见 | ||
| 182 | _extract_clipboard_image)。给焦点在输入框时的 Ctrl/Cmd+V 路由用: | ||
| 183 | 有图就走图片粘贴,否则放行给文本粘贴。 | ||
| 184 | """ | ||
| 185 | clipboard = QGuiApplication.clipboard() | ||
| 186 | if clipboard is None: | ||
| 187 | return False | ||
| 188 | mime_data = clipboard.mimeData() | ||
| 189 | if mime_data is None: | ||
| 190 | return False | ||
| 191 | # 路径 A:文件 URL 指向图片文件 | ||
| 192 | if mime_data.hasUrls(): | ||
| 193 | for url in mime_data.urls(): | ||
| 194 | if url.isLocalFile() and Path(url.toLocalFile()).suffix.lower() in _PASTE_VALID_EXT: | ||
| 195 | return True | ||
| 196 | # 路径 B/C:剪贴板里挂着图像 MIME(仅列 formats,不读字节) | ||
| 197 | image_mimes = {"image/png", "image/jpeg", "image/bmp", "image/tiff", | ||
| 198 | "image/gif", "image/webp", "application/x-qt-image"} | ||
| 199 | return any(fmt in image_mimes for fmt in mime_data.formats()) | ||
| 200 | |||
| 175 | @Slot(result="QVariantList") | 201 | @Slot(result="QVariantList") |
| 176 | def pasteFromClipboard(self) -> list: | 202 | def pasteFromClipboard(self) -> list: |
| 177 | """从系统剪贴板拿图片,返回本地路径列表(正斜杠风格)。失败返回空列表。 | 203 | """从系统剪贴板拿图片,返回本地路径列表(正斜杠风格)。失败返回空列表。 | ... | ... |
| ... | @@ -122,8 +122,9 @@ Item { | ... | @@ -122,8 +122,9 @@ Item { |
| 122 | } | 122 | } |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | // 全局 Ctrl+V 粘贴图片(仅在图片生成 tab 激活时生效; | 125 | // 全局 Ctrl/Cmd+V 粘贴图片:覆盖焦点不在输入框的情况(按钮/下拉/空白处)。 |
| 126 | // 焦点在 TextArea/TextField 里 Ctrl+V 优先走文本粘贴,不会触发这个 Shortcut) | 126 | // 焦点在 promptArea 里时编辑控件会吞掉 Ctrl/Cmd+V,这个 Shortcut 拿不到, |
| 127 | // 那种情况由 promptArea 的 Keys.onPressed 按剪贴板内容分流(见下方 TextArea)。 | ||
| 127 | Shortcut { | 128 | Shortcut { |
| 128 | sequences: [StandardKey.Paste] | 129 | sequences: [StandardKey.Paste] |
| 129 | enabled: appState.currentTab === 0 | 130 | enabled: appState.currentTab === 0 |
| ... | @@ -635,6 +636,17 @@ Item { | ... | @@ -635,6 +636,17 @@ Item { |
| 635 | wrapMode: TextArea.Wrap | 636 | wrapMode: TextArea.Wrap |
| 636 | selectionColor: App.Theme.accent | 637 | selectionColor: App.Theme.accent |
| 637 | selectedTextColor: App.Theme.textOnAccent | 638 | selectedTextColor: App.Theme.textOnAccent |
| 639 | |||
| 640 | // 焦点在输入框时,编辑控件会吞掉 Ctrl/Cmd+V 走内置文本粘贴, | ||
| 641 | // 顶层那个 StandardKey.Paste Shortcut 拿不到 = 粘贴图片失效。 | ||
| 642 | // 这里按"剪贴板内容"分流:有图就粘图(吃掉事件), | ||
| 643 | // 没图就放行,让 TextArea 自己做文本粘贴。 | ||
| 644 | Keys.onPressed: function(event) { | ||
| 645 | if (event.matches(StandardKey.Paste) && imageGen.clipboardHasImage()) { | ||
| 646 | tab.pasteFromClipboardAction() | ||
| 647 | event.accepted = true | ||
| 648 | } | ||
| 649 | } | ||
| 638 | background: Rectangle { | 650 | background: Rectangle { |
| 639 | color: App.Theme.bgSubtle | 651 | color: App.Theme.bgSubtle |
| 640 | radius: App.Theme.radiusMd | 652 | radius: App.Theme.radiusMd | ... | ... |
-
Please register or sign in to post a comment