diff options
| author | xbzk <xbzk@eden-emu.dev> | 2025-11-20 19:19:14 +0100 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2025-11-20 19:19:14 +0100 |
| commit | 65fa1a37e27579db54b14904a732dab785eebf99 (patch) | |
| tree | 37d2fdd2c7f7b4894ccfe494ad82db355451a33f /src/android | |
| parent | 41192e6e3de7f5aac6916fd137642afd6ca15f91 (diff) | |
READY TO MERGE [android] fix for carousel late bottominset and one single game bugs (#3028)
kleidis found a rare condition that pops when using gesture navigation, in which by the lack of bottom inset availability in time, carousel sizes get oversized. then i've put some non zero value backup to cover.
Co-authored-by: Allison Cunha <allisonbzk@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3028
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
Diffstat (limited to 'src/android')
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt | 19 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt | 86 |
2 files changed, 59 insertions, 46 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt index 80055628e1..106bf3faa2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt @@ -53,6 +53,7 @@ class GamesFragment : Fragment() { private var originalHeaderLeftMargin: Int? = null private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID + private var fallbackBottomInset: Int = 0 companion object { private const val SEARCH_TEXT = "SearchText" @@ -208,12 +209,12 @@ class GamesFragment : Fragment() { else -> throw IllegalArgumentException("Invalid view type: $savedViewType") } if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) { - doOnNextLayout { - (this as? CarouselRecyclerView)?.setCarouselMode(true, gameAdapter) - adapter = gameAdapter + (binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)} + doOnNextLayout { //Carousel: important to avoid overlap issues + (this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset) } } else { - (this as? CarouselRecyclerView)?.setCarouselMode(false) + (this as? CarouselRecyclerView)?.setupCarousel(false) } adapter = gameAdapter lastViewType = savedViewType @@ -237,9 +238,8 @@ class GamesFragment : Fragment() { override fun onResume() { super.onResume() if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) { - (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState( - gamesViewModel.lastScrollPosition - ) + (binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true) + (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition) } } @@ -494,6 +494,11 @@ class GamesFragment : Fragment() { mlpFab.rightMargin = rightInset + fabPadding binding.addDirectory.layoutParams = mlpFab + val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()) + val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) + val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom) + fallbackBottomInset = bottomInset + (binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset) windowInsets } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt index 2c35e7349a..33ca9ff328 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt @@ -20,9 +20,8 @@ import androidx.core.view.doOnNextLayout import org.yuzu.yuzu_emu.YuzuApplication import androidx.preference.PreferenceManager import androidx.core.view.WindowInsetsCompat - /** - * CarouselRecyclerView encapsulates all carousel logic for the games UI. + * CarouselRecyclerView encapsulates all carousel content for the games UI. * It manages overlapping cards, center snapping, custom drawing order, * joypad & fling navigation and mid-screen swipe-to-refresh. */ @@ -34,6 +33,7 @@ class CarouselRecyclerView @JvmOverloads constructor( private var overlapFactor: Float = 0f private var overlapPx: Int = 0 + private var bottomInset: Int = -1 private var overlapDecoration: OverlappingDecoration? = null private var pagerSnapHelper: PagerSnapHelper? = null private var scalingScrollListener: OnScrollListener? = null @@ -202,46 +202,61 @@ class CarouselRecyclerView @JvmOverloads constructor( } } - fun setCarouselMode(enabled: Boolean, gameAdapter: GameAdapter? = null) { + fun refreshView() { + updateChildScalesAndAlpha() + focusCenteredCard() + } + + fun notifyInsetsReady(newBottomInset: Int) { + if (bottomInset != newBottomInset) { + bottomInset = newBottomInset + } + setupCarousel(true) + } + + fun notifyLaidOut(fallBackBottomInset: Int) { + if (bottomInset < 0) bottomInset = fallBackBottomInset + var gameAdapter = adapter as? GameAdapter ?: return + var newCardSize = cardSize(bottomInset) + if (gameAdapter.cardSize != newCardSize) { + gameAdapter.setCardSize(newCardSize) + } + setupCarousel(true) + } + + fun cardSize(bottomInset: Int): Int { + val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1) + val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn( + 0f, + 1f + ) + return (userFactor * (height - bottomInset)).toInt() + } + + fun setupCarousel(enabled: Boolean) { if (enabled) { - useCustomDrawingOrder = true + val gameAdapter = adapter as? GameAdapter ?: return + if (gameAdapter.cardSize == 0) return + if (bottomInset < 0) return - val insets = rootWindowInsets?.let { WindowInsetsCompat.toWindowInsetsCompat(it, this) } - val bottomInset = insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0 - val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1) - val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn( - 0f, - 1f - ) - val cardSize = (userFactor * (height - bottomInset)).toInt() - gameAdapter?.setCardSize(cardSize) + useCustomDrawingOrder = true + val cardSize = gameAdapter.cardSize - val internalOverlapFactor = resources.getFraction( - R.fraction.carousel_overlap_factor, - 1, - 1 - ) - overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn( - 0f, - 1f - ) + val internalOverlapFactor = resources.getFraction(R.fraction.carousel_overlap_factor,1,1) + overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(0f,1f) overlapPx = (cardSize * overlapFactor).toInt() - val internalFlingMultiplier = resources.getFraction( - R.fraction.carousel_fling_multiplier, - 1, - 1 - ) + val internalFlingMultiplier = resources.getFraction(R.fraction.carousel_fling_multiplier,1,1) flingMultiplier = preferences.getFloat( CAROUSEL_FLING_MULTIPLIER, internalFlingMultiplier ).coerceIn(1f, 5f) - gameAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + gameAdapter .registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onChanged() { if (pendingScrollAfterReload) { - post { - jigglyScroll() + doOnNextLayout { + refreshView() pendingScrollAfterReload = false } } @@ -257,7 +272,7 @@ class CarouselRecyclerView @JvmOverloads constructor( addItemDecoration(overlapDecoration!!) } - // Gradual scalingAdd commentMore actions + // Gradual scaling on scroll if (scalingScrollListener == null) { scalingScrollListener = object : OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -315,8 +330,7 @@ class CarouselRecyclerView @JvmOverloads constructor( super.scrollToPosition(position) (layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(position, overlapPx) doOnNextLayout { - updateChildScalesAndAlpha() - focusCenteredCard() + refreshView() } } @@ -382,12 +396,6 @@ class CarouselRecyclerView @JvmOverloads constructor( return sorted[i].first } - fun jigglyScroll() { - scrollBy(-1, 0) - scrollBy(1, 0) - focusCenteredCard() - } - inner class OverlappingDecoration(private val overlap: Int) : ItemDecoration() { override fun getItemOffsets( outRect: Rect, |
