NarratorOverlayWindow
ゲーム画面に重ねて表示するオーバーレイ専用ウィンドウ(デスクトップアプリのみ、常時表示)。
WindowDecoration.Undecorated()かつresizable = trueの組み合わせで、Compose Multiplatform 独自の リサイズハンドル(androidx.compose.ui.window.UndecoratedWindowResizer)が有効になる。パネル全体のドラッグで移動できるよう、detectDragGestures でウィンドウ位置を直接更新する。 androidx.compose.foundation.gestures.PointerInputChange の
dragAmountはウィンドウ内のローカル座標 系での差分であり、これを毎フレーム加算してウィンドウを動かすと、ウィンドウの移動そのものが次フレームの ローカル座標の基準をずらしてしまい、位置計算がフィードバックループを起こしてガタつく(実機で確認済み)。 そのため、ドラッグ開始時のウィンドウ位置とポインタのスクリーン座標(MouseInfo.getPointerInfo)を基準に 保持し、以降は「現在のポインタのスクリーン座標との差分」から絶対位置としてウィンドウ位置を計算する。タスクバーに表示させないため
Window.Type.UTILITYを設定する。java.awt.Window#setTypeは ウィンドウが displayable になった後に呼ぶとIllegalComponentStateExceptionを送出するため、 通常のWindowcomposable の content 内LaunchedEffectから設定すると、環境によっては 既に displayable になっていて例外が発生しうる(実機で確認済み)。SwingWindow のinitはComposeWindow生成直後・displayable になる前に一度だけ呼ばれることが保証されているため、 最小/最大サイズの設定とあわせてここで行う。表示ON/OFFはその他タブの「オーバーレイ設定」で切り替える(NarratorOverlayWindowHost を参照)。
位置・サイズは initialBounds で復元し、変更を onBoundsChange で通知する(永続化は NarratorOverlayWindowHost が行う)。
window.locationを直接書き換えるドラッグ移動も、 SwingWindow が AWT のcomponentMoved/componentResizedを WindowState へ反映するため、 WindowState を監視するだけで移動・リサイズの両方を拾える。常時最前面の詳細な制御(フォーカス連動等)は別PRで対応する。
alwaysOnTop = trueにより通常のウィンドウより手前には表示されるが、排他的フルスクリーンで 起動したゲームには他の常駐オーバーレイツールと同様に表示されないため、LMU 側をボーダーレス ウィンドウモードで起動する前提となる。ゲーム画面をなるべく隠さないよう
transparent = trueでウィンドウ背景を透過させ、コンテンツ側 (NarratorOverlayScreen)の半透明な背景色と組み合わせている。isTransparent(transparentパラメータの実体)はウィンドウが表示済みの状態で変更すると例外を送出するが、SwingWindowのupdateブロックは値に変化がない限り再設定を行わないため、固定値trueを渡す限りは ウィンドウがまだ displayable になる前の初回update呼び出し時にのみ設定される。