DesktopPanel.kt 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package eu.mjdev.desktop.components.desktoppanel
  2. import androidx.compose.animation.*
  3. import androidx.compose.foundation.ExperimentalFoundationApi
  4. import androidx.compose.foundation.TooltipArea
  5. import androidx.compose.foundation.TooltipPlacement
  6. import androidx.compose.foundation.background
  7. import androidx.compose.foundation.gestures.Orientation
  8. import androidx.compose.foundation.layout.*
  9. import androidx.compose.foundation.lazy.LazyRow
  10. import androidx.compose.foundation.lazy.items
  11. import androidx.compose.material.Divider
  12. import androidx.compose.runtime.*
  13. import androidx.compose.ui.Alignment
  14. import androidx.compose.ui.Modifier
  15. import androidx.compose.ui.graphics.Brush
  16. import androidx.compose.ui.graphics.Color
  17. import androidx.compose.ui.unit.Dp
  18. import androidx.compose.ui.unit.DpOffset
  19. import androidx.compose.ui.unit.DpSize
  20. import androidx.compose.ui.unit.dp
  21. import eu.mjdev.desktop.components.shadow.TopShadow
  22. import eu.mjdev.desktop.components.slidemenu.SlidingMenu
  23. import eu.mjdev.desktop.components.slidemenu.VisibilityState
  24. import eu.mjdev.desktop.components.slidemenu.VisibilityState.Companion.rememberVisibilityState
  25. import eu.mjdev.desktop.extensions.Compose.height
  26. import eu.mjdev.desktop.provider.DesktopProvider
  27. import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
  28. import eu.mjdev.desktop.provider.data.App
  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. @OptIn(ExperimentalFoundationApi::class)
  34. @Suppress("FunctionName")
  35. @Composable
  36. fun DesktopPanel(
  37. modifier: Modifier = Modifier,
  38. api: DesktopProvider = LocalDesktop.current,
  39. backgroundColor: Color = api.currentUser.theme.backgroundColor,
  40. shadowColor: Color = backgroundColor,
  41. iconColor: Color = Color.Black,
  42. iconBackgroundColor: Color = Color.White,
  43. dividerWidth: Dp = 4.dp,
  44. iconSize: DpSize = DpSize(56.dp, 56.dp),
  45. iconPadding: PaddingValues = PaddingValues(4.dp),
  46. iconOuterPadding: PaddingValues = PaddingValues(2.dp),
  47. panelAlpha: Float = 0.7f,
  48. panelHeight: Dp = iconSize.height + iconPadding.height + iconOuterPadding.height,
  49. showMenuIcon: Boolean = true,
  50. panelState: VisibilityState = rememberVisibilityState(),
  51. enter: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it }),
  52. exit: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
  53. menuState: VisibilityState = rememberVisibilityState(),
  54. windowState: TopWindowState = rememberTopWindowState(
  55. position = api.currentUser.theme.panelLocation.alignment,
  56. size = api.containerSize.copy(height = panelHeight),
  57. exit = exit,
  58. enter = enter,
  59. computeBounds = { isVisible ->
  60. val containerHeight = api.containerSize.height
  61. val containerWidth = api.containerSize.width
  62. WindowBounds(
  63. 0.dp,
  64. if (isVisible) (containerHeight - panelHeight) else (containerHeight - dividerWidth),
  65. containerWidth,
  66. if (isVisible) panelHeight else dividerWidth
  67. )
  68. }
  69. ),
  70. favoriteApps: State<List<App>> = api.appsProvider.favoriteApps.collectAsState(emptyList()),
  71. tooltipData: MutableState<Any?> = mutableStateOf(null),
  72. onToolTip: (item: Any?) -> Unit = { item -> tooltipData.value = item },
  73. onVisibilityChange: (visible: Boolean) -> Unit = {},
  74. onMenuIconClicked: () -> Unit,
  75. ) = TopWindow(
  76. windowState = windowState,
  77. onFocusChange = { hasFocus ->
  78. if (!hasFocus && !menuState.isVisible) panelState.hide()
  79. }
  80. ) {
  81. SlidingMenu(
  82. modifier = modifier,
  83. orientation = Orientation.Vertical,
  84. state = panelState,
  85. onVisibilityChange = onVisibilityChange
  86. ) { isVisible ->
  87. if (!isVisible) {
  88. Divider(
  89. modifier = Modifier
  90. .fillMaxWidth()
  91. .height(dividerWidth),
  92. color = Color.Transparent,
  93. thickness = dividerWidth
  94. )
  95. }
  96. TooltipArea(
  97. tooltip = {
  98. // todo more types
  99. when (tooltipData.value) {
  100. is App -> DesktopPanelAppTooltip(tooltipData.value as App)
  101. else -> {}
  102. }
  103. },
  104. modifier = Modifier.fillMaxSize(),
  105. tooltipPlacement = TooltipPlacement.CursorPoint(
  106. alignment = Alignment.TopEnd,
  107. offset = DpOffset(16.dp, 32.dp)
  108. )
  109. ) {
  110. TopShadow(
  111. modifier = Modifier.fillMaxWidth(),
  112. color = shadowColor,
  113. contentBackgroundColor = backgroundColor.copy(alpha = panelAlpha)
  114. ) {
  115. LazyRow(
  116. modifier = Modifier.fillMaxSize().background(
  117. brush = Brush.verticalGradient(
  118. colors = listOf(
  119. backgroundColor.copy(alpha = 0.3f),
  120. backgroundColor.copy(alpha = 0.7f),
  121. backgroundColor.copy(alpha = 0.3f),
  122. )
  123. )
  124. ),
  125. horizontalArrangement = Arrangement.Start,
  126. verticalAlignment = Alignment.CenterVertically
  127. ) {
  128. if (showMenuIcon) {
  129. item {
  130. DesktopPanelIcon(
  131. icon = "menu",
  132. iconColor = iconColor,
  133. iconBackgroundColor = iconBackgroundColor,
  134. iconBackgroundHover = Color.White.copy(alpha = 0.3f),
  135. iconSize = iconSize,
  136. iconPadding = iconPadding,
  137. iconOuterPadding = iconOuterPadding,
  138. onToolTip = onToolTip,
  139. onClick = {
  140. onMenuIconClicked()
  141. }
  142. )
  143. Divider(
  144. modifier = Modifier
  145. .padding(2.dp)
  146. .height(iconSize.height - 8.dp)
  147. .width(2.dp)
  148. .background(Color.White.copy(alpha = 0.4f))
  149. )
  150. }
  151. }
  152. items(favoriteApps.value) { app ->
  153. DesktopPanelIcon(
  154. app = app,
  155. iconColor = iconColor,
  156. iconBackgroundColor = iconBackgroundColor,
  157. iconBackgroundHover = Color.White.copy(alpha = 0.3f),
  158. iconSize = iconSize,
  159. iconPadding = iconPadding,
  160. iconOuterPadding = iconOuterPadding,
  161. onToolTip = onToolTip,
  162. onClick = {
  163. app.start()
  164. }
  165. )
  166. }
  167. item {
  168. DesktopPanelText(
  169. text = api.appsProvider.currentLocale.country,
  170. backgroundHover = Color.White.copy(alpha = 0.3f),
  171. onToolTip = onToolTip,
  172. onClick = {
  173. // todo
  174. }
  175. )
  176. }
  177. }
  178. }
  179. }
  180. }
  181. LaunchedEffect(
  182. panelState.isVisible,
  183. api.currentUser.theme.panelHideDelay
  184. ) {
  185. panelState.enabled = api.currentUser.theme.panelHideDelay > 0
  186. windowState.isVisible = api.currentUser.theme.panelHideDelay < 1 || panelState.isVisible
  187. }
  188. }