AppsMenu.kt 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package eu.mjdev.desktop.components.appsmenu
  2. import androidx.compose.animation.*
  3. import androidx.compose.foundation.background
  4. import androidx.compose.foundation.border
  5. import androidx.compose.foundation.gestures.Orientation
  6. import androidx.compose.foundation.layout.*
  7. import androidx.compose.foundation.lazy.LazyColumn
  8. import androidx.compose.foundation.lazy.items
  9. import androidx.compose.foundation.shape.RoundedCornerShape
  10. import androidx.compose.runtime.Composable
  11. import androidx.compose.runtime.MutableState
  12. import androidx.compose.runtime.mutableStateOf
  13. import androidx.compose.runtime.remember
  14. import androidx.compose.ui.Alignment
  15. import androidx.compose.ui.Modifier
  16. import androidx.compose.ui.draw.clip
  17. import androidx.compose.ui.graphics.Color
  18. import androidx.compose.ui.unit.Dp
  19. import androidx.compose.ui.unit.DpSize
  20. import androidx.compose.ui.unit.dp
  21. import eu.mjdev.desktop.components.custom.UserAvatar
  22. import eu.mjdev.desktop.components.slidemenu.VisibilityState
  23. import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
  24. import eu.mjdev.desktop.extensions.Compose.launchedEffect
  25. import eu.mjdev.desktop.provider.DesktopProvider
  26. import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
  27. import eu.mjdev.desktop.provider.data.App
  28. import eu.mjdev.desktop.provider.data.Category
  29. import eu.mjdev.desktop.windows.TopWindow
  30. import eu.mjdev.desktop.windows.TopWindowState
  31. import eu.mjdev.desktop.windows.TopWindowState.Companion.rememberTopWindowState
  32. import eu.mjdev.desktop.windows.WindowBounds
  33. @Composable
  34. fun AppsMenu(
  35. modifier: Modifier = Modifier,
  36. api: DesktopProvider = LocalDesktop.current,
  37. appMenuMinWidth: Dp = 480.dp, // todo
  38. appMenuMinHeight: Dp = 640.dp, // todo
  39. bottomY: Dp = 64.dp, // todo panel height
  40. backgroundColor: Color = api.currentUser.theme.backgroundColor,
  41. menuPadding: PaddingValues = PaddingValues(2.dp), // todo
  42. menuState: VisibilityState = rememberVisibilityState(),
  43. panelState: VisibilityState = rememberVisibilityState(),
  44. items: MutableState<List<Any>> = remember { mutableStateOf(api.appsProvider.appCategories) },
  45. enter: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it }),
  46. exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
  47. appMenuMaxHeight: Dp = api.containerSize.height - bottomY,
  48. windowState: TopWindowState = rememberTopWindowState(
  49. position = api.currentUser.theme.panelLocation.alignment,
  50. size = DpSize(appMenuMinWidth, appMenuMinHeight),
  51. exit = exit,
  52. enter = enter,
  53. visible = menuState.isVisible,
  54. computeBounds = { isVisible ->
  55. val containerHeight = api.containerSize.height
  56. WindowBounds(
  57. 0.dp,
  58. if (isVisible) (containerHeight - appMenuMinHeight) else containerHeight,
  59. appMenuMinWidth,
  60. if (isVisible) appMenuMinHeight else 0.dp
  61. )
  62. }
  63. )
  64. ) = TopWindow(
  65. windowState = windowState,
  66. onFocusChange = { hasFocus ->
  67. if (!hasFocus) menuState.hide()
  68. }
  69. ) {
  70. Box(
  71. modifier = modifier
  72. .padding(bottom = bottomY)
  73. .width(appMenuMinWidth)
  74. .heightIn(
  75. min = appMenuMinHeight,
  76. max = appMenuMaxHeight
  77. )
  78. ) {
  79. Box(
  80. modifier = Modifier
  81. .fillMaxSize()
  82. .padding(menuPadding)
  83. .background(backgroundColor.copy(alpha = 0.7f), RoundedCornerShape(24.dp))
  84. .clip(RoundedCornerShape(24.dp))
  85. .border(2.dp, backgroundColor, RoundedCornerShape(24.dp))
  86. ) {
  87. Column(
  88. modifier = Modifier.fillMaxSize()
  89. ) {
  90. UserAvatar(
  91. avatarSize = 64.dp,
  92. backgroundColor = backgroundColor,
  93. orientation = Orientation.Horizontal
  94. )
  95. LazyColumn(
  96. modifier = Modifier
  97. .padding(bottom = 48.dp)
  98. .fillMaxSize()
  99. .padding(24.dp)
  100. ) {
  101. when (items.value.firstOrNull()) {
  102. is Category -> {
  103. items(items.value) { item ->
  104. AppsMenuCategory(
  105. category = item as Category,
  106. backgroundColor = backgroundColor,
  107. iconTint = Color.White
  108. ) { category ->
  109. items.value = api.appsProvider.categoriesAndApps[category.name]
  110. ?.sortedBy { it.name } ?: emptyList()
  111. }
  112. }
  113. }
  114. is App -> {
  115. items(items.value) { item ->
  116. AppsMenuApp(
  117. app = item as App,
  118. backgroundColor = backgroundColor,
  119. iconTint = Color.White
  120. ) { app ->
  121. app?.start()
  122. menuState.hide()
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }
  129. AppsBottomBar(
  130. modifier = Modifier
  131. .fillMaxWidth()
  132. .wrapContentHeight()
  133. .align(Alignment.BottomStart)
  134. .background(backgroundColor)
  135. )
  136. }
  137. launchedEffect(panelState.isVisible) { isVisible ->
  138. if (!isVisible) menuState.isVisible = false
  139. }
  140. launchedEffect(menuState.isVisible) { isVisible ->
  141. if (!isVisible) {
  142. items.value = api.appsProvider.appCategories
  143. }
  144. }
  145. }
  146. }