确定特定系统托盘图标的位置(x,y)

Vad*_*huk 5 java desktop swing kotlin jetbrains-compose

问题

我正在使用 Jetbrains Compose 开发桌面应用程序,但我也愿意接受使用 Swing(它在幕后使用)的解决方案。我想实现一个功能,其中应用程序窗口最初位于系统托盘图标上,类似于 JetBrains Toolbox 的行为方式(我想将一个(未修饰的)窗口放置在靠近托盘图标的位置)。

例子

截图示例 在此输入图像描述

我尝试使用鼠标事件来检测托盘图标上的点击并相应地定位窗口,但该方法无法实现所需的行为。它确实有效,但窗口有时会跳跃(取决于我单击的位置,并且解决方案总体看起来很奇怪)。

在此输入图像描述

我之前的解决方案

我发布我的解决方案,以防有人需要它:

internal object XTray {

    fun initialize(image: Image, onClick: (x: Int, y: Int) -> Unit) {
        createTrayIcon(image, onClick)
    }

    private fun createTrayIcon(
        image: Image,
        onClick: (x: Int, y: Int) -> Unit,
    ): TrayIcon {
        val systemTray = SystemTray.getSystemTray()

        val trayIcon = TrayIcon(image, "Open X")

        trayIcon.popupMenu = PopupMenu("X")

        trayIcon.addMouseListener(createTrayMouseListener { x, y ->
            onClick(x, y)
        })

        systemTray.add(trayIcon)

        return trayIcon
    }

    private fun createTrayMouseListener(
        onClick: (x: Int, y: Int) -> Unit,
    ): MouseAdapter {
        return object : MouseAdapter() {
            override fun mouseClicked(e: MouseEvent?) {
                e ?: return

                if (SwingUtilities.isLeftMouseButton(e)) {
                    println("${e.x} & ${e.y}")
                    onClick(calculateWindowX(e.x), calculateWindowY(e.y))
                }
            }
        }
    }

    private fun calculateWindowX(
        trayCenterX: Int,
        windowWidth: Int = 350,
        screenWidth: Int = Toolkit.getDefaultToolkit().screenSize.width,
    ): Int {
        val halfWindowWidth = windowWidth / 2

        return when {
            trayCenterX - halfWindowWidth < 0 -> 0 // Tray icon is closer to the left edge
            trayCenterX + halfWindowWidth > screenWidth -> screenWidth - windowWidth // Tray icon is closer to the right edge
            else -> trayCenterX - halfWindowWidth // Tray icon is in the middle of the screen
        }
    }

    private fun calculateWindowY(
        trayCenterY: Int,
        windowHeight: Int = 650,
        screenHeight: Int = Toolkit.getDefaultToolkit().screenSize.height,
    ): Int {
        val halfWindowHeight = windowHeight / 2

        return when {
            trayCenterY - halfWindowHeight < 0 -> 0 // Tray icon is closer to the top edge
            trayCenterY + halfWindowHeight > screenHeight -> screenHeight - windowHeight // Tray icon is closer to the bottom edge
            else -> trayCenterY - halfWindowHeight // Tray icon is in the middle of the screen
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其他应用程序(例如 JetBrains Toolbox)在启动时显示其窗口,无需通过单击图标来找到该图标。所以,应该有另一个解决方案。

我怀疑我需要使用系统 API 来完成此任务,但我想知道是否有现成的解决方案或库可以提供此功能。这似乎是一个常见的功能,我不想重新发明轮子。

我想看看例子,但我找不到它们。

小智 0

Java 中的系统托盘 API 允许您与托盘图标进行交互(添加、删除、更新其图像或工具提示等),但它不提供查询其在屏幕上的位置的方法。与托盘图标关联的鼠标事件提供相对于图标本身的坐标,而不是相对于屏幕的坐标。

在 Windows 上,您需要与管理系统托盘区域及其图标的 Shell_TrayWnd 和 TrayNotifyWnd Windows 控件进行交互。这需要调用 Windows API 函数并处理 Windows 消息,这不能直接在 Java 中完成,但需要使用 JNI 或第三方库,如 JNA (Java Native Access) 或 JNR (Java Native Runtime)。