Skip to content

Commit f877070

Browse files
authored
Merge pull request #310 from kdroidFilter/fix-tray-app-lifecycle
Improve TrayApp window positioning and state management
2 parents df54fe5 + b5d275d commit f877070

1 file changed

Lines changed: 43 additions & 14 deletions

File tree

  • src/commonMain/kotlin/com/kdroid/composetray/tray/api

src/commonMain/kotlin/com/kdroid/composetray/tray/api/TrayApp.kt

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ fun ApplicationScope.TrayApp(
287287
// Track if window has been created at least once
288288
var windowCreated by remember { mutableStateOf(false) }
289289

290+
// Initial position for the dialog state, calculated for initial visible
291+
var initialPosition by remember { mutableStateOf(WindowPosition(-10000.dp, -10000.dp)) }
292+
290293
// System information
291294
val isDark = isMenuBarInDarkMode()
292295
val os = getOperatingSystem()
@@ -393,7 +396,9 @@ fun ApplicationScope.TrayApp(
393396
if (isVisible) {
394397
// Show window immediately
395398
shouldShowWindow = true
396-
windowCreated = true
399+
if (!windowCreated) {
400+
windowCreated = true
401+
}
397402
lastShownAt = System.currentTimeMillis()
398403
} else {
399404
// Hide window after fade animation completes
@@ -440,6 +445,15 @@ fun ApplicationScope.TrayApp(
440445
autoHideEnabledAt = System.currentTimeMillis() + 1000
441446
}
442447

448+
// Calculate initial position after delays
449+
val widthPx = windowSize.width.value.toInt()
450+
val heightPx = windowSize.height.value.toInt()
451+
initialPosition = getTrayWindowPositionForInstance(
452+
tray.instanceKey(),
453+
widthPx,
454+
heightPx
455+
) as WindowPosition.Absolute
456+
443457
shouldShowWindow = true
444458
windowCreated = true
445459
lastShownAt = System.currentTimeMillis()
@@ -468,16 +482,11 @@ fun ApplicationScope.TrayApp(
468482
// Main popup window - always mounted once created to preserve states
469483
// Uses hybrid approach: moves off-screen instead of unmounting
470484
if (windowCreated) {
471-
// Calculate position: off-screen when hidden, correct position when visible
472-
val widthPx = windowSize.width.value.toInt()
473-
val heightPx = windowSize.height.value.toInt()
474-
val windowPosition = if (shouldShowWindow) {
475-
// Visible: use correct position near tray icon
476-
getTrayWindowPositionForInstance(tray.instanceKey(), widthPx, heightPx)
477-
} else {
478-
// Hidden: move far off-screen to avoid taskbar appearance
479-
WindowPosition(-10000.dp, -10000.dp)
480-
}
485+
// Use the pre-calculated initial position
486+
val dialogState = rememberDialogState(
487+
position = initialPosition,
488+
size = windowSize
489+
)
481490

482491
DialogWindow(
483492
onCloseRequest = { requestHide() },
@@ -488,7 +497,7 @@ fun ApplicationScope.TrayApp(
488497
alwaysOnTop = shouldShowWindow, // Only on top when visible
489498
transparent = true,
490499
visible = true, // Always visible to the system
491-
state = rememberDialogState(position = windowPosition, size = windowSize)
500+
state = dialogState
492501
) {
493502
DisposableEffect(Unit) {
494503
// Set window name for monitoring
@@ -542,9 +551,26 @@ fun ApplicationScope.TrayApp(
542551
}
543552
}
544553

545-
// React to visibility changes
546-
LaunchedEffect(shouldShowWindow) {
554+
// React to visibility changes and reposition window
555+
// IMPORTANT: Position is recalculated each time window shows because:
556+
// - The tray icon might have moved (user moved taskbar, changed resolution, etc.)
557+
// - Window size might have changed
558+
LaunchedEffect(shouldShowWindow, windowSize) {
547559
if (shouldShowWindow) {
560+
// Recalculate position when showing (tray icon might have moved)
561+
// or when window size changes
562+
val widthPx = windowSize.width.value.toInt()
563+
val heightPx = windowSize.height.value.toInt()
564+
val newPosition = getTrayWindowPositionForInstance(
565+
tray.instanceKey(),
566+
widthPx,
567+
heightPx
568+
)
569+
dialogState.position = newPosition
570+
571+
// Update size if changed
572+
dialogState.size = windowSize
573+
548574
// Window is becoming visible
549575
runCatching { WindowVisibilityMonitor.recompute() }
550576
invokeLater {
@@ -557,6 +583,9 @@ fun ApplicationScope.TrayApp(
557583
}
558584
}
559585
} else {
586+
// Move window off-screen when hiding
587+
dialogState.position = WindowPosition(-10000.dp, -10000.dp)
588+
560589
// Window is becoming hidden
561590
runCatching { WindowVisibilityMonitor.recompute() }
562591
}

0 commit comments

Comments
 (0)