From 45510a8dd4a59217dcb1b8923ae99647d63dda55 Mon Sep 17 00:00:00 2001 From: Yiqun Zhang Date: Sun, 6 Jul 2025 23:10:09 +0800 Subject: [PATCH 1/2] :hammer: Refactor main window position control logic --- .../crosspaste/app/DesktopAppWindowManager.kt | 20 ----------- .../kotlin/com/crosspaste/ui/LinuxTrayView.kt | 32 ----------------- .../kotlin/com/crosspaste/ui/MacTrayView.kt | 28 +-------------- .../kotlin/com/crosspaste/ui/MainWindow.kt | 10 +++++- .../com/crosspaste/ui/WindowsTrayView.kt | 35 ++----------------- 5 files changed, 12 insertions(+), 113 deletions(-) diff --git a/app/src/desktopMain/kotlin/com/crosspaste/app/DesktopAppWindowManager.kt b/app/src/desktopMain/kotlin/com/crosspaste/app/DesktopAppWindowManager.kt index 619c32f60..bba0b44e9 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/app/DesktopAppWindowManager.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/app/DesktopAppWindowManager.kt @@ -1,8 +1,6 @@ package com.crosspaste.app import androidx.compose.ui.awt.ComposeWindow -import androidx.compose.ui.window.WindowPlacement -import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.WindowState import com.crosspaste.config.DesktopConfigManager import com.crosspaste.listener.ShortcutKeys @@ -82,16 +80,6 @@ abstract class DesktopAppWindowManager( private val _showMainWindow = MutableStateFlow(false) val showMainWindow: StateFlow = _showMainWindow - private val _mainWindowState = - MutableStateFlow( - WindowState( - placement = WindowPlacement.Floating, - position = WindowPosition.PlatformDefault, - size = appSize.mainWindowSize, - ), - ) - val mainWindowState: StateFlow = _mainWindowState - var mainComposeWindow: ComposeWindow? = null private val _showMainDialog = MutableStateFlow(false) @@ -124,14 +112,6 @@ abstract class DesktopAppWindowManager( _showSearchWindow.value = true } - fun setMainWindowState(windowState: WindowState) { - _mainWindowState.value = windowState - } - - fun getMainWindowState(): WindowState { - return mainWindowState.value - } - fun setSearchWindowState(windowState: WindowState) { _searchWindowState.value = windowState } diff --git a/app/src/desktopMain/kotlin/com/crosspaste/ui/LinuxTrayView.kt b/app/src/desktopMain/kotlin/com/crosspaste/ui/LinuxTrayView.kt index 0e643c6ed..d11df791f 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/ui/LinuxTrayView.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/ui/LinuxTrayView.kt @@ -8,9 +8,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.WindowState import com.crosspaste.app.AppLaunchState import com.crosspaste.app.DesktopAppLaunch import com.crosspaste.app.DesktopAppWindowManager @@ -22,8 +19,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging import kotlinx.coroutines.delay import org.jetbrains.compose.resources.ExperimentalResourceApi import org.koin.compose.koinInject -import java.awt.GraphicsEnvironment -import java.awt.Toolkit import java.net.URI object LinuxTrayView { @@ -83,8 +78,6 @@ object LinuxTrayView { } LaunchedEffect(Unit) { - refreshWindowPosition(appWindowManager) - if (appLaunchState.firstLaunch && !firstLaunchCompleted) { appWindowManager.showMainWindow() appLaunch.setFirstLaunchCompleted(true) @@ -92,31 +85,6 @@ object LinuxTrayView { } } - private fun refreshWindowPosition(appWindowManager: DesktopAppWindowManager) { - val gd = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice - val bounds = gd.defaultConfiguration.bounds - val insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.defaultConfiguration) - - val appSize = appWindowManager.appSize - - val windowWidth = appSize.mainWindowSize.width - - val windowPosition = - WindowPosition.Absolute( - x = (bounds.x + bounds.width).dp - windowWidth + appSize.mainHorizontalShadowPadding, - y = (bounds.y + insets.top).dp, - ) - - appWindowManager.setMainWindowState( - WindowState( - size = appWindowManager.appSize.mainWindowSize, - position = windowPosition, - ), - ) - - logger.debug { "main position: $windowPosition" } - } - private fun getTrayType(): TrayType { return System.getProperty("linux.force.trayType")?.let { safeFromString(it) diff --git a/app/src/desktopMain/kotlin/com/crosspaste/ui/MacTrayView.kt b/app/src/desktopMain/kotlin/com/crosspaste/ui/MacTrayView.kt index ff2a7e1a7..fc1b7a161 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/ui/MacTrayView.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/ui/MacTrayView.kt @@ -8,9 +8,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.WindowState import com.crosspaste.app.DesktopAppLaunch import com.crosspaste.app.DesktopAppLaunchState import com.crosspaste.app.DesktopAppWindowManager @@ -79,8 +76,6 @@ object MacTrayView { val screenDevice = ge.defaultScreenDevice (windowInfos.firstOrNull { it.contained(screenDevice) } ?: windowInfos.firstOrNull())?.let { - logger.debug { "windowInfo: $it" } - refreshWindowPosition(appWindowManager, it) if (appLaunchState.firstLaunch && !firstLaunchCompleted) { appWindowManager.showMainWindow() appLaunch.setFirstLaunchCompleted(true) @@ -94,7 +89,7 @@ object MacTrayView { state = remember { notificationManager.trayState }, tooltip = "CrossPaste", mouseListener = - MacTrayMouseClicked(appWindowManager, appLaunchState) { event, windowInfo -> + MacTrayMouseClicked(appLaunchState) { event, windowInfo -> val isCtrlDown = (event.modifiersEx and InputEvent.CTRL_DOWN_MASK) != 0 if (event.button == MouseEvent.BUTTON1 && !isCtrlDown) { mainCoroutineDispatcher.launch(CoroutineName("Switch CrossPaste")) { @@ -121,27 +116,7 @@ object MacTrayView { ) } - private fun refreshWindowPosition( - appWindowManager: DesktopAppWindowManager, - windowInfo: WindowInfo, - ) { - val appSize = appWindowManager.appSize - val windowPosition = - WindowPosition.Absolute( - x = windowInfo.x.dp + (windowInfo.width.dp / 2) - (appSize.mainWindowSize.width / 2), - y = windowInfo.y.dp + appSize.mainWindowTopMargin, - ) - appWindowManager.setMainWindowState( - WindowState( - size = appWindowManager.appSize.mainWindowSize, - position = windowPosition, - ), - ) - logger.debug { "main position: $windowPosition" } - } - class MacTrayMouseClicked( - private val appWindowManager: DesktopAppWindowManager, private val appLaunchState: DesktopAppLaunchState, private val mouseClickedAction: (MouseEvent, WindowInfo) -> Unit, ) : MouseAdapter() { @@ -151,7 +126,6 @@ object MacTrayView { windowInfos.useAll { windowInfos.first { it.contains(e.xOnScreen, e.yOnScreen) }.let { mouseClickedAction(e, it) - refreshWindowPosition(appWindowManager, it) } } } diff --git a/app/src/desktopMain/kotlin/com/crosspaste/ui/MainWindow.kt b/app/src/desktopMain/kotlin/com/crosspaste/ui/MainWindow.kt index 1e1012b77..9fbc9b2a9 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/ui/MainWindow.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/ui/MainWindow.kt @@ -5,7 +5,10 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.rememberWindowState import com.crosspaste.app.DesktopAppSize import com.crosspaste.app.DesktopAppWindowManager import com.crosspaste.listener.GlobalListener @@ -36,9 +39,14 @@ fun MainWindow(windowIcon: Painter?) { val globalListener = koinInject() val themeDetector = koinInject() - val mainWindowState by appWindowManager.mainWindowState.collectAsState() val showMainWindow by appWindowManager.showMainWindow.collectAsState() + val mainWindowState = + rememberWindowState( + size = appSize.mainWindowSize, + position = WindowPosition(Alignment.Center), + ) + // Initialize global listener only once LaunchedEffect(Unit) { globalListener.start() diff --git a/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt b/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt index 3f315e1c9..bd286715c 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt @@ -18,7 +18,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState import com.crosspaste.app.AppLaunchState import com.crosspaste.app.DesktopAppLaunch @@ -76,7 +75,6 @@ object WindowsTrayView { LaunchedEffect(Unit) { delay(1000) - refreshWindowPosition(appWindowManager, null) { _, _, _ -> } if (appLaunchState.firstLaunch && !firstLaunchCompleted) { appWindowManager.showMainWindow() appLaunch.setFirstLaunchCompleted(true) @@ -88,7 +86,7 @@ object WindowsTrayView { state = remember { notificationManager.trayState }, tooltip = "CrossPaste", mouseListener = - WindowsTrayMouseClicked(appWindowManager) { event, gd, insets -> + WindowsTrayMouseClicked { event, gd, insets -> if (event.button == MouseEvent.BUTTON1) { mainCoroutineDispatcher.launch(CoroutineName("Switch CrossPaste")) { appWindowManager.hideMainWindow() @@ -181,49 +179,20 @@ object WindowsTrayView { } fun refreshWindowPosition( - appWindowManager: DesktopAppWindowManager, event: MouseEvent?, eventAction: (MouseEvent, GraphicsDevice, Insets) -> Unit, ) { val gd = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice val insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.defaultConfiguration) event?.let { eventAction(it, gd, insets) } - - val bounds = gd.defaultConfiguration.bounds - val usableWidth = bounds.width - insets.right - val usableHeight = bounds.height - insets.bottom - - val appSize = appWindowManager.appSize - - val windowWidth = appSize.mainWindowSize.width - val windowHeight = appSize.mainWindowSize.height - - val xOffset = appSize.mainHorizontalShadowPadding - appSize.edgePadding - val yOffset = appSize.mainBottomShadowPadding - appSize.edgePadding - - val windowPosition = - WindowPosition.Absolute( - x = usableWidth.dp - windowWidth + xOffset, - y = usableHeight.dp - windowHeight + yOffset, - ) - - appWindowManager.setMainWindowState( - WindowState( - size = appWindowManager.appSize.mainWindowSize, - position = windowPosition, - ), - ) - - logger.debug { "main position: $windowPosition" } } class WindowsTrayMouseClicked( - private val appWindowManager: DesktopAppWindowManager, private val mouseClickedAction: (MouseEvent, GraphicsDevice, Insets) -> Unit, ) : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { - refreshWindowPosition(appWindowManager, e, mouseClickedAction) + refreshWindowPosition(e, mouseClickedAction) } } } From 5fca5b0c943be63db1ff91776ee1ca8d12351965 Mon Sep 17 00:00:00 2001 From: Yiqun Zhang Date: Sun, 6 Jul 2025 23:19:19 +0800 Subject: [PATCH 2/2] :hammer: Refactor main window position control logic --- .../kotlin/com/crosspaste/ui/WindowsTrayView.kt | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt b/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt index bd286715c..319b1e9d9 100644 --- a/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt +++ b/app/src/desktopMain/kotlin/com/crosspaste/ui/WindowsTrayView.kt @@ -178,21 +178,14 @@ object WindowsTrayView { } } - fun refreshWindowPosition( - event: MouseEvent?, - eventAction: (MouseEvent, GraphicsDevice, Insets) -> Unit, - ) { - val gd = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice - val insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.defaultConfiguration) - event?.let { eventAction(it, gd, insets) } - } - class WindowsTrayMouseClicked( private val mouseClickedAction: (MouseEvent, GraphicsDevice, Insets) -> Unit, ) : MouseAdapter() { - override fun mouseClicked(e: MouseEvent) { - refreshWindowPosition(e, mouseClickedAction) + override fun mouseClicked(event: MouseEvent) { + val gd = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice + val insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.defaultConfiguration) + mouseClickedAction(event, gd, insets) } } }