Skip to content

fix: 修复 Retina 屏幕截图和翻译结果显示放大问题#12

Merged
hubo1989 merged 2 commits into
mainfrom
fix-highresolution
Feb 10, 2026
Merged

fix: 修复 Retina 屏幕截图和翻译结果显示放大问题#12
hubo1989 merged 2 commits into
mainfrom
fix-highresolution

Conversation

@hubo1989
Copy link
Copy Markdown
Owner

@hubo1989 hubo1989 commented Feb 10, 2026

Summary

  • 修复 Retina 屏幕上区域截图预览和翻译结果窗口显示为原始选区 2 倍大小的问题,使用 backingScaleFactor 将像素尺寸正确换算为点尺寸
  • 移除渲染结果中多余的"译文对照"标题,使原文与译文位置对齐
  • HistoryView.TextSection 的未使用 label 参数添加 accessibilityLabel

Test plan

  • 在 Retina 屏幕上进行区域截图,验证预览窗口大小与选区一致
  • 执行截图翻译,验证等待和结果页面大小正确
  • 检查翻译结果图片中译文与原文位置对齐
  • 在非 Retina 屏幕上验证无回归

🤖 Generated with Claude Code

Summary by CodeRabbit

发布说明

  • 新功能

    • 历史记录项增加了无障碍标签,改善可访问性
  • 改进与优化

    • 在不同屏幕缩放下优化了图像显示、缩放与坐标换算,预览和带注释视图的缩放行为更一致
    • 改进了窗口按图像尺寸自动调整的计算逻辑,保持最小窗口约束
    • 调整了翻译对照区的界面呈现,移除了标题和分隔线

- 预览窗口使用 displayScaleFactor 正确换算像素到点坐标,保持截图与原始选区大小一致
- 翻译等待和结果页面同样使用 backingScaleFactor 计算窗口和图片尺寸
- 移除渲染结果中多余的"译文对照"标题,使原文-译文位置对齐
- 为 HistoryView 的 TextSection 添加 accessibilityLabel 消除未使用参数警告

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

引入 displayScaleFactor 并将多处图像/窗口尺寸计算从像素改为逻辑点(point)单位;在双语结果、预览视图与窗口控制器中传递并使用该因子;同时为历史条目添加无障碍标签,并从 OverlayRenderer 中移除右侧翻译区标题与分隔线。

Changes

Cohort / File(s) Summary
双语结果视图与模型
ScreenTranslate/Features/BilingualResult/BilingualResultView.swift, ScreenTranslate/Features/BilingualResult/BilingualResultViewModel.swift
新增 displayScaleFactor,引入 imagePointWidth/imagePointHeight(基于点值),将视图中 Image/frame 及缩放相关计算改为使用点值。
双语结果窗口控制器
ScreenTranslate/Features/BilingualResult/BilingualResultWindowController.swift
添加读取屏幕 scale 的逻辑与 calculateWindowSize 辅助函数;将所有窗口尺寸计算改为基于 image point 尺寸,并在创建 ViewModel 时传入 displayScaleFactor
预览视图与窗口
ScreenTranslate/Features/Preview/PreviewAnnotatedImageView.swift, ScreenTranslate/Features/Preview/PreviewWindow.swift
新增/使用 displayScaleFactor,调整 Image 缩放、AnnotationCanvas 与 Overlay 的 scale,统一坐标转换为点/像素间的正确映射,窗口初始与重置大小改为基于点值。
历史与渲染器调整
ScreenTranslate/Features/History/HistoryView.swift, ScreenTranslate/Services/OverlayRenderer.swift
为历史条目文本添加无障碍标签;从 overlay 的 side-by-side 渲染路径移除右侧翻译区的标题与分隔线渲染。

Sequence Diagram(s)

sequenceDiagram
    participant WC as WindowController
    participant VM as BilingualResultViewModel
    participant V as BilingualResultView
    participant Screen as NSScreen
    Note over WC,Screen: 计算 displayScaleFactor (backingScaleFactor)
    WC->>Screen: read backingScaleFactor
    WC->>VM: init(image, displayScaleFactor)
    VM-->>WC: imagePointWidth/imagePointHeight
    WC->>V: set frame size based on imagePointWidth/height
    V->>VM: request image rendering / zoom
    VM-->>V: provide image + scale info
Loading

Estimated code review effort

🎯 3 (中等) | ⏱️ ~25 分钟

Possibly related PRs

🐰 像素跳舞成点值,
缩放因子轻声喵,
窗口重算新衣裳,
坐标对齐步儿妙,
高分屏下笑开囊。

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确反映了主要变更,即修复Retina屏幕上截图和翻译结果显示放大的问题,与代码变更的核心目标一致。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-highresolution

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ScreenTranslate/Features/BilingualResult/BilingualResultWindowController.swift (1)

85-110: ⚠️ Potential issue | 🟡 Minor

showResultdisplayScaleFactor 未同步更新到 viewModel。

showResult 被调用时,仅更新了图片但未更新 viewModel.displayScaleFactor。如果屏幕配置在 showLoadingshowResult 之间发生变化(例如用户插拔显示器),可能导致缩放不一致。

考虑在 BilingualResultViewModel 中添加更新 displayScaleFactor 的方法,或在 showResult 时重新创建 viewModel。

🛠️ 建议的修复方案

BilingualResultViewModel 中添加:

func showResult(image: CGImage, displayScaleFactor: CGFloat? = nil) {
    self.image = image
    if let scaleFactor = displayScaleFactor {
        self.displayScaleFactor = scaleFactor
    }
    self.isLoading = false
    self.loadingMessage = ""
    self.errorMessage = nil
    self.scale = 1.0
}

然后在 showResult 调用时传入当前的 scaleFactor。

🤖 Fix all issues with AI agents
In `@ScreenTranslate/Features/BilingualResult/BilingualResultViewModel.swift`:
- Line 9: displayScaleFactor is immutable and never updated when the window
moves between DPI scales; make displayScaleFactor a mutable var (remove or relax
private(set)) and ensure you update it whenever you grab the currentScaleFactor
in BilingualResultWindowController.showResult() and in updateImage(): assign
viewModel.displayScaleFactor = currentScaleFactor (or pass currentScaleFactor
into viewModel.showResult(image:) and set there) so the ViewModel uses the
correct scale for image sizing; update the initializer to set a sensible default
but rely on runtime updates in showResult(image:) and updateImage() to keep the
scale in sync.
🧹 Nitpick comments (2)
ScreenTranslate/Features/BilingualResult/BilingualResultWindowController.swift (2)

15-18: 多显示器场景下的缩放因子考量。

currentScaleFactor 使用 NSScreen.main?.backingScaleFactor,但窗口可能显示在非主显示器上。在主显示器为 Retina(2x)而外接显示器为非 Retina(1x)的场景下,如果窗口实际显示在外接显示器上,计算的尺寸可能不准确。

不过考虑到窗口创建时通常会 center() 到主屏幕,且这种边界情况影响有限,当前实现是可接受的。


116-160: 考虑提取窗口创建的公共逻辑。

showLoadingshow 方法中的窗口创建逻辑高度相似,包括:NSWindow 配置、contentView 设置、minSize、collectionBehavior 等。

♻️ 建议提取公共方法
private func createWindow(size: NSSize) -> NSWindow {
    let newWindow = NSWindow(
        contentRect: NSRect(x: 0, y: 0, width: size.width, height: size.height),
        styleMask: [.titled, .closable, .miniaturizable, .resizable],
        backing: .buffered,
        defer: false
    )
    newWindow.title = String(localized: "bilingualResult.window.title")
    newWindow.center()
    newWindow.isReleasedWhenClosed = false
    newWindow.delegate = self
    newWindow.minSize = NSSize(width: 400, height: 300)
    newWindow.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
    return newWindow
}

Comment thread ScreenTranslate/Features/BilingualResult/BilingualResultViewModel.swift Outdated
showResult 和 updateImage 现在接收并更新 displayScaleFactor,
确保窗口在不同 DPI 屏幕间移动后仍使用正确的缩放因子。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@ScreenTranslate/Features/BilingualResult/BilingualResultWindowController.swift`:
- Around line 35-43: The existing showLoading(originalImage:message:) reads
currentScaleFactor into scaleFactor when reusing an already-visible window but
never updates the viewModel with it, so the ViewModel may keep an outdated
displayScaleFactor; inside the reuse branch (where you check if let
existingWindow = window, existingWindow.isVisible) call the appropriate
ViewModel setter/update method or property (e.g., viewModel?.displayScaleFactor
= currentScaleFactor or viewModel?.updateDisplayScaleFactor(currentScaleFactor))
before invoking viewModel?.showLoading(...), ensuring the viewModel's
displayScaleFactor/current scale is synchronized with currentScaleFactor
whenever reusing the window (references: showLoading, currentScaleFactor,
viewModel, window/existingWindow).

Comment on lines 35 to 43
func showLoading(originalImage: CGImage, message: String? = nil) {
let scaleFactor = currentScaleFactor

if let existingWindow = window, existingWindow.isVisible {
viewModel?.showLoading(originalImage: originalImage, message: message)
existingWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

复用窗口时未同步 displayScaleFactor,可能仍以旧 DPI 渲染。
已有窗口分支里读取了 scaleFactor,但未写回 ViewModel,跨屏后加载态可能仍用旧缩放因子。建议在复用时同步更新。

🔧 建议修改
if let existingWindow = window, existingWindow.isVisible {
+    viewModel?.displayScaleFactor = scaleFactor
     viewModel?.showLoading(originalImage: originalImage, message: message)
     existingWindow.makeKeyAndOrderFront(nil)
     NSApp.activate(ignoringOtherApps: true)
     return
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func showLoading(originalImage: CGImage, message: String? = nil) {
let scaleFactor = currentScaleFactor
if let existingWindow = window, existingWindow.isVisible {
viewModel?.showLoading(originalImage: originalImage, message: message)
existingWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return
}
func showLoading(originalImage: CGImage, message: String? = nil) {
let scaleFactor = currentScaleFactor
if let existingWindow = window, existingWindow.isVisible {
viewModel?.displayScaleFactor = scaleFactor
viewModel?.showLoading(originalImage: originalImage, message: message)
existingWindow.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
return
}
🤖 Prompt for AI Agents
In
`@ScreenTranslate/Features/BilingualResult/BilingualResultWindowController.swift`
around lines 35 - 43, The existing showLoading(originalImage:message:) reads
currentScaleFactor into scaleFactor when reusing an already-visible window but
never updates the viewModel with it, so the ViewModel may keep an outdated
displayScaleFactor; inside the reuse branch (where you check if let
existingWindow = window, existingWindow.isVisible) call the appropriate
ViewModel setter/update method or property (e.g., viewModel?.displayScaleFactor
= currentScaleFactor or viewModel?.updateDisplayScaleFactor(currentScaleFactor))
before invoking viewModel?.showLoading(...), ensuring the viewModel's
displayScaleFactor/current scale is synchronized with currentScaleFactor
whenever reusing the window (references: showLoading, currentScaleFactor,
viewModel, window/existingWindow).

@hubo1989 hubo1989 merged commit 8c72f87 into main Feb 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant