バージョン情報
plugins { kotlin("jvm") version "1.4.30" id("org.jetbrains.compose") version "0.3.0-build150" }
本文
フレームなしウィンドウを作成してウィンドウを移動させるまで【Jetpack Compose Desktop】
の続きになります。
https://matsudamper.hatenablog.com/entry/2021/02/08/025214
フレームなしにしたらフレームと同じ機能を別で実装しないといけないですからね。
Jetpack Compose DesktopというよりはJavaのSwingの話になっています。
windowDecoratedInstead
という関数名にしました。maximize
とかrestore
を呼びます。mouseEvent.clickCount
を発見しました。こちらを使ってダブルクリックを実現しました。秒数とか取らなくて済みました。
@Composable fun Modifier.windowDecoratedInstead( doubleClickEvent: (() -> Unit)? = null ): Modifier { val window = LocalAppWindow.current val innerDoubleClickEvent = doubleClickEvent ?: { if (window.isMaximized) { window.restore() } else { window.maximize() } } return pointerInput(Unit) { forEachGesture { awaitPointerEventScope { val firstEvent = awaitPointerEvent() val firstWindowPointer = firstEvent.mouseEvent?.point ?: return@awaitPointerEventScope while (true) { val event = awaitPointerEvent() val displayPointer = event.mouseEvent?.locationOnScreen ?: break window.setLocation( (displayPointer.x - firstWindowPointer.x), (displayPointer.y - firstWindowPointer.y), ) when (event.mouseEvent?.id) { null, MouseEvent.MOUSE_RELEASED -> { if (firstEvent.mouseEvent?.clickCount == 2) { innerDoubleClickEvent() } break } } } } } } }
不具合
WIndows10 20H2でしか確認していませんが、上記のコードでは全画面ではなくフルスクリーンモードになってしまいます。
同じような問題が発見されていたのでこちらを元に修正しました。
https://stackoverflow.com/questions/7403584/does-jframe-setextendedstatemaximized-both-work-with-undecorated-frames
sun.java2d.SunGraphicsEnvironment
のコードを使用しますが、IntelliJからは解決できましたが、コンパイラが認識していなかったので面倒だったのでコードを確認した所、他に依存が無いコードだったのでコピーして使用しました。
maximizedBounds
にタスクバーを除いたサイズを教えてあげると直るようです。
@Composable fun jFrameBugFix() { val window = LocalAppWindow.current val listener = remember { fun updateMaximizedBounds() { val bounds = getUsableBounds(window.window.graphicsConfiguration.device) window.window.maximizedBounds = Rectangle(0, 0, bounds.width, bounds.height) } updateMaximizedBounds() PropertyChangeListener { updateMaximizedBounds() } } remember { object : RememberObserver { override fun onAbandoned() {} override fun onForgotten() { window.window.removePropertyChangeListener(listener) } override fun onRemembered() { window.window.addPropertyChangeListener(listener) } } } } // sun.java2d.SunGraphicsEnvironment private fun getUsableBounds(gd: GraphicsDevice): Rectangle { val gc = gd.defaultConfiguration val insets = Toolkit.getDefaultToolkit().getScreenInsets(gc) val usableBounds = gc.bounds usableBounds.x += insets.left usableBounds.y += insets.top usableBounds.width -= insets.left + insets.right usableBounds.height -= insets.top + insets.bottom return usableBounds }
さらなる問題
続いて、上記ページにもありますが、プライマリディスプレイにウィンドウが移動してしまいます。
そのため、最大化を手動でしてあげます。
if (window.isMaximized) { window.restore() } else { val device = window.window.graphicsConfiguration.device val bounds = getUsableBounds(device) val position = getUsableDisplayZeroPoint(device) window.window.setSize(bounds.width, bounds.height) window.window.setLocation( position.x, position.y, ) }
ディスプレイの上側にタスクバーがついている場合も考慮します。
private fun getUsableDisplayZeroPoint(gd: GraphicsDevice) : Point { val gc = gd.defaultConfiguration val insets = Toolkit.getDefaultToolkit().getScreenInsets(gc) val usableBounds = gc.bounds return Point(usableBounds.x + insets.left, usableBounds.y + insets.top) }
さらなる問題(諦め)
私のディスプレイは以下のようになっています。赤い部分がタスクバーです。
取得できるInsetはこのようになっています。
1: java.awt.Insets[top=0,left=0,bottom=0,right=56]
2: java.awt.Insets[top=0,left=0,bottom=0,right=67]
3: java.awt.Insets[top=0,left=0,bottom=40,right=0]
ディスプレイ1だけどこにタスクバーを置いても right=56
となってしまうのです。
おわりに
Jetpack Composeはモダンですが、結局は昔からあるレガシーな部分に乗っかっているので、大分辛いという印象が植え付けられました。これを直すには直接Win32APIを触るしか無さそうなのかなと思っています。