@@ -5,53 +5,112 @@ package org.polyfrost.polyplus.client.gui
55import kotlinx.coroutines.future.asCompletableFuture
66import org.polyfrost.polyplus.client.network.http.PolyCosmetics
77import org.polyfrost.polyplus.client.network.http.responses.Cosmetic
8+ import org.polyfrost.polyui.PolyUI
89import org.polyfrost.polyui.color.rgba
910import org.polyfrost.polyui.component.Drawable
11+ import org.polyfrost.polyui.component.extensions.events
1012import org.polyfrost.polyui.component.extensions.hide
1113import org.polyfrost.polyui.component.extensions.ignoreLayout
1214import org.polyfrost.polyui.component.extensions.named
1315import org.polyfrost.polyui.component.extensions.setPalette
1416import org.polyfrost.polyui.component.impl.Block
1517import org.polyfrost.polyui.component.impl.Group
18+ import org.polyfrost.polyui.component.impl.Image
1619import org.polyfrost.polyui.component.impl.Text
20+ import org.polyfrost.polyui.event.Event
21+ import org.polyfrost.polyui.event.State
1722import org.polyfrost.polyui.unit.Align
1823import org.polyfrost.polyui.unit.Vec2
24+ import org.polyfrost.polyui.utils.image
25+ import kotlin.math.max
1926
2027private const val TEST_PRICE = " 12.99"
28+ private const val BOX_HORIZONTAL_PADDING = 13f
29+ private const val BOX_VERTICAL_PADDING = 17f
2130private const val CARD_WIDTH = 180f
2231private const val CARD_HEIGHT = 258f
2332
24- fun CosmeticList (size : Vec2 ): Drawable {
25- val columnCount = (size.x / CARD_WIDTH ).toInt()
26- val rowCount = (size.y / CARD_HEIGHT ).toInt()
33+ fun CosmeticList (polyUI : PolyUI , size : Vec2 ): Drawable {
34+ // yay magic numbies :3
35+ val rawColumns = ((size.x - 2 * BOX_HORIZONTAL_PADDING + 18f ) / (CARD_WIDTH + 18f )).toInt()
36+ val rawRows = ((size.y - 2 * BOX_VERTICAL_PADDING + 14f ) / (CARD_HEIGHT + 14f )).toInt()
37+ val columnCount = max(1 , rawColumns)
38+ val rowCount = max(1 , rawRows)
39+ println (" CosmeticList will have $columnCount columns and $rowCount rows for size $size " )
2740
28- val cosmetics = mutableListOf<Cosmetic >()
29- PolyCosmetics .getAll().asCompletableFuture().thenAccept { result ->
30- result.onSuccess { list ->
31- cosmetics.addAll(list.contents)
41+ val cosmetics = State <List <Cosmetic >>(emptyList())
42+ val completableFuture = PolyCosmetics .getAll()
43+ .asCompletableFuture()
44+ .thenAccept { result ->
45+ result.onSuccess { list ->
46+ cosmetics.value = list.contents
47+ }
3248 }
33- }.join()
3449
35- return Group (
36- children = List (rowCount) { rowIndex ->
50+ var rowsContainer: Drawable ? = null
51+ val cosmeticsListener = cosmeticsListener@ { newCosmetics: List <Cosmetic > ->
52+ println (" Received ${newCosmetics.size} cosmetics:\n ${newCosmetics.joinToString(" \n " )} " )
53+ val theRows = rowsContainer ? : return @cosmeticsListener false
54+ val maxVisible = rowCount * columnCount
55+ val showing = if (newCosmetics.size > maxVisible) {
56+ newCosmetics.subList(0 , maxVisible)
57+ } else {
58+ newCosmetics
59+ }
60+
61+ // Clear existing cards
62+ for (rowIndex in 0 until rowCount) {
63+ val rowDrawable = theRows[rowIndex] as ? Group ? : continue
64+
65+ val existingChildren = rowDrawable.children ? : emptyList()
66+ for (child in existingChildren) {
67+ rowDrawable.removeChild(child)
68+ }
69+
70+ for (columnIndex in 0 until columnCount) {
71+ val cosmeticIndex = rowIndex * columnCount + columnIndex
72+ val cosmetic = showing.getOrNull(cosmeticIndex) ? : break
73+ rowDrawable.addChild(CosmeticCard (cosmetic))
74+ }
75+ }
76+
77+ false
78+ }
79+
80+ cosmetics.listen(cosmeticsListener)
81+
82+ val list = Group (
83+ children = Array (rowCount) { rowIndex ->
3784 Group (
38- children = List (columnCount) { columnIndex ->
39- val cosmeticIndex = rowIndex * columnCount + columnIndex
40- if (cosmeticIndex < cosmetics.size) {
41- CosmeticCard (cosmetics[cosmeticIndex])
42- } else {
43- null
44- }
45- }.filterNotNull().toTypedArray(),
46-
47- alignment = Align (padEdges = Vec2 .ZERO , padBetween = Vec2 (18f , 14f )),
85+ children = emptyArray(), // start empty; listener will populate
86+ alignment = Align (
87+ padEdges = Vec2 .ZERO ,
88+ padBetween = Vec2 (18f , 14f ),
89+ ),
4890 size = Vec2 (CARD_WIDTH * columnCount, CARD_HEIGHT ),
49- )
50- }.toTypedArray() ,
91+ ).named( " CosmeticListRow $rowIndex " )
92+ },
5193
52- alignment = Align (padEdges = Vec2 (13f , 17f )),
94+ alignment = Align (padEdges = Vec2 (BOX_HORIZONTAL_PADDING , BOX_VERTICAL_PADDING )),
5395 size = size,
54- ).named(" CosmeticList" )
96+ ).events {
97+ Event .Lifetime .Removed then {
98+ cosmetics.removeListener(cosmeticsListener)
99+ completableFuture.cancel(true )
100+ Unit
101+ }
102+ }.named(" CosmeticList" )
103+
104+ // Hook the container so the listener can see it
105+ list.setup(polyUI)
106+ rowsContainer = list
107+
108+ // Optionally, populate immediately with whatever is currently in state
109+ if (cosmetics.value.isNotEmpty()) {
110+ cosmeticsListener(cosmetics.value)
111+ }
112+
113+ return list
55114}
56115
57116private fun CosmeticCard (cosmetic : Cosmetic ): Drawable {
@@ -61,36 +120,52 @@ private fun CosmeticCard(cosmetic: Cosmetic): Drawable {
61120
62121 ).ignoreLayout().hide(),
63122
64- // Preview
65- Block (
66- size = Vec2 (144f , 144f ),
67- color = rgba(40 , 40 , 40 ),
68- ),
69-
123+ // Content
70124 Group (
71- // Name
72- Text (
73- text = " Cosmetic ${cosmetic.id} " ,
74- fontSize = 14f
125+ // Preview
126+ Block (
127+ size = Vec2 ( 144f , 144f ) ,
128+ color = rgba( 40 , 40 , 40 ),
75129 ),
76130
77- // Price
78- Text (
79- text = " $${TEST_PRICE } " ,
80- fontSize = 14f ,
81- ),
131+ Group (
132+ // Name
133+ Text (
134+ text = " Cosmetic ${cosmetic.id} " ,
135+ fontSize = 14f
136+ ),
82137
83- size = Vec2 (144f , 42f ),
84- alignment = Align (
85- line = Align .Line .Start ,
86- mode = Align .Mode .Vertical ,
87- padBetween = Vec2 (0f , 2f ),
88- padEdges = Vec2 .ZERO
138+ // Price
139+ Text (
140+ text = " $${TEST_PRICE } " ,
141+ fontSize = 14f ,
142+ ),
143+
144+ size = Vec2 (144f , 42f ),
145+ alignment = Align (
146+ line = Align .Line .Start ,
147+ mode = Align .Mode .Vertical ,
148+ padBetween = Vec2 (0f , 2f ),
149+ padEdges = Vec2 .ZERO
150+ ),
89151 ),
152+
153+ size = Vec2 (CARD_WIDTH , 198f ),
154+ alignment = Align (padEdges = Vec2 (18f , 18f ), padBetween = Vec2 (0f , 8f )),
90155 ),
91156
157+ // Add to Cart Button
158+ Block (
159+ Image (" /assets/polyplus/ico/shopping-cart/0.svg" .image()),
160+ Text (" Add to cart" , fontSize = 14f ),
161+
162+ size = Vec2 (180f , 36f ),
163+ alignment = Align (main = Align .Content .Center , cross = Align .Content .Center ),
164+ radii = floatArrayOf(0f , 0f , 12f , 12f ),
165+ ).setPalette { component.bg },
166+
92167 size = Vec2 (CARD_WIDTH , CARD_HEIGHT ),
93- alignment = Align (main = Align .Content .Center , cross = Align .Content .Center , padEdges = Vec2 .ZERO ),
168+ alignment = Align (main = Align .Content .Center , cross = Align .Content .SpaceBetween , padEdges = Vec2 .ZERO ),
94169 radii = floatArrayOf(12f ),
95170 ).named(" CosmeticCard" )
96171}
0 commit comments