Ver Fonte

code improvements & transparency (mutter)

milan jurkulák há 2 anos atrás
pai
commit
a78ba994ff

BIN
resources/linux/usr/share/444782450_373988189002367_3189848826327767968_n.jpg


BIN
resources/linux/usr/share/unnamed.jpg


+ 3 - 2
resources/linux/usr/share/wayland-sessions/mjdev-desktop.desktop

@@ -1,6 +1,7 @@
 [Desktop Entry]
-Name=Mjdev Desktop
+Name=Mjdev Desktop - Wayland
 Comment=This session logs you into Mjdev Desktop
-Exec=gnome-session --session mjdev-desktop --debug
+Exec=dbus-run-session mutter --wayland mjdev-desktop
 TryExec=gnome-session
 Type=Application
+

+ 2 - 2
resources/linux/usr/share/xsessions/mjdev-desktop.desktop

@@ -1,6 +1,6 @@
 [Desktop Entry]
-Name=Mjdev Desktop
+Name=Mjdev Desktop - X11
 Comment=This session logs you into Mjdev Desktop
-Exec=gnome-session --session mjdev-desktop --debug
+Exec=dbus-run-session mutter mjdev-desktop
 TryExec=gnome-session
 Type=Application

+ 2 - 1
src/main/kotlin/eu/mjdev/dadb/helpers/Logging.kt

@@ -1,6 +1,7 @@
 package eu.mjdev.dadb.helpers
 
-private val ENABLED = "true" == System.getenv("DADB_LOGGING")
+// todo
+private val ENABLED = true
 
 internal fun log(block: () -> String) {
     if (ENABLED) {

+ 8 - 6
src/main/kotlin/eu/mjdev/desktop/Main.kt

@@ -22,10 +22,14 @@ fun main() = application(
         LocalDesktop provides DesktopProvider(scope)
     ) {
         MaterialTheme {
-            val api: DesktopProvider = LocalDesktop.current
             val controlCenterState = rememberVisibilityState()
-            val panelState = rememberVisibilityState(true, api.currentUser.config.autoHidePanel)
+            val panelState = rememberVisibilityState()
             val menuState = rememberVisibilityState()
+//            scope.launch {
+//                api.askGemini("How are you today?").let {
+//                    log { "Gemini answer: $it" }
+//                }
+//            }
             MainWindow(
                 panelState = panelState,
                 controlCenterState = controlCenterState,
@@ -46,11 +50,9 @@ fun main() = application(
                 }
             )
             AppsMenu(
+                // todo
                 bottomY = 64.dp,
-                menuState = menuState,
-                onVisibilityChange = { visible ->
-                    log { "Menu visible : $visible" }
-                }
+                menuState = menuState
             )
             ControlCenter(
                 controlCenterState = controlCenterState

+ 83 - 101
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenu.kt

@@ -17,143 +17,125 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-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 eu.mjdev.desktop.components.custom.UserAvatar
 import eu.mjdev.desktop.components.slidemenu.VisibilityState
 import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
 import eu.mjdev.desktop.extensions.Compose.launchedEffect
-import eu.mjdev.desktop.extensions.Compose.setWindowBounds
-import eu.mjdev.desktop.helpers.WindowFocusState.Companion.windowFocusHandler
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import eu.mjdev.desktop.provider.data.App
 import eu.mjdev.desktop.provider.data.Category
 import eu.mjdev.desktop.windows.TopWindow
+import eu.mjdev.desktop.windows.TopWindowState
+import eu.mjdev.desktop.windows.TopWindowState.Companion.rememberTopWindowState
+import eu.mjdev.desktop.windows.WindowBounds
 
 @Composable
 fun AppsMenu(
     modifier: Modifier = Modifier,
     api: DesktopProvider = LocalDesktop.current,
-    appMenuExpandedWidth: Dp = 480.dp, // todo
+    appMenuMinWidth: Dp = 480.dp, // todo
     appMenuMinHeight: Dp = 640.dp, // todo
     bottomY: Dp = 64.dp, // todo panel height
     backgroundColor: Color = api.currentUser.theme.backgroundColor,
     menuPadding: PaddingValues = PaddingValues(2.dp), // todo
     menuState: VisibilityState = rememberVisibilityState(),
     items: MutableState<List<Any>> = remember { mutableStateOf(api.appsProvider.appCategories) },
-    onVisibilityChange: (visible: Boolean) -> Unit = {},
-) {
-    val windowState: WindowState = rememberWindowState(
-        placement = WindowPlacement.Floating,
-        position = WindowPosition.Aligned(api.currentUser.theme.panelLocation.alignment),
-        size = api.containerSize.copy(
-            width = if (menuState.isVisible) appMenuMinHeight else 0.dp
-        ),
-        isMinimized = false
+    enter: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it }),
+    exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
+    windowState: TopWindowState = rememberTopWindowState(
+        position = api.currentUser.theme.panelLocation.alignment,
+        size = DpSize(appMenuMinWidth, appMenuMinHeight),
+        exit = exit,
+        enter = enter,
+        visible = menuState.isVisible,
+        computeBounds = { isVisible ->
+            val containerHeight = api.containerSize.height
+            WindowBounds(
+                0.dp,
+                if (isVisible) (containerHeight - appMenuMinHeight) else containerHeight,
+                appMenuMinWidth,
+                if (isVisible) appMenuMinHeight else 0.dp
+            )
+        }
     )
-    TopWindow(
-        windowState = windowState,
+) = TopWindow(
+    windowState = windowState,
+    onFocusChange = { hasFocus -> if (!hasFocus) menuState.hide() }
+) {
+    Box(
+        modifier = modifier
+            .padding(bottom = bottomY)
+            .width(appMenuMinWidth)
+            .heightIn(
+                min = appMenuMinHeight,
+                max = appMenuMinHeight
+            )
     ) {
-        windowFocusHandler { hasFocus ->
-            if (!hasFocus) {
-                menuState.hide()
-            }
-        }
-        AnimatedVisibility(
-            menuState.isVisible,
-            modifier = modifier,
-            enter = fadeIn() + slideInVertically(initialOffsetY = { it }),
-            exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
+        Box(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(menuPadding)
+                .background(backgroundColor.copy(alpha = 0.7f), RoundedCornerShape(24.dp))
+                .clip(RoundedCornerShape(24.dp))
+                .border(2.dp, backgroundColor, RoundedCornerShape(24.dp))
         ) {
-            Box(
-                modifier = modifier
-                    .padding(bottom = bottomY)
-                    .width(appMenuExpandedWidth)
-                    .heightIn(
-                        min = appMenuMinHeight,
-                        max = appMenuMinHeight
-                    )
+            Column(
+                modifier = Modifier.fillMaxSize()
             ) {
-                Box(
+                UserAvatar(
+                    avatarSize = 64.dp,
+                    backgroundColor = backgroundColor,
+                    orientation = Orientation.Horizontal
+                )
+                LazyColumn(
                     modifier = Modifier
+                        .padding(bottom = 48.dp)
                         .fillMaxSize()
-                        .padding(menuPadding)
-                        .background(backgroundColor.copy(alpha = 0.7f), RoundedCornerShape(24.dp))
-                        .clip(RoundedCornerShape(24.dp))
-                        .border(2.dp, backgroundColor, RoundedCornerShape(24.dp))
+                        .padding(24.dp)
                 ) {
-                    Column(
-                        modifier = Modifier.fillMaxSize()
-                    ) {
-                        UserAvatar(
-                            avatarSize = 64.dp,
-                            backgroundColor = backgroundColor,
-                            orientation = Orientation.Horizontal
-                        )
-                        LazyColumn(
-                            modifier = Modifier
-                                .padding(bottom = 48.dp)
-                                .fillMaxSize()
-                                .padding(24.dp)
-                        ) {
-                            when (items.value.firstOrNull()) {
-                                is Category -> {
-                                    items(items.value) { item ->
-                                        AppsMenuCategory(
-                                            category = item as Category,
-                                            backgroundColor = backgroundColor,
-                                            iconTint = Color.White
-                                        ) { category ->
-                                            items.value = api.appsProvider.categoriesAndApps[category.name]
-                                                ?: emptyList()
-                                        }
-                                    }
+                    when (items.value.firstOrNull()) {
+                        is Category -> {
+                            items(items.value) { item ->
+                                AppsMenuCategory(
+                                    category = item as Category,
+                                    backgroundColor = backgroundColor,
+                                    iconTint = Color.White
+                                ) { category ->
+                                    items.value = api.appsProvider.categoriesAndApps[category.name]
+                                        ?: emptyList()
                                 }
+                            }
+                        }
 
-                                is App -> {
-                                    items(items.value) { item ->
-                                        AppsMenuApp(
-                                            app = item as App,
-                                            backgroundColor = backgroundColor,
-                                            iconTint = Color.White
-                                        ) { app ->
-                                            app?.start()
-                                            menuState.hide()
-                                        }
-                                    }
+                        is App -> {
+                            items(items.value) { item ->
+                                AppsMenuApp(
+                                    app = item as App,
+                                    backgroundColor = backgroundColor,
+                                    iconTint = Color.White
+                                ) { app ->
+                                    app?.start()
+                                    menuState.hide()
                                 }
                             }
                         }
                     }
-                    AppsBottomBar(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .wrapContentHeight()
-                            .align(Alignment.BottomStart)
-                            .background(backgroundColor)
-                    )
                 }
             }
-            launchedEffect(menuState.isVisible) { isVisible ->
-                val x = 0.dp
-                val y = when (isVisible) {
-                    true -> (api.containerSize.height - appMenuMinHeight)
-                    else -> api.containerSize.height
-                }
-                val width = appMenuExpandedWidth
-                val height = when (isVisible) {
-                    true -> appMenuMinHeight
-                    else -> 0.dp
-                }
-                window.setWindowBounds(x, y, width, height)
-                if (!isVisible) {
-                    items.value = api.appsProvider.appCategories
-                }
-                onVisibilityChange(isVisible)
+            AppsBottomBar(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .wrapContentHeight()
+                    .align(Alignment.BottomStart)
+                    .background(backgroundColor)
+            )
+        }
+        launchedEffect(menuState.isVisible) { isVisible ->
+            if (!isVisible) {
+                items.value = api.appsProvider.appCategories
             }
         }
     }

+ 104 - 116
src/main/kotlin/eu/mjdev/desktop/components/controlcenter/ControlCenter.kt

@@ -14,7 +14,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Divider
 import androidx.compose.material.Icon
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -22,21 +21,18 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-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 eu.mjdev.desktop.components.shadow.LeftShadow
 import eu.mjdev.desktop.components.shadow.RightShadow
 import eu.mjdev.desktop.components.slidemenu.SlidingMenu
 import eu.mjdev.desktop.components.slidemenu.VisibilityState
 import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
 import eu.mjdev.desktop.extensions.Compose.launchedEffect
-import eu.mjdev.desktop.extensions.Compose.setWindowBounds
-import eu.mjdev.desktop.helpers.WindowFocusState.Companion.windowFocusHandler
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import eu.mjdev.desktop.windows.TopWindow
+import eu.mjdev.desktop.windows.TopWindowState
+import eu.mjdev.desktop.windows.TopWindowState.Companion.rememberTopWindowState
+import eu.mjdev.desktop.windows.WindowBounds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
@@ -57,102 +53,111 @@ fun ControlCenter(
     pagerState: PagerState = rememberPagerState(pageCount = { pages.size }, initialPage = 0),
     controlCenterState: VisibilityState = rememberVisibilityState(),
     scope: CoroutineScope = rememberCoroutineScope(),
+    enter: EnterTransition = fadeIn() + slideInHorizontally(initialOffsetX = { it }),
+    exit: ExitTransition = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
+    windowState: TopWindowState = rememberTopWindowState(
+        position = api.currentUser.theme.controlCenterLocation.alignment,
+        size = api.containerSize.copy(width = controlCenterDividerWidth),
+        enter = enter,
+        exit = exit,
+        minSize = DpSize(controlCenterDividerWidth, api.containerSize.height),
+        computeBounds = { isVisible ->
+            val containerHeight = api.containerSize.height
+            val containerWidth = api.containerSize.width
+            when {
+                isVisible -> WindowBounds(
+                    containerWidth - controlCenterExpandedWidth,
+                    0.dp,
+                    controlCenterExpandedWidth,
+                    containerHeight
+                )
+
+                else -> WindowBounds(
+                    containerWidth - controlCenterDividerWidth,
+                    0.dp,
+                    controlCenterDividerWidth,
+                    containerHeight
+                )
+            }
+        }
+    ),
     onVisibilityChange: (visible: Boolean) -> Unit = {},
+) = TopWindow(
+    windowState = windowState,
+    onFocusChange = { hasFocus -> if (!hasFocus) controlCenterState.hide() }
 ) {
-    val windowState: WindowState = rememberWindowState(
-        placement = WindowPlacement.Floating,
-        position = WindowPosition.Aligned(api.currentUser.theme.controlCenterLocation.alignment),
-        size = api.containerSize.copy(width = controlCenterDividerWidth),
-        isMinimized = false
-    )
-    TopWindow(
-        windowState = windowState,
-    ) {
-        windowFocusHandler { hasFocus -> if (!hasFocus) controlCenterState.hide() }
-        SlidingMenu(
-            modifier = modifier,
-            orientation = Orientation.Horizontal,
-            state = controlCenterState,
-            onVisibilityChange = onVisibilityChange
-        ) { isVisible ->
-            if (!isVisible) {
+    SlidingMenu(
+        modifier = modifier,
+        orientation = Orientation.Horizontal,
+        state = controlCenterState,
+        onVisibilityChange = onVisibilityChange
+    ) { isVisible ->
+        if (!isVisible) {
+            Divider(
+                modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
+                color = Color.Transparent,
+                thickness = controlCenterDividerWidth
+            )
+        }
+        LeftShadow(
+            modifier = Modifier.fillMaxHeight().wrapContentSize(),
+            color = shadowColor,
+            contentBackgroundColor = backgroundColor.copy(alpha = backgroundAlpha)
+        ) {
+            Row(
+                modifier = Modifier.fillMaxHeight().wrapContentSize(),
+            ) {
                 Divider(
                     modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
-                    color = Color.Transparent,
+                    color = controlCenterDividerColor,
                     thickness = controlCenterDividerWidth
                 )
-            }
-            AnimatedVisibility(
-                isVisible,
-                enter = fadeIn() + slideInHorizontally(initialOffsetX = { it }),
-                exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
-            ) {
-                LeftShadow(
-                    modifier = Modifier.fillMaxHeight().wrapContentSize(),
-                    color = shadowColor,
-                    contentBackgroundColor = backgroundColor.copy(alpha = backgroundAlpha)
+                Box(
+                    contentAlignment = Alignment.TopEnd
                 ) {
-                    Row(
-                        modifier = Modifier.fillMaxHeight().wrapContentSize(),
+                    LazyColumn(
+                        modifier = Modifier
+                            .fillMaxHeight()
+                            .background(backgroundColor)
+                            .padding(8.dp),
+                        verticalArrangement = Arrangement.Top,
+                        horizontalAlignment = Alignment.Start
                     ) {
-                        Divider(
-                            modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
-                            color = controlCenterDividerColor,
-                            thickness = controlCenterDividerWidth
-                        )
-                        Box(
-                            contentAlignment = Alignment.TopEnd
-                        ) {
-                            LazyColumn(
+                        itemsIndexed(pages) { idx, page ->
+                            val isSelected = (pagerState.currentPage == idx)
+                            Icon(
                                 modifier = Modifier
-                                    .fillMaxHeight()
-                                    .background(backgroundColor)
-                                    .padding(8.dp),
-                                verticalArrangement = Arrangement.Top,
-                                horizontalAlignment = Alignment.Start
-                            ) {
-                                itemsIndexed(pages) { idx, page ->
-                                    val isSelected = (pagerState.currentPage == idx)
-                                    Icon(
-                                        modifier = Modifier
-                                            .padding(vertical = 8.dp)
-                                            .size(controlCenterIconSize)
-                                            .background(
-                                                color = if (isSelected) Color.White.copy(alpha = 0.4f) else Color.Transparent,
-                                                shape = RoundedCornerShape(8.dp)
-                                            )
-                                            .clickable {
-                                                scope.launch {
-                                                    pagerState.scrollToPage(idx)
-                                                }
-                                            },
-                                        imageVector = page.icon,
-                                        contentDescription = "",
-                                        tint = if (isSelected) backgroundColor else controlCenterIconColor
-                                    )
-                                }
-                            }
-                            RightShadow(
-                                modifier = Modifier.padding(
-                                    end = controlCenterIconSize.width + 8.dp
-                                ).fillMaxHeight().wrapContentSize(),
-                                color = Color.Black,
-                                alpha = 0.3f,
-                                contentBackgroundColor = Color.Transparent
-                            ) {
-                                Row {
-                                    HorizontalPager(
-                                        modifier = Modifier.width(controlCenterExpandedWidth),
-                                        state = pagerState
-                                    ) { page ->
-                                        pages[page].content(backgroundColor)
-                                    }
-                                    Divider(
-                                        modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
-                                        color = controlCenterDividerColor,
-                                        thickness = controlCenterDividerWidth
+                                    .padding(vertical = 8.dp)
+                                    .size(controlCenterIconSize)
+                                    .background(
+                                        color = if (isSelected) Color.White.copy(alpha = 0.4f) else Color.Transparent,
+                                        shape = RoundedCornerShape(8.dp)
                                     )
-                                }
+                                    .clickable {
+                                        scope.launch {
+                                            pagerState.scrollToPage(idx)
+                                        }
+                                    },
+                                imageVector = page.icon,
+                                contentDescription = "",
+                                tint = if (isSelected) backgroundColor else controlCenterIconColor
+                            )
+                        }
+                    }
+                    RightShadow(
+                        modifier = Modifier.padding(
+                            end = controlCenterIconSize.width + 8.dp
+                        ).fillMaxHeight().wrapContentSize(),
+                        color = Color.Black,
+                        alpha = 0.3f,
+                        contentBackgroundColor = Color.Transparent
+                    ) {
+                        Row {
+                            HorizontalPager(
+                                modifier = Modifier.width(controlCenterExpandedWidth),
+                                state = pagerState
+                            ) { page ->
+                                pages[page].content(backgroundColor)
                             }
                             Divider(
                                 modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
@@ -161,31 +166,14 @@ fun ControlCenter(
                             )
                         }
                     }
-                }
-            }
-        }
-        launchedEffect(controlCenterState.isVisible) { isVisible ->
-            val x = when (isVisible) {
-                true -> (api.containerSize.width - controlCenterExpandedWidth)
-                else -> (api.containerSize.width - controlCenterDividerWidth)
-            }
-            val y = 0.dp
-            val width = when (isVisible) {
-                true -> controlCenterExpandedWidth
-                else -> controlCenterDividerWidth
-            }
-            val height = api.containerSize.height
-            window.setWindowBounds(x, y, width, height)
-        }
-        DisposableEffect(Unit) {
-            scope.launch {
-                pagerState.scrollToPage(0)
-            }
-            onDispose {
-                scope.launch {
-                    pagerState.scrollToPage(0)
+                    Divider(
+                        modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
+                        color = controlCenterDividerColor,
+                        thickness = controlCenterDividerWidth
+                    )
                 }
             }
         }
     }
+    launchedEffect(controlCenterState.isVisible) { isVisible -> windowState.isVisible = isVisible }
 }

+ 126 - 134
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanel.kt

@@ -19,22 +19,18 @@ import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-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 eu.mjdev.desktop.components.shadow.TopShadow
 import eu.mjdev.desktop.components.slidemenu.SlidingMenu
 import eu.mjdev.desktop.components.slidemenu.VisibilityState
 import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
 import eu.mjdev.desktop.extensions.Compose.height
-import eu.mjdev.desktop.extensions.Compose.launchedEffect
-import eu.mjdev.desktop.extensions.Compose.setWindowBounds
-import eu.mjdev.desktop.helpers.WindowFocusState.Companion.windowFocusHandler
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import eu.mjdev.desktop.provider.data.App
 import eu.mjdev.desktop.windows.TopWindow
+import eu.mjdev.desktop.windows.TopWindowState
+import eu.mjdev.desktop.windows.TopWindowState.Companion.rememberTopWindowState
+import eu.mjdev.desktop.windows.WindowBounds
 
 @OptIn(ExperimentalFoundationApi::class)
 @Suppress("FunctionName")
@@ -51,145 +47,141 @@ fun DesktopPanel(
     iconPadding: PaddingValues = PaddingValues(4.dp),
     iconOuterPadding: PaddingValues = PaddingValues(2.dp),
     panelAlpha: Float = 0.7f,
+    panelHeight: Dp = iconSize.height + iconPadding.height + iconOuterPadding.height,
     showMenuIcon: Boolean = true,
     panelState: VisibilityState = rememberVisibilityState(),
+    enter: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it }),
+    exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
+    windowState: TopWindowState = rememberTopWindowState(
+        position = api.currentUser.theme.panelLocation.alignment,
+        size = api.containerSize.copy(height = panelHeight),
+        exit = exit,
+        enter = enter,
+        computeBounds = { isVisible ->
+            val containerHeight = api.containerSize.height
+            val containerWidth = api.containerSize.width
+            WindowBounds(
+                0.dp,
+                if (isVisible) (containerHeight - panelHeight) else (containerHeight - dividerWidth),
+                containerWidth,
+                if (isVisible) panelHeight else dividerWidth
+            )
+        }
+    ),
+    favoriteApps: State<List<App>> = api.appsProvider.favoriteApps.collectAsState(emptyList()),
+    tooltipData: MutableState<Any?> = mutableStateOf(null),
+    onToolTip: (item: Any?) -> Unit = { item -> tooltipData.value = item },
     onVisibilityChange: (visible: Boolean) -> Unit = {},
     onMenuIconClicked: () -> Unit,
+) = TopWindow(
+    windowState = windowState,
+    onFocusChange = { hasFocus -> if (!hasFocus) panelState.hide() }
 ) {
-    val panelSize = api.containerSize.copy(height = iconSize.height + iconPadding.height + iconOuterPadding.height)
-    val windowState: WindowState = rememberWindowState(
-        placement = WindowPlacement.Floating,
-        position = WindowPosition.Aligned(api.currentUser.theme.panelLocation.alignment),
-        size = api.containerSize.copy(
-            width = if (panelState.isVisible) panelSize.height else dividerWidth
-        ),
-        isMinimized = false
-    )
-    TopWindow(
-        windowState = windowState,
-    ) {
-        windowFocusHandler { hasFocus -> if (!hasFocus) panelState.hide() }
-        SlidingMenu(
-            modifier = modifier,
-            orientation = Orientation.Vertical,
-            state = panelState,
-            onVisibilityChange = onVisibilityChange
-        ) { isVisible ->
-            val favoriteApps: State<List<App>> = api.appsProvider.favoriteApps.collectAsState(emptyList())
-            val tooltipData: MutableState<Any?> = mutableStateOf(null)
-            val onToolTip: (item: Any?) -> Unit = { item -> tooltipData.value = item }
-            if (!isVisible) {
-                Divider(
-                    modifier = Modifier
-                        .fillMaxWidth()
-                        .height(dividerWidth),
-                    color = Color.Transparent,
-                    thickness = dividerWidth
-                )
-            }
-            AnimatedVisibility(
-                isVisible,
-                enter = fadeIn() + slideInVertically(initialOffsetY = { it }),
-                exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
+    SlidingMenu(
+        modifier = modifier,
+        orientation = Orientation.Vertical,
+        state = panelState,
+        onVisibilityChange = onVisibilityChange
+    ) { isVisible ->
+        if (!isVisible) {
+            Divider(
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(dividerWidth),
+                color = Color.Transparent,
+                thickness = dividerWidth
+            )
+        }
+        TooltipArea(
+            tooltip = {
+                // todo more types
+                when (tooltipData.value) {
+                    is App -> DesktopPanelAppTooltip(tooltipData.value as App)
+                    else -> {}
+                }
+            },
+            modifier = Modifier.fillMaxSize(),
+            tooltipPlacement = TooltipPlacement.CursorPoint(
+                alignment = Alignment.TopEnd,
+                offset = DpOffset(16.dp, 32.dp)
+            )
+        ) {
+            TopShadow(
+                modifier = Modifier.fillMaxWidth(),
+                color = shadowColor,
+                contentBackgroundColor = backgroundColor.copy(alpha = panelAlpha)
             ) {
-                TooltipArea(
-                    tooltip = {
-                        // todo more types
-                        when (tooltipData.value) {
-                            is App -> DesktopPanelAppTooltip(tooltipData.value as App)
-                            else -> {}
-                        }
-                    },
-                    modifier = Modifier.fillMaxSize(),
-                    tooltipPlacement = TooltipPlacement.CursorPoint(
-                        alignment = Alignment.TopEnd,
-                        offset = DpOffset(16.dp, 32.dp)
-                    )
+                LazyRow(
+                    modifier = Modifier.fillMaxSize().background(
+                        brush = Brush.verticalGradient(
+                            colors = listOf(
+                                backgroundColor.copy(alpha = 0.3f),
+                                backgroundColor.copy(alpha = 0.7f),
+                                backgroundColor.copy(alpha = 0.3f),
+                            )
+                        )
+                    ),
+                    horizontalArrangement = Arrangement.Start,
+                    verticalAlignment = Alignment.CenterVertically
                 ) {
-                    TopShadow(
-                        modifier = Modifier.fillMaxWidth(),
-                        color = shadowColor,
-                        contentBackgroundColor = backgroundColor.copy(alpha = panelAlpha)
-                    ) {
-                        LazyRow(
-                            modifier = Modifier.fillMaxSize().background(
-                                brush = Brush.verticalGradient(
-                                    colors = listOf(
-                                        backgroundColor.copy(alpha = 0.3f),
-                                        backgroundColor.copy(alpha = 0.7f),
-                                        backgroundColor.copy(alpha = 0.3f),
-                                    )
-                                )
-                            ),
-                            horizontalArrangement = Arrangement.Start,
-                            verticalAlignment = Alignment.CenterVertically
-                        ) {
-                            if (showMenuIcon) {
-                                item {
-                                    DesktopPanelIcon(
-                                        icon = "menu",
-                                        iconColor = iconColor,
-                                        iconBackgroundColor = iconBackgroundColor,
-                                        iconBackgroundHover = Color.White.copy(alpha = 0.3f),
-                                        iconSize = iconSize,
-                                        iconPadding = iconPadding,
-                                        iconOuterPadding = iconOuterPadding,
-                                        onToolTip = onToolTip,
-                                        onClick = {
-                                            onMenuIconClicked()
-                                        }
-                                    )
-                                    Divider(
-                                        modifier = Modifier
-                                            .padding(2.dp)
-                                            .height(iconSize.height - 8.dp)
-                                            .width(2.dp)
-                                            .background(Color.White.copy(alpha = 0.4f))
-                                    )
+                    if (showMenuIcon) {
+                        item {
+                            DesktopPanelIcon(
+                                icon = "menu",
+                                iconColor = iconColor,
+                                iconBackgroundColor = iconBackgroundColor,
+                                iconBackgroundHover = Color.White.copy(alpha = 0.3f),
+                                iconSize = iconSize,
+                                iconPadding = iconPadding,
+                                iconOuterPadding = iconOuterPadding,
+                                onToolTip = onToolTip,
+                                onClick = {
+                                    onMenuIconClicked()
                                 }
+                            )
+                            Divider(
+                                modifier = Modifier
+                                    .padding(2.dp)
+                                    .height(iconSize.height - 8.dp)
+                                    .width(2.dp)
+                                    .background(Color.White.copy(alpha = 0.4f))
+                            )
+                        }
+                    }
+                    items(favoriteApps.value) { app ->
+                        DesktopPanelIcon(
+                            app = app,
+                            iconColor = iconColor,
+                            iconBackgroundColor = iconBackgroundColor,
+                            iconBackgroundHover = Color.White.copy(alpha = 0.3f),
+                            iconSize = iconSize,
+                            iconPadding = iconPadding,
+                            iconOuterPadding = iconOuterPadding,
+                            onToolTip = onToolTip,
+                            onClick = {
+                                app.start()
                             }
-                            items(favoriteApps.value) { app ->
-                                DesktopPanelIcon(
-                                    app = app,
-                                    iconColor = iconColor,
-                                    iconBackgroundColor = iconBackgroundColor,
-                                    iconBackgroundHover = Color.White.copy(alpha = 0.3f),
-                                    iconSize = iconSize,
-                                    iconPadding = iconPadding,
-                                    iconOuterPadding = iconOuterPadding,
-                                    onToolTip = onToolTip,
-                                    onClick = {
-                                        app.start()
-                                    }
-                                )
-                            }
-                            item {
-                                DesktopPanelText(
-                                    text = api.appsProvider.currentLocale.country,
-                                    backgroundHover = Color.White.copy(alpha = 0.3f),
-                                    onToolTip = onToolTip,
-                                    onClick = {
-                                        // todo
-                                    }
-                                )
+                        )
+                    }
+                    item {
+                        DesktopPanelText(
+                            text = api.appsProvider.currentLocale.country,
+                            backgroundHover = Color.White.copy(alpha = 0.3f),
+                            onToolTip = onToolTip,
+                            onClick = {
+                                // todo
                             }
-                        }
+                        )
                     }
                 }
             }
         }
-        launchedEffect(panelState.isVisible) { isVisible ->
-            val x = 0.dp
-            val y = when (isVisible) {
-                true -> (api.containerSize.height - panelSize.height)
-                else -> (api.containerSize.height - dividerWidth)
-            }
-            val width = api.containerSize.width
-            val height = when (isVisible) {
-                true -> panelSize.height
-                else -> dividerWidth
-            }
-            window.setWindowBounds(x, y, width, height)
-        }
     }
-}
+    LaunchedEffect(
+        panelState.isVisible,
+        api.currentUser.theme.panelHideDelay
+    ) {
+        panelState.enabled = api.currentUser.theme.panelHideDelay > 0
+        windowState.isVisible = api.currentUser.theme.panelHideDelay < 1 || panelState.isVisible
+    }
+}

+ 6 - 1
src/main/kotlin/eu/mjdev/desktop/components/menu/ContextMenu.kt

@@ -1,6 +1,7 @@
 package eu.mjdev.desktop.components.menu
 
 import androidx.compose.foundation.background
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.CursorDropdownMenu
 import androidx.compose.material.DropdownMenuItem
 import androidx.compose.material.Text
@@ -10,13 +11,17 @@ import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
 
+// todo
 @Composable
 fun ContextMenu(
     backgroundColor: Color = Color.SuperDarkGray,
     textColor: Color = Color.White,
     contextMenuState: ContextMenuState,
+    onShow: () -> Unit = {},
+    onHide: () -> Unit = {},
     onMenuItemClick: (item: String) -> Unit = {}
 ) = listOf(
     contextMenuState.menuRenderCount,
@@ -25,7 +30,7 @@ fun ContextMenu(
     val isActive = renderId == contextMenuState.menuRenderCount
     key(renderId) {
         CursorDropdownMenu(
-            modifier = Modifier.background(backgroundColor),
+            modifier = Modifier.background(color = backgroundColor, shape = RoundedCornerShape(16.dp)),
             expanded = contextMenuState.menuExpanded.value && isActive,
             onDismissRequest = {
                 if (isActive) {

+ 2 - 5
src/main/kotlin/eu/mjdev/desktop/components/slidemenu/SlidingMenu.kt

@@ -10,6 +10,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.onPointerEvent
+import eu.mjdev.dadb.helpers.log
 import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
 import eu.mjdev.desktop.extensions.Compose.launchedEffect
 
@@ -30,6 +31,7 @@ fun SlidingMenu(
                 .fillMaxHeight()
                 .wrapContentWidth()
                 .onPointerEvent(PointerEventType.Enter) {
+                    log { "Showing sliding menu." }
                     state.show()
                 }
         ) {
@@ -50,9 +52,4 @@ fun SlidingMenu(
     launchedEffect(state.isVisible) {
         onVisibilityChange(state.isVisible)
     }
-//    launchedEffect(windowFocusState.isFocused) {
-//        if (!windowFocusState.isFocused) {
-//            state.hide()
-//        }
-//    }
 }

+ 1 - 1
src/main/kotlin/eu/mjdev/desktop/components/slidemenu/VisibilityState.kt

@@ -8,7 +8,7 @@ import kotlinx.coroutines.launch
 class VisibilityState(
     private val scope: CoroutineScope,
     private val startState: Boolean = false,
-    private val enabled: Boolean = true
+    var enabled: Boolean = true
 ) {
     var isVisible: Boolean
         get() = visible.value

+ 30 - 2
src/main/kotlin/eu/mjdev/desktop/provider/DesktopProvider.kt

@@ -5,22 +5,28 @@ import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
+import dev.shreyaspatil.ai.client.generativeai.GenerativeModel
+import dev.shreyaspatil.ai.client.generativeai.type.content
 import eu.mjdev.dadb.AdbDiscover.Companion.adbDevicesHandler
+import eu.mjdev.dadb.helpers.log
 import eu.mjdev.desktop.components.controlcenter.ControlCenterPage
 import eu.mjdev.desktop.components.controlcenter.pages.*
 import eu.mjdev.desktop.provider.data.User
 import eu.mjdev.desktop.provider.providers.AppsProvider
 import kotlinx.coroutines.CoroutineScope
+import java.awt.GraphicsDevice
+import java.awt.GraphicsEnvironment
 import java.awt.Toolkit
 import java.io.File
 import java.io.FileReader
 import javax.script.ScriptEngine
 import javax.script.ScriptEngineManager
 
-@Suppress("unused")
+@Suppress("unused", "MemberVisibilityCanBePrivate", "CanBeParameter")
 class DesktopProvider(
     private val scope: CoroutineScope? = null,
-    val currentUser: User = User.load(scope), // todo
+    val currentUser: User = User.load(),
+    val geminiApiKey: String = "AIzaSyAGhYvYsTc_7nf4bv7HAQH6q8Pb7mbCKoQ", // todo
 ) {
     private val _currentUser: MutableState<User> = mutableStateOf(currentUser)
 
@@ -60,10 +66,32 @@ class DesktopProvider(
     val controlCenterPages get() = _controlCenterPages.value
     val appsProvider by lazy { AppsProvider(this) }
 
+    val graphicsEnvironment: GraphicsEnvironment
+        get() = GraphicsEnvironment.getLocalGraphicsEnvironment()
+    val graphicsDevice: GraphicsDevice
+        get() = graphicsEnvironment.defaultScreenDevice
+    val isTransparencySupported
+        get() = graphicsDevice.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT)
+
+    val generativeModel: GenerativeModel by lazy {
+        GenerativeModel(
+            modelName = "gemini-1.5-pro-latest",
+            apiKey = geminiApiKey
+        )
+    }
+
     init {
         currentUser.theme.controlCenterExpandedWidth = containerSize.width.div(4)
     }
 
+    suspend fun askGemini(question: String) = runCatching {
+        generativeModel.generateContent(content {
+            text(question)
+        }).text
+    }.onFailure { e ->
+        log { e.message ?: "" }
+    }.getOrNull()
+
     fun runScript(script: String): Any =
         engine.eval(script)
 

+ 2 - 7
src/main/kotlin/eu/mjdev/desktop/provider/data/DesktopConfig.kt

@@ -4,13 +4,9 @@ import androidx.compose.runtime.mutableStateOf
 
 @Suppress("MemberVisibilityCanBePrivate")
 class DesktopConfig(
-    autoHidePanel: Boolean = true,
     desktopBackgroundUrls: List<Any> = emptyList()
 ) {
-    val autoHidePanelState = mutableStateOf(autoHidePanel)
     val desktopBackgroundUrlsState = mutableStateOf(desktopBackgroundUrls.toMutableList())
-
-    val autoHidePanel get() = autoHidePanelState.value
     val desktopBackgroundUrls get() = desktopBackgroundUrlsState.value
 
     fun addBackground(path: String) {
@@ -18,9 +14,8 @@ class DesktopConfig(
     }
 
     companion object {
-        val Default = DesktopConfig(
-            autoHidePanel = false,
-        ).apply {
+        val Default = DesktopConfig().apply {
+            // todo add from url or file
             addBackground("http://erzvo.com/wp-content/uploads/2022/11/22-11-05-10429-Kubo-kvety-2013-1.jpg")
         }
     }

+ 1 - 7
src/main/kotlin/eu/mjdev/desktop/provider/data/Theme.kt

@@ -8,12 +8,10 @@ import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.MaterialIconFont
 import eu.mjdev.desktop.components.fonticon.MaterialSymbolsSharp
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
-import kotlinx.coroutines.CoroutineScope
 
 // todo all customizable
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class Theme(
-    val scope: CoroutineScope?,
     backgroundColor: Color,
     backgroundRotationDelay: Long,
 
@@ -69,11 +67,7 @@ class Theme(
     val backgroundRotationDelay get() = backgroundRotationDelayState.value
 
     companion object {
-        @Suppress("FunctionName")
-        fun Default(
-            scope: CoroutineScope?
-        ) = Theme(
-            scope = scope,
+        val Default = Theme(
             iconSet = MaterialSymbolsSharp,
 
             backgroundColor = Color.SuperDarkGray,

+ 2 - 5
src/main/kotlin/eu/mjdev/desktop/provider/data/User.kt

@@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.AccountCircle
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.vector.ImageVector
-import kotlinx.coroutines.CoroutineScope
 
 @Suppress("MemberVisibilityCanBePrivate")
 class User(
@@ -25,13 +24,11 @@ class User(
 
     companion object {
         // todo
-        fun load(
-            scope: CoroutineScope?
-        ) = User(
+        fun load() = User(
             name = null,
             picture = Icons.Filled.AccountCircle,
             config = DesktopConfig.Default,
-            theme = Theme.Default(scope)
+            theme = Theme.Default
         )
     }
 }

+ 3 - 1
src/main/kotlin/eu/mjdev/desktop/windows/MainWindow.kt

@@ -67,7 +67,9 @@ fun MainWindow(
     }
     ContextMenu(
         contextMenuState = contextMenuState,
-        onMenuItemClick = { item -> log { "Context menu : $item" } }
+        onMenuItemClick = { item -> log { "Context menu : $item" } },
+        onShow = { onDesktopMenuShow() },
+        onHide = {}
     )
 //    launchedEffect {
 //        runCatching {

+ 155 - 18
src/main/kotlin/eu/mjdev/desktop/windows/TopWindow.kt

@@ -1,35 +1,172 @@
 package eu.mjdev.desktop.windows
 
+import androidx.compose.animation.*
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.window.FrameWindowScope
-import androidx.compose.ui.window.Window
-import androidx.compose.ui.window.WindowState
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.*
+import eu.mjdev.desktop.extensions.Compose.setWindowBounds
+import eu.mjdev.desktop.helpers.WindowFocusState.Companion.windowFocusHandler
+import eu.mjdev.desktop.provider.DesktopProvider
+import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
+import java.awt.Window
 
 @Composable
 fun TopWindow(
+    windowState: TopWindowState,
     resizable: Boolean = false,
     title: String = "",
     icon: Painter? = null,
     focusable: Boolean = true,
     onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
     onKeyEvent: (KeyEvent) -> Boolean = { false },
-    windowState: WindowState,
     onCloseRequest: () -> Unit = {},
+    onFocusChange: (hasFocus: Boolean) -> Unit = {},
     content: @Composable FrameWindowScope.() -> Unit
-) = Window(
-    state = windowState,
-    resizable = resizable,
-    undecorated = true,
-    onCloseRequest = onCloseRequest,
-    visible = true,
-    transparent = true,
-    title = title,
-    icon = icon,
-    focusable = focusable,
-    alwaysOnTop = true,
-    onPreviewKeyEvent = onPreviewKeyEvent,
-    onKeyEvent = onKeyEvent,
-    content = content
+) {
+    Window(
+        state = windowState,
+        resizable = resizable,
+        undecorated = true,
+        onCloseRequest = onCloseRequest,
+        visible = true,
+        transparent = true,
+        title = title,
+        icon = icon,
+        focusable = focusable,
+        alwaysOnTop = true,
+        onPreviewKeyEvent = onPreviewKeyEvent,
+        onKeyEvent = onKeyEvent,
+        content = {
+            windowFocusHandler { hasFocus -> onFocusChange(hasFocus) }
+            windowState.window = window
+            AnimatedVisibility(
+                visible = windowState.isVisible,
+                enter = windowState.enter,
+                exit = windowState.exit,
+            ) {
+                content()
+            }
+        }
+    )
+}
+
+data class WindowBounds(
+    val x: Dp = 0.dp,
+    val y: Dp = 0.dp,
+    val width: Dp = 0.dp,
+    val height: Dp = 0.dp
 )
+
+@Suppress("MemberVisibilityCanBePrivate", "unused")
+class TopWindowState(
+    val containerSize: DpSize,
+    override var placement: WindowPlacement,
+    override var position: WindowPosition,
+    override var size: DpSize = containerSize,
+    override var isMinimized: Boolean = false,
+    val minSize: DpSize = DpSize(0.dp, 0.dp),
+    val visible: Boolean = true,
+    val enter: EnterTransition = fadeIn() + expandIn(),
+    val exit: ExitTransition = shrinkOut() + fadeOut(),
+    val computeBounds: (visible: Boolean) -> WindowBounds = { isVisible ->
+        when {
+            isVisible -> WindowBounds(0.dp, 0.dp, size.width, size.height)
+            else -> WindowBounds(0.dp, 0.dp, minSize.width, minSize.height)
+        }
+    },
+
+    ) : WindowState {
+    var isVisible: Boolean = !isMinimized && visible
+        set(value) {
+            field = value
+            if (window != null) {
+                computeBounds(value).let { bounds ->
+                    window?.setWindowBounds(bounds.x, bounds.y, bounds.width, bounds.height)
+                }
+            }
+        }
+
+    var window: Window? = null
+        set(value) {
+            field = value
+            if (window != null) {
+                computeBounds(isVisible).let { bounds ->
+                    value?.setWindowBounds(bounds.x, bounds.y, bounds.width, bounds.height)
+                }
+            }
+        }
+
+    fun hide() {
+        isVisible = false
+    }
+
+    fun show() {
+        isVisible = true
+    }
+
+    companion object {
+        @Composable
+        fun rememberTopWindowState(
+            size: DpSize = DpSize(640.dp, 480.dp),
+            minSize: DpSize = DpSize(0.dp, 0.dp),
+            position: WindowPosition,
+            visible: Boolean = true,
+            placement: WindowPlacement = WindowPlacement.Floating,
+            api: DesktopProvider = LocalDesktop.current,
+            containerSize: DpSize = api.containerSize,
+            enter: EnterTransition = fadeIn() + expandIn(),
+            exit: ExitTransition = shrinkOut() + fadeOut(),
+            computeBounds: (visible: Boolean) -> WindowBounds = { isVisible ->
+                when {
+                    isVisible -> WindowBounds(0.dp, 0.dp, size.width, size.height)
+                    else -> WindowBounds(0.dp, 0.dp, 0.dp, 0.dp)
+                }
+            }
+        ) = TopWindowState(
+            containerSize = containerSize,
+            placement = placement,
+            position = position,
+            size = size,
+            minSize = minSize,
+            computeBounds = computeBounds,
+            enter = enter,
+            exit = exit,
+            visible = visible
+        )
+
+        @Composable
+        fun rememberTopWindowState(
+            size: DpSize = DpSize(640.dp, 480.dp),
+            minSize: DpSize = DpSize(0.dp, 0.dp),
+            position: Alignment = Alignment.BottomCenter,
+            visible: Boolean = true,
+            placement: WindowPlacement = WindowPlacement.Floating,
+            api: DesktopProvider = LocalDesktop.current,
+            containerSize: DpSize = api.containerSize,
+            enter: EnterTransition = fadeIn() + expandIn(),
+            exit: ExitTransition = shrinkOut() + fadeOut(),
+            computeBounds: (visible: Boolean) -> WindowBounds = { isVisible ->
+                when {
+                    isVisible -> WindowBounds(0.dp, 0.dp, size.width, size.height)
+                    else -> WindowBounds(0.dp, 0.dp, 0.dp, 0.dp)
+                }
+            }
+        ) = rememberTopWindowState(
+            size = size,
+            minSize = minSize,
+            position = WindowPosition.Aligned(position),
+            placement = placement,
+            api = api,
+            containerSize = containerSize,
+            computeBounds = computeBounds,
+            enter = enter,
+            exit = exit,
+            visible = visible
+        )
+    }
+}