@@ -30,6 +30,7 @@ import kotlinx.coroutines.Job
3030import kotlinx.coroutines.ensureActive
3131import ru.nsu.ccfit.zuev.osu.Config
3232import ru.nsu.ccfit.zuev.osu.ResourceManager
33+ import ru.nsu.ccfit.zuev.osu.helper.MD5Calculator
3334import ru.nsu.ccfit.zuev.osu.helper.StringTable
3435import ru.nsu.ccfit.zuev.osu.online.OnlineManager
3536import ru.nsu.ccfit.zuev.osuplus.R
@@ -73,7 +74,8 @@ class RoomPlayerCard : UILinearContainer() {
7374 }
7475 }
7576
76- fun cancelBannerJob () {
77+ fun cancelJobs () {
78+ playerButton.avatarJob?.cancel()
7779 playerButton.bannerJob?.cancel()
7880 }
7981
@@ -86,17 +88,22 @@ class RoomPlayerCard : UILinearContainer() {
8688 private var modDisplay: UIComponent ? = null
8789
8890 private val bannerSprite: UIShapedSprite
91+ private val avatarSprite: UIShapedSprite
8992
9093 var bannerJob: Job ? = null
9194 private set
9295
96+ var avatarJob: Job ? = null
97+ private set
98+
9399 private val defaultBackground = UIBox ().apply {
94100 cornerRadius = 12f
95101 color = Theme .current.accentColor * 0.15f
96102 alpha = 0.5f
97103 }
98104
99105 private var lastPlayerId = - 1L
106+ private val defaultAvatar = ResourceManager .getInstance().getTexture(" emptyavatar" )
100107
101108 private val hostIcon = FontAwesomeIcon (Icon .Crown ).apply {
102109 applyTheme = { color = it.accentColor }
@@ -115,9 +122,11 @@ class RoomPlayerCard : UILinearContainer() {
115122 init {
116123 width = FillParent
117124 orientation = Orientation .Horizontal
118- spacing = 6f
125+ spacing = 12f
119126
120127 bannerSprite = UIShapedSprite ().apply {
128+ inheritAncestorsColor = false
129+
121130 shape = object : UIBox () {
122131 init {
123132 cornerRadius = 12f
@@ -134,13 +143,35 @@ class RoomPlayerCard : UILinearContainer() {
134143 setColor(0.25f , 0.25f , 0.25f )
135144 }
136145
146+ avatarSprite = UIShapedSprite ().apply {
147+ inheritAncestorsColor = false
148+ size = Vec2 (50f )
149+
150+ shape = object : UIBox () {
151+ init {
152+ cornerRadius = 8f
153+ color = Color4 .Transparent
154+ }
155+
156+ override fun beginDraw (gl : GL10 ) {
157+ gl.glDepthMask(true )
158+ super .beginDraw(gl)
159+ }
160+ }
161+
162+ scaleType = ScaleType .Crop
163+ textureRegion = defaultAvatar
164+ }
165+
137166 background = defaultBackground
138167
139168 foreground = UIBox ().apply {
140169 cornerRadius = 12f
141170 paintStyle = PaintStyle .Outline
142171 }
143172
173+ + avatarSprite
174+
144175 innerContainer = linearContainer {
145176 orientation = Orientation .Vertical
146177 inheritAncestorsColor = false
@@ -157,7 +188,7 @@ class RoomPlayerCard : UILinearContainer() {
157188 textureRegion = ResourceManager .getInstance().getTexture(" missing" )
158189 anchor = Anchor .CenterLeft
159190 origin = Anchor .CenterLeft
160- size = Vec2 (18f )
191+ size = Vec2 (24f )
161192 }
162193 }
163194 }
@@ -167,6 +198,8 @@ class RoomPlayerCard : UILinearContainer() {
167198
168199 override fun onDetached () {
169200 super .onDetached()
201+
202+ avatarJob?.cancel()
170203 bannerJob?.cancel()
171204 }
172205
@@ -178,50 +211,11 @@ class RoomPlayerCard : UILinearContainer() {
178211 }
179212
180213 if (lastPlayerId != player.id) {
181- lastPlayerId = player.id
182- val bannerUrl = OnlineManager .getProfileBannerURL(player.id)
183-
184- bannerJob?.cancel()
185- bannerJob = null
186-
187- val resourceManager = ResourceManager .getInstance()
188- val loadedTexture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
189-
190- if (loadedTexture != null ) {
191- bannerSprite.textureRegion = loadedTexture
192- background = bannerSprite
193- } else {
194- bannerSprite.textureRegion = null
195- background = defaultBackground
196-
197- bannerJob = async {
198- ensureActive()
199-
200- if (OnlineManager .getInstance().loadProfileBannerToTextureManager(bannerUrl)) {
201- ensureActive()
202-
203- val texture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
204-
205- updateThread {
206- if (lastPlayerId == player.id) {
207- bannerSprite.textureRegion = texture
208-
209- if (texture != null ) {
210- background = bannerSprite
211- }
212- }
213- }
214- } else {
215- updateThread {
216- if (lastPlayerId == player.id) {
217- background = defaultBackground
218- }
219- }
220- }
221- }
222- }
214+ loadAvatar(player.id)
215+ loadBanner(player.id)
223216 }
224217
218+ lastPlayerId = player.id
225219 nameText.text = player.name
226220 nameText.spacing = 6f
227221
@@ -250,7 +244,7 @@ class RoomPlayerCard : UILinearContainer() {
250244 modDisplay?.detachSelf()
251245
252246 modDisplay = UIText ().apply {
253- minHeight = 18f // Force to take space even if no mods are enabled
247+ minHeight = 24f // Force to take space even if no mods are enabled
254248 font = ResourceManager .getInstance().getFont(" xs" )
255249 applyTheme = { color = it.accentColor * 0.8f }
256250 }
@@ -264,8 +258,8 @@ class RoomPlayerCard : UILinearContainer() {
264258 modDisplay?.detachSelf()
265259
266260 modDisplay = ModsIndicator ().apply {
267- minHeight = 18f // Force to take space even if no mods are enabled
268- iconSize = 18f
261+ minHeight = 24f // Force to take space even if no mods are enabled
262+ iconSize = 24f
269263 }
270264
271265 innerContainer + = modDisplay!!
@@ -338,5 +332,87 @@ class RoomPlayerCard : UILinearContainer() {
338332 }.show()
339333 }
340334 }
335+
336+ private fun loadAvatar (userId : Long ) {
337+ val resourceManager = ResourceManager .getInstance()
338+ val avatarUrl = OnlineManager .getAvatarURL(userId)
339+
340+ avatarJob?.cancel()
341+ avatarJob = null
342+
343+ val avatarKey = MD5Calculator .getStringMD5(avatarUrl)
344+ val loadedTexture = resourceManager.getTextureIfLoaded(avatarKey)
345+
346+ if (loadedTexture != null ) {
347+ avatarSprite.textureRegion = loadedTexture
348+ } else {
349+ avatarSprite.textureRegion = defaultAvatar
350+
351+ avatarJob = async {
352+ ensureActive()
353+
354+ if (OnlineManager .getInstance().loadAvatarToTextureManager(avatarUrl)) {
355+ ensureActive()
356+
357+ val texture = resourceManager.getTextureIfLoaded(avatarKey)
358+
359+ updateThread {
360+ if (lastPlayerId == userId) {
361+ avatarSprite.textureRegion = texture ? : defaultAvatar
362+ }
363+ }
364+ } else {
365+ updateThread {
366+ if (lastPlayerId == userId) {
367+ avatarSprite.textureRegion = defaultAvatar
368+ }
369+ }
370+ }
371+ }
372+ }
373+ }
374+
375+ private fun loadBanner (userId : Long ) {
376+ val resourceManager = ResourceManager .getInstance()
377+ val bannerUrl = OnlineManager .getProfileBannerURL(userId)
378+
379+ bannerJob?.cancel()
380+ bannerJob = null
381+
382+ val loadedTexture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
383+
384+ if (loadedTexture != null ) {
385+ bannerSprite.textureRegion = loadedTexture
386+ background = bannerSprite
387+ } else {
388+ bannerSprite.textureRegion = null
389+ background = defaultBackground
390+
391+ bannerJob = async {
392+ ensureActive()
393+
394+ if (OnlineManager .getInstance().loadProfileBannerToTextureManager(bannerUrl)) {
395+ ensureActive()
396+
397+ val texture = resourceManager.getProfileBannerTextureIfLoaded(bannerUrl)
398+
399+ updateThread {
400+ if (lastPlayerId == userId) {
401+ bannerSprite.textureRegion = texture
402+ if (texture != null ) {
403+ background = bannerSprite
404+ }
405+ }
406+ }
407+ } else {
408+ updateThread {
409+ if (lastPlayerId == userId) {
410+ background = defaultBackground
411+ }
412+ }
413+ }
414+ }
415+ }
416+ }
341417 }
342418}
0 commit comments