GridPlacedScopeImpl.kt 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (c) Milan Jurkulák 2024.
  3. * Contact:
  4. * e: mimoccc@gmail.com
  5. * e: mj@mjdev.org
  6. * w: https://mjdev.org
  7. */
  8. package eu.mjdev.desktop.components.grid.base
  9. import androidx.compose.runtime.Composable
  10. import eu.mjdev.desktop.components.grid.base.GridPlacedCells.Companion.firstColumn
  11. import eu.mjdev.desktop.components.grid.base.GridPlacedCells.Companion.firstRow
  12. import eu.mjdev.desktop.components.grid.base.GridPlacedCells.Companion.isColumnOutsideOfGrid
  13. import eu.mjdev.desktop.components.grid.base.GridPlacedCells.Companion.isRowOutsideOfGrid
  14. import eu.mjdev.desktop.components.grid.base.GridPlacedSpanAnchor.Companion.bottomBound
  15. import eu.mjdev.desktop.components.grid.base.GridPlacedSpanAnchor.Companion.leftBound
  16. import eu.mjdev.desktop.components.grid.base.GridPlacedSpanAnchor.Companion.rightBound
  17. import eu.mjdev.desktop.components.grid.base.GridPlacedSpanAnchor.Companion.topBound
  18. class GridPlacedScopeImpl(
  19. private val cells: GridPlacedCells,
  20. private val placementPolicy: GridPlacedPlacementPolicy
  21. ) : GridPlacedScope {
  22. val data: MutableList<GridPlacedContent> = mutableListOf()
  23. override fun item(
  24. row: Int,
  25. column: Int,
  26. rowSpan: Int,
  27. columnSpan: Int,
  28. itemContent: @Composable GridPlacedItemScope.() -> Unit
  29. ) {
  30. checkSpan(rowSpan = rowSpan, columnSpan = columnSpan)
  31. placeExplicitly(
  32. row = row,
  33. column = column,
  34. rowSpan = rowSpan,
  35. columnSpan = columnSpan,
  36. itemContent = itemContent
  37. )
  38. }
  39. override fun item(
  40. rowSpan: Int,
  41. columnSpan: Int,
  42. itemContent: @Composable GridPlacedItemScope.() -> Unit
  43. ) {
  44. checkSpan(rowSpan = rowSpan, columnSpan = columnSpan)
  45. placeImplicitly(
  46. rowSpan = rowSpan,
  47. columnSpan = columnSpan,
  48. itemContent = itemContent
  49. )
  50. }
  51. private fun checkSpan(rowSpan: Int, columnSpan: Int) {
  52. check(rowSpan > 0) { "`rowSpan` must be > 0" }
  53. check(columnSpan > 0) { "`columnSpan` must be > 0" }
  54. }
  55. private fun placeImplicitly(
  56. rowSpan: Int,
  57. columnSpan: Int,
  58. itemContent: @Composable GridPlacedItemScope.() -> Unit
  59. ) {
  60. val anchor = placementPolicy.anchor
  61. val lastItem = data.lastOrNull()
  62. var row: Int
  63. var column: Int
  64. when (placementPolicy.mainAxis) {
  65. GridPlacedPlacementPolicy.MainAxis.HORIZONTAL -> {
  66. row = findCurrentRow(cells, placementPolicy, lastItem)
  67. column = findNextColumn(cells, placementPolicy, lastItem)
  68. if (cells.isColumnOutsideOfGrid(column, columnSpan, anchor)) {
  69. column = cells.firstColumn(placementPolicy)
  70. row = findNextRow(cells, placementPolicy, lastItem)
  71. }
  72. }
  73. GridPlacedPlacementPolicy.MainAxis.VERTICAL -> {
  74. column = findCurrentColumn(cells, placementPolicy, lastItem)
  75. row = findNextRow(cells, placementPolicy, lastItem)
  76. if (cells.isRowOutsideOfGrid(row, rowSpan, anchor)) {
  77. row = cells.firstRow(placementPolicy)
  78. column = findNextColumn(cells, placementPolicy, lastItem)
  79. }
  80. }
  81. }
  82. placeExplicitly(
  83. row = row,
  84. column = column,
  85. rowSpan = rowSpan,
  86. columnSpan = columnSpan,
  87. itemContent = itemContent
  88. )
  89. }
  90. private fun placeExplicitly(
  91. row: Int,
  92. column: Int,
  93. rowSpan: Int,
  94. columnSpan: Int,
  95. itemContent: @Composable GridPlacedItemScope.() -> Unit
  96. ) {
  97. val anchor = placementPolicy.anchor
  98. val rowOutside = cells.isRowOutsideOfGrid(row, rowSpan, anchor)
  99. val columnOutside = cells.isColumnOutsideOfGrid(column, columnSpan, anchor)
  100. if (rowOutside || columnOutside) {
  101. onSkipped(row, column, rowSpan, columnSpan)
  102. } else {
  103. onPlaced(
  104. left = anchor.leftBound(column, columnSpan),
  105. top = anchor.topBound(row, rowSpan),
  106. right = anchor.rightBound(column, columnSpan),
  107. bottom = anchor.bottomBound(row, rowSpan),
  108. itemContent = itemContent
  109. )
  110. }
  111. }
  112. private fun onSkipped(row: Int?, column: Int?, rowSpan: Int, columnSpan: Int) {
  113. println(
  114. """
  115. Skipped position: [${row}x$column], span size: [${rowSpan}x$columnSpan]
  116. Grid size: [${cells.rowCount}x${cells.columnCount}]
  117. """.trimIndent()
  118. )
  119. }
  120. private fun onPlaced(
  121. left: Int,
  122. top: Int,
  123. right: Int,
  124. bottom: Int,
  125. itemContent: @Composable GridPlacedItemScope.() -> Unit
  126. ) {
  127. data.add(
  128. GridPlacedContent(
  129. top = top,
  130. left = left,
  131. right = right,
  132. bottom = bottom,
  133. item = { itemContent() }
  134. )
  135. )
  136. }
  137. private fun findCurrentRow(
  138. cells: GridPlacedCells,
  139. placementPolicy: GridPlacedPlacementPolicy,
  140. lastItem: GridPlacedContent?
  141. ): Int {
  142. return if (lastItem != null) {
  143. when (placementPolicy.verticalDirection) {
  144. GridPlacedPlacementPolicy.VerticalDirection.TOP_BOTTOM -> lastItem.top
  145. GridPlacedPlacementPolicy.VerticalDirection.BOTTOM_TOP -> lastItem.bottom
  146. }
  147. } else {
  148. cells.firstRow(placementPolicy)
  149. }
  150. }
  151. private fun findNextRow(
  152. cells: GridPlacedCells,
  153. placementPolicy: GridPlacedPlacementPolicy,
  154. lastItem: GridPlacedContent?
  155. ): Int {
  156. val lastRow = findCurrentRow(cells, placementPolicy, lastItem)
  157. val lastRowSpan = lastItem?.rowSpan ?: 0
  158. return when (placementPolicy.verticalDirection) {
  159. GridPlacedPlacementPolicy.VerticalDirection.TOP_BOTTOM -> lastRow + lastRowSpan
  160. GridPlacedPlacementPolicy.VerticalDirection.BOTTOM_TOP -> lastRow - lastRowSpan
  161. }
  162. }
  163. private fun findCurrentColumn(
  164. cells: GridPlacedCells,
  165. placementPolicy: GridPlacedPlacementPolicy,
  166. lastItem: GridPlacedContent?
  167. ): Int {
  168. return if (lastItem != null) {
  169. when (placementPolicy.horizontalDirection) {
  170. GridPlacedPlacementPolicy.HorizontalDirection.START_END -> lastItem.left
  171. GridPlacedPlacementPolicy.HorizontalDirection.END_START -> lastItem.right
  172. }
  173. } else {
  174. cells.firstColumn(placementPolicy)
  175. }
  176. }
  177. private fun findNextColumn(
  178. cells: GridPlacedCells,
  179. placementPolicy: GridPlacedPlacementPolicy,
  180. lastItem: GridPlacedContent?
  181. ): Int {
  182. val lastColumn = findCurrentColumn(cells, placementPolicy, lastItem)
  183. val lastColumnSpan = lastItem?.columnSpan ?: 0
  184. return when (placementPolicy.horizontalDirection) {
  185. GridPlacedPlacementPolicy.HorizontalDirection.START_END -> lastColumn + lastColumnSpan
  186. GridPlacedPlacementPolicy.HorizontalDirection.END_START -> lastColumn - lastColumnSpan
  187. }
  188. }
  189. }