ControlCenter.kt 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package eu.mjdev.desktop.components.controlcenter
  2. import androidx.compose.animation.EnterTransition
  3. import androidx.compose.animation.ExitTransition
  4. import androidx.compose.foundation.ExperimentalFoundationApi
  5. import androidx.compose.foundation.background
  6. import androidx.compose.foundation.gestures.Orientation.Horizontal
  7. import androidx.compose.foundation.layout.*
  8. import androidx.compose.foundation.lazy.LazyColumn
  9. import androidx.compose.foundation.lazy.itemsIndexed
  10. import androidx.compose.foundation.mouseClickable
  11. import androidx.compose.foundation.shape.RoundedCornerShape
  12. import androidx.compose.material.Divider
  13. import androidx.compose.material.Icon
  14. import androidx.compose.runtime.*
  15. import androidx.compose.ui.Alignment
  16. import androidx.compose.ui.Modifier
  17. import androidx.compose.ui.graphics.Color
  18. import androidx.compose.ui.input.pointer.isSecondaryPressed
  19. import androidx.compose.ui.unit.DpSize
  20. import androidx.compose.ui.unit.dp
  21. import androidx.compose.ui.window.WindowPosition
  22. import eu.mjdev.desktop.components.controlcenter.ControlCenterPage.Companion.rememberControlCenterScope
  23. import eu.mjdev.desktop.components.sliding.SlidingMenu
  24. import eu.mjdev.desktop.components.sliding.VisibilityState
  25. import eu.mjdev.desktop.components.sliding.VisibilityState.Companion.rememberVisibilityState
  26. import eu.mjdev.desktop.extensions.Modifier.rightShadow
  27. import eu.mjdev.desktop.helpers.Animations.ControlCenterEnterAnimation
  28. import eu.mjdev.desktop.helpers.Animations.ControlCenterExitAnimation
  29. import eu.mjdev.desktop.provider.DesktopProvider
  30. import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
  31. import eu.mjdev.desktop.windows.ChromeWindow
  32. import kotlinx.coroutines.CoroutineScope
  33. import kotlinx.coroutines.launch
  34. @OptIn(ExperimentalFoundationApi::class)
  35. @Suppress("FunctionName", "DEPRECATION")
  36. @Composable
  37. fun ControlCenter(
  38. api: DesktopProvider = LocalDesktop.current,
  39. shadowColor: Color = Color.Black.copy(alpha = 0.3f), // todo theme
  40. pagerState: MutableState<Int> = remember { mutableStateOf(0) },
  41. controlCenterState: VisibilityState = rememberVisibilityState(),
  42. scope: CoroutineScope = rememberCoroutineScope(),
  43. enterAnimation: EnterTransition = ControlCenterEnterAnimation,
  44. exitAnimation: ExitTransition = ControlCenterExitAnimation,
  45. onFocusChange: (Boolean) -> Unit = {},
  46. onContextMenuClick: () -> Unit = {}
  47. ) {
  48. val backgroundColor by remember { api.currentUser.theme.backgroundColorState }
  49. val backgroundAlpha by remember { api.currentUser.theme.controlCenterBackgroundAlphaState }
  50. val backgroundInvoker = remember { { backgroundColor } }
  51. val cscope = rememberControlCenterScope(backgroundInvoker, api)
  52. val controlCenterExpandedWidth by remember { api.currentUser.theme.controlCenterExpandedWidthState }
  53. val controlCenterDividerWidth by remember { api.currentUser.theme.controlCenterDividerWidthState }
  54. val controlCenterIconColor by remember { api.currentUser.theme.controlCenterIconColorState }
  55. val controlCenterIconSize by remember { api.currentUser.theme.controlCenterIconSizeState }
  56. val pages by remember { api.controlCenterPagesState }
  57. val pagesFiltered = remember(pages) {
  58. derivedStateOf {
  59. pages.filter { p -> p.condition.invoke(cscope) }
  60. }
  61. }
  62. ChromeWindow(
  63. visible = true,
  64. enterAnimation = enterAnimation,
  65. exitAnimation = exitAnimation,
  66. position = WindowPosition.Aligned(Alignment.TopEnd),
  67. size = if (controlCenterState.isVisible) {
  68. DpSize(controlCenterExpandedWidth, api.containerSize.height)
  69. } else {
  70. DpSize(controlCenterDividerWidth, api.containerSize.height)
  71. },
  72. onFocusChange = { focused ->
  73. controlCenterState.onFocusChange(focused)
  74. onFocusChange(focused)
  75. }
  76. ) {
  77. SlidingMenu(
  78. modifier = Modifier.fillMaxHeight(),
  79. orientation = Horizontal,
  80. state = controlCenterState,
  81. ) { isVisible ->
  82. if (!isVisible) {
  83. Divider(
  84. modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
  85. color = Color.Transparent,
  86. thickness = controlCenterDividerWidth
  87. )
  88. } else {
  89. Box(
  90. modifier = Modifier
  91. .fillMaxHeight()
  92. .wrapContentSize()
  93. .background(backgroundColor.copy(alpha = backgroundAlpha)),
  94. ) {
  95. Row(
  96. modifier = Modifier.fillMaxHeight().wrapContentSize(),
  97. ) {
  98. Box(
  99. contentAlignment = Alignment.TopEnd
  100. ) {
  101. LazyColumn(
  102. modifier = Modifier
  103. .fillMaxHeight()
  104. .background(backgroundColor)
  105. .padding(8.dp),
  106. verticalArrangement = Arrangement.Top,
  107. horizontalAlignment = Alignment.Start
  108. ) {
  109. itemsIndexed(pagesFiltered.value) { idx, page ->
  110. val isSelected = (pagerState.value == idx)
  111. Icon(
  112. modifier = Modifier
  113. .padding(vertical = 8.dp)
  114. .size(controlCenterIconSize)
  115. .background(
  116. color = if (isSelected) Color.White else Color.Transparent,
  117. shape = RoundedCornerShape(8.dp)
  118. )
  119. .mouseClickable {
  120. if (buttons.isSecondaryPressed) {
  121. onContextMenuClick()
  122. } else {
  123. scope.launch {
  124. pagerState.value = idx
  125. }
  126. }
  127. },
  128. imageVector = page.icon,
  129. contentDescription = "",
  130. tint = if (isSelected) backgroundColor else controlCenterIconColor
  131. )
  132. }
  133. }
  134. Box(
  135. modifier = Modifier
  136. .padding(
  137. end = controlCenterIconSize.width + 16.dp
  138. )
  139. .fillMaxHeight()
  140. .wrapContentSize(),
  141. ) {
  142. Row {
  143. Divider(
  144. modifier = Modifier
  145. .fillMaxHeight()
  146. .width(2.dp),
  147. color = Color.White.copy(0.1f),
  148. thickness = 2.dp
  149. )
  150. Box(
  151. modifier = Modifier
  152. .width(controlCenterExpandedWidth)
  153. .rightShadow(
  154. color = shadowColor,
  155. offsetX = 4.dp,
  156. blur = 10.dp,
  157. ),
  158. ) {
  159. with(pagesFiltered.value[pagerState.value]) {
  160. content(cscope)
  161. }
  162. }
  163. }
  164. }
  165. Divider(
  166. modifier = Modifier.fillMaxHeight().width(controlCenterDividerWidth),
  167. color = Color.White.copy(0.1f),
  168. thickness = controlCenterDividerWidth
  169. )
  170. }
  171. }
  172. }
  173. }
  174. }
  175. }
  176. }