Skip to content

fix: マルチディスプレイ環境で変換候補ウィンドウがカーソルと異なるディスプレイに表示される不具合を修正#323

Open
itouuuuuuuuu wants to merge 1 commit intoazooKey:mainfrom
itouuuuuuuuu:fix/candidate-window-multi-monitor
Open

fix: マルチディスプレイ環境で変換候補ウィンドウがカーソルと異なるディスプレイに表示される不具合を修正#323
itouuuuuuuuu wants to merge 1 commit intoazooKey:mainfrom
itouuuuuuuuu:fix/candidate-window-multi-monitor

Conversation

@itouuuuuuuuu
Copy link
Copy Markdown

問題

Closes #322

マルチディスプレイ環境で日本語入力中にスペースを押して変換すると、変換候補ウィンドウがカーソルのあるディスプレイとは別のディスプレイに表示されることがある不具合の修正です。

原因

BaseCandidateViewController.resizeWindowToFitContent(cursorLocation:) が、window.screen から取った visibleFrameWindowPositioning.frameNearCursor に渡していました。

window.screen「ウィンドウが現在乗っているスクリーン」 を返すため、マルチディスプレイ環境でカーソルが別ディスプレイへ移動した直後は、カーソル所在のディスプレイと一致しないことがあります。

その状態で frameNearCursor 内の右端クランプ:

if newWindowFrame.maxX > screenRect.maxX {
    newWindowFrame.origin.x = screenRect.maxX - newWindowFrame.width
}

が走ると、カーソル所在ディスプレイのグローバル座標(例: 右モニターの x=2000)と、別ディスプレイの screenRect.maxX(例: 左モニターの 1920)を比較してしまい、ウィンドウが誤ったディスプレイの右端に貼り付く症状が発生していました。

positionPredictionWindowRightOfCandidateWindowpredictionWindow.screen ?? candidatesWindow.screen を使っており、同じ理由で予測ウィンドウも候補ウィンドウに引きずられていました。

なお PromptInputWindow.adjustWindowPositionNSScreen.screens.first(where: { NSMouseInRect(cursorLocation, $0.frame, false) }) でカーソル所在のスクリーンを正しく選んでいるため、候補ウィンドウ系だけ同様のケアが抜けていた状態でした。

修正内容

1. ScreenLookup ヘルパーの追加

azooKeyMac/InputController/WindowPositioning+CoreGraphics.swift にカーソル位置を含む NSScreen を引くヘルパーを追加しました。

  • 包含判定には NSScreen.frame(メニューバー / Dock を含むディスプレイ全体)を使用。visibleFrame で判定するとメニューバー直下や Dock 領域付近で取りこぼすことがあるため、包含と利用領域を意図的に分離(クランプには引き続き visibleFrame を渡す)。
  • フォールバック順は カーソル位置を含む screen → 最近接 screen → fallbackWindow?.screenNSScreen.main。本ヘルパーの主目的が「window.screen がカーソル所在のディスプレイと一致しない問題を避けること」なので、fallbackWindow?.screen より最近接 screen を優先しています。

2. BaseCandidateViewController.resizeWindowToFitContent の修正

window.screen 参照を ScreenLookup.screen(containing: cursorLocation, fallbackWindow: window) に置換。PredictionCandidatesViewController も同 superclass のため一括で解消されます。

3. positionPredictionWindowRightOfCandidateWindow の修正

candidatesWindow.frame の中心点が乗るスクリーンを ScreenLookup で能動的に判定する形に変更。*.screen プロパティの状態に依存しないようにしました。

4. WindowPositioning.frameNearCursor のクランプを 4 方向化

これまで右端しかクランプしておらず、左モニター(負 origin)や縦配置で破綻する余地がありました。左端・上端・下端のクランプを追加し、ウィンドウが screenRect より大きい場合は最終的に min 側に寄せるフォールバック挙動も明示しています。

動作確認

  • swift test --filter WindowPositioningTests(Core パッケージ)→ 11/11 PASS(既存 5 + 新規 6)
  • xcodebuild -scheme azooKeyMac -configuration Debug buildBUILD SUCCEEDED
  • swiftlint lint 変更ファイル全件 → 警告ゼロ
  • マルチディスプレイ実機(macOS Tahoe 26.3.1)で以下を目視確認:
    • 副ディスプレイで日本語入力 → スペース変換 → 候補がカーソルのあるディスプレイに表示される
    • 主ディスプレイでも同様に表示される(リグレッション無し)
    • アプリウィンドウを副 → 主にドラッグ移動して継続変換 → 候補が新しい側に追従する
    • 副ディスプレイ右端 / 左端付近で変換 → 隣のディスプレイに飛び移らない

window.screen は「ウィンドウが現在乗っているスクリーン」を返すため、
カーソルが別ディスプレイへ移動した直後に誤った visibleFrame を渡してしまい、
候補ウィンドウが隣ディスプレイの右端に貼り付く症状が出ていた。

- カーソル位置を含む NSScreen を引く ScreenLookup ヘルパーを追加
  (包含判定は frame、フォールバックは最近接 screen → window.screen → main)
- BaseCandidateViewController.resizeWindowToFitContent を ScreenLookup ベースに変更
- positionPredictionWindowRightOfCandidateWindow を候補ウィンドウ中心が乗る
  スクリーン基準に変更
- WindowPositioning.frameNearCursor に左端/上下のクランプを追加
  (これまで右端しかクランプしていなかった)
- WindowPositioningTests に副ディスプレイ/クランプ各種のケースを追加(+6 ケース)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@itouuuuuuuuu itouuuuuuuuu force-pushed the fix/candidate-window-multi-monitor branch from a63afdd to 928c74f Compare May 7, 2026 16:54
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.

bug: マルチディスプレイ環境で変換候補ウィンドウがカーソルと異なるディスプレイに表示される

1 participant