mirror of
https://github.com/cmclark00/mintris.git
synced 2025-05-18 05:15:20 +01:00
Fix customization menu navigation and layout issues: - Fix D-pad navigation between theme and block skin selectors - Prevent word wrapping in customization title - Add ThemeManager for theme color management - Improve menu item highlighting and focus handling
This commit is contained in:
parent
a8f095cf42
commit
71a8efff91
6 changed files with 555 additions and 114 deletions
|
@ -105,6 +105,10 @@ class MainActivity : AppCompatActivity(),
|
||||||
// Track currently selected menu item in pause menu for gamepad navigation
|
// Track currently selected menu item in pause menu for gamepad navigation
|
||||||
private var currentMenuSelection = 0
|
private var currentMenuSelection = 0
|
||||||
private val pauseMenuItems = mutableListOf<View>()
|
private val pauseMenuItems = mutableListOf<View>()
|
||||||
|
private val customizationMenuItems = mutableListOf<View>()
|
||||||
|
|
||||||
|
// Add these new properties at the class level
|
||||||
|
private var currentCustomizationMenuSelection = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// Register activity result launcher for high score entry
|
// Register activity result launcher for high score entry
|
||||||
|
@ -130,8 +134,8 @@ class MainActivity : AppCompatActivity(),
|
||||||
highScoreManager = HighScoreManager(this)
|
highScoreManager = HighScoreManager(this)
|
||||||
statsManager = StatsManager(this)
|
statsManager = StatsManager(this)
|
||||||
progressionManager = PlayerProgressionManager(this)
|
progressionManager = PlayerProgressionManager(this)
|
||||||
themeSelector = binding.themeSelector
|
themeSelector = binding.customizationThemeSelector!!
|
||||||
blockSkinSelector = binding.blockSkinSelector
|
blockSkinSelector = binding.customizationBlockSkinSelector!!
|
||||||
pauseMenuScrollView = binding.pauseMenuScrollView
|
pauseMenuScrollView = binding.pauseMenuScrollView
|
||||||
|
|
||||||
// Initialize gamepad controller
|
// Initialize gamepad controller
|
||||||
|
@ -190,44 +194,6 @@ class MainActivity : AppCompatActivity(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up landscape mode theme selector if available
|
|
||||||
val inPauseThemeSelector = findViewById<ThemeSelector>(R.id.inPauseThemeSelector)
|
|
||||||
inPauseThemeSelector?.onThemeSelected = { themeId: String ->
|
|
||||||
// Apply the new theme
|
|
||||||
applyTheme(themeId)
|
|
||||||
|
|
||||||
// Provide haptic feedback
|
|
||||||
gameHaptics.vibrateForPieceLock()
|
|
||||||
|
|
||||||
// Refresh the pause menu
|
|
||||||
showPauseMenu()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up block skin selector
|
|
||||||
blockSkinSelector.onBlockSkinSelected = { skinId: String ->
|
|
||||||
// Apply the new block skin
|
|
||||||
gameView.setBlockSkin(skinId)
|
|
||||||
|
|
||||||
// Save the selection
|
|
||||||
progressionManager.setSelectedBlockSkin(skinId)
|
|
||||||
|
|
||||||
// Provide haptic feedback
|
|
||||||
gameHaptics.vibrateForPieceLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up landscape mode block skin selector if available
|
|
||||||
val inPauseBlockSkinSelector = findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector)
|
|
||||||
inPauseBlockSkinSelector?.onBlockSkinSelected = { skinId: String ->
|
|
||||||
// Apply the new block skin
|
|
||||||
gameView.setBlockSkin(skinId)
|
|
||||||
|
|
||||||
// Save the selection
|
|
||||||
progressionManager.setSelectedBlockSkin(skinId)
|
|
||||||
|
|
||||||
// Provide haptic feedback
|
|
||||||
gameHaptics.vibrateForPieceLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up title screen
|
// Set up title screen
|
||||||
titleScreen.onStartGame = {
|
titleScreen.onStartGame = {
|
||||||
titleScreen.visibility = View.GONE
|
titleScreen.visibility = View.GONE
|
||||||
|
@ -405,6 +371,18 @@ class MainActivity : AppCompatActivity(),
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up customization button
|
||||||
|
binding.customizationButton.setOnClickListener {
|
||||||
|
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
|
||||||
|
showCustomizationMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up customization back button
|
||||||
|
binding.customizationBackButton.setOnClickListener {
|
||||||
|
gameHaptics.performHapticFeedback(it, HapticFeedbackConstants.VIRTUAL_KEY)
|
||||||
|
hideCustomizationMenu()
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize level selector
|
// Initialize level selector
|
||||||
updateLevelSelector()
|
updateLevelSelector()
|
||||||
|
|
||||||
|
@ -493,12 +471,6 @@ class MainActivity : AppCompatActivity(),
|
||||||
Log.d("MainActivity", "Triggering game over animation")
|
Log.d("MainActivity", "Triggering game over animation")
|
||||||
gameView.startGameOverAnimation()
|
gameView.startGameOverAnimation()
|
||||||
|
|
||||||
// Hide game UI elements in landscape mode
|
|
||||||
if (resources.configuration.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE) {
|
|
||||||
binding.leftControlsPanel?.visibility = View.GONE
|
|
||||||
binding.rightControlsPanel?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a moment before showing progression screen to let animation be visible
|
// Wait a moment before showing progression screen to let animation be visible
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
// Show progression screen first with XP animation
|
// Show progression screen first with XP animation
|
||||||
|
@ -549,11 +521,119 @@ class MainActivity : AppCompatActivity(),
|
||||||
progressionScreen.visibility = View.GONE
|
progressionScreen.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show customization menu
|
||||||
|
*/
|
||||||
|
private fun showCustomizationMenu() {
|
||||||
|
binding.customizationContainer.visibility = View.VISIBLE
|
||||||
|
binding.pauseContainer.visibility = View.GONE
|
||||||
|
|
||||||
|
// Update theme colors
|
||||||
|
val themeColors = ThemeManager.getThemeColors()
|
||||||
|
binding.customizationContainer.setBackgroundColor(themeColors.background)
|
||||||
|
|
||||||
|
// Update level badge
|
||||||
|
binding.customizationLevelBadge.setLevel(progressionManager.getPlayerLevel())
|
||||||
|
binding.customizationLevelBadge.setThemeColor(themeColors.accent)
|
||||||
|
|
||||||
|
// Apply theme colors to text
|
||||||
|
binding.customizationTitle.setTextColor(themeColors.text.toInt())
|
||||||
|
|
||||||
|
// Update theme and block skin selectors
|
||||||
|
binding.customizationThemeSelector.updateThemes(
|
||||||
|
progressionManager.getUnlockedThemes(),
|
||||||
|
currentTheme
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up block skin selector callback
|
||||||
|
binding.customizationBlockSkinSelector.onBlockSkinSelected = { selectedSkin ->
|
||||||
|
// Update the game view with the selected block skin
|
||||||
|
gameView.setBlockSkin(selectedSkin)
|
||||||
|
// Save the selection
|
||||||
|
progressionManager.setSelectedBlockSkin(selectedSkin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update block skin selector with current selection
|
||||||
|
binding.customizationBlockSkinSelector.updateBlockSkins(
|
||||||
|
progressionManager.getUnlockedBlocks(),
|
||||||
|
gameView.getCurrentBlockSkin(),
|
||||||
|
progressionManager.getPlayerLevel()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reset scroll position
|
||||||
|
binding.customizationMenuScrollView.scrollTo(0, 0)
|
||||||
|
|
||||||
|
// Initialize customization menu items
|
||||||
|
customizationMenuItems.clear()
|
||||||
|
customizationMenuItems.addAll(listOf(
|
||||||
|
binding.customizationThemeSelector,
|
||||||
|
binding.customizationBlockSkinSelector,
|
||||||
|
binding.customizationBackButton
|
||||||
|
).filterNotNull().filter { it.visibility == View.VISIBLE })
|
||||||
|
|
||||||
|
// Set initial selection and highlight
|
||||||
|
currentCustomizationMenuSelection = 0
|
||||||
|
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
|
||||||
|
|
||||||
|
// Set initial focus to theme selector
|
||||||
|
binding.customizationThemeSelector.requestFocus()
|
||||||
|
|
||||||
|
// Initialize customization menu navigation
|
||||||
|
initCustomizationMenuNavigation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initCustomizationMenuNavigation() {
|
||||||
|
customizationMenuItems.clear()
|
||||||
|
|
||||||
|
// Add items in order
|
||||||
|
customizationMenuItems.addAll(listOf(
|
||||||
|
binding.customizationThemeSelector,
|
||||||
|
binding.customizationBlockSkinSelector,
|
||||||
|
binding.customizationBackButton
|
||||||
|
).filterNotNull().filter { it.visibility == View.VISIBLE })
|
||||||
|
|
||||||
|
// Set up focus change listener for scrolling
|
||||||
|
binding.customizationMenuScrollView.setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
if (hasFocus) {
|
||||||
|
// When scroll view gets focus, scroll to focused item
|
||||||
|
val focusedView = currentFocus
|
||||||
|
if (focusedView != null) {
|
||||||
|
val scrollView = binding.customizationMenuScrollView
|
||||||
|
val itemTop = focusedView.top
|
||||||
|
val scrollViewHeight = scrollView.height
|
||||||
|
|
||||||
|
// Calculate scroll position to center the focused item
|
||||||
|
val scrollY = itemTop - (scrollViewHeight / 2) + (focusedView.height / 2)
|
||||||
|
scrollView.smoothScrollTo(0, scrollY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up focus handling between items
|
||||||
|
customizationMenuItems.forEachIndexed { index, item ->
|
||||||
|
item.setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
if (hasFocus) {
|
||||||
|
currentCustomizationMenuSelection = index
|
||||||
|
highlightCustomizationMenuItem(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide customization menu
|
||||||
|
*/
|
||||||
|
private fun hideCustomizationMenu() {
|
||||||
|
binding.customizationContainer.visibility = View.GONE
|
||||||
|
binding.pauseContainer.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show settings menu
|
* Show settings menu
|
||||||
*/
|
*/
|
||||||
private fun showPauseMenu() {
|
private fun showPauseMenu() {
|
||||||
binding.pauseContainer.visibility = View.VISIBLE
|
binding.pauseContainer.visibility = View.VISIBLE
|
||||||
|
binding.customizationContainer.visibility = View.GONE
|
||||||
|
|
||||||
// Set button visibility based on game state
|
// Set button visibility based on game state
|
||||||
if (gameView.isPaused) {
|
if (gameView.isPaused) {
|
||||||
|
@ -589,6 +669,7 @@ class MainActivity : AppCompatActivity(),
|
||||||
binding.pauseLevelDownButton?.setTextColor(themeColor) // Safe call
|
binding.pauseLevelDownButton?.setTextColor(themeColor) // Safe call
|
||||||
binding.settingsButton?.setTextColor(themeColor) // Safe call for sound toggle button text
|
binding.settingsButton?.setTextColor(themeColor) // Safe call for sound toggle button text
|
||||||
binding.musicToggle?.setColorFilter(themeColor) // Safe call
|
binding.musicToggle?.setColorFilter(themeColor) // Safe call
|
||||||
|
binding.customizationButton?.setTextColor(themeColor) // Safe call
|
||||||
|
|
||||||
// Apply theme colors to text elements (using safe calls)
|
// Apply theme colors to text elements (using safe calls)
|
||||||
binding.settingsTitle?.setTextColor(themeColor)
|
binding.settingsTitle?.setTextColor(themeColor)
|
||||||
|
@ -596,30 +677,8 @@ class MainActivity : AppCompatActivity(),
|
||||||
binding.musicText?.setTextColor(themeColor)
|
binding.musicText?.setTextColor(themeColor)
|
||||||
binding.pauseLevelText?.setTextColor(themeColor)
|
binding.pauseLevelText?.setTextColor(themeColor)
|
||||||
|
|
||||||
// Apply theme to specific labels in portrait/landscape - RELY ON HELPER BELOW
|
|
||||||
if (isLandscape) {
|
|
||||||
// findViewById<TextView>(R.id.selectThemeTextLandscape)?.setTextColor(themeColor) // REMOVED
|
|
||||||
// findViewById<TextView>(R.id.selectBlockSkinTextLandscape)?.setTextColor(themeColor) // REMOVED
|
|
||||||
// Also color general text views in the landscape container, avoiding selectors
|
|
||||||
applyThemeColorToTextViews(findViewById(R.id.pauseContainer), themeColor)
|
|
||||||
} else {
|
|
||||||
// binding.selectThemeText?.setTextColor(themeColor) // REMOVED
|
|
||||||
// binding.selectBlockSkinText?.setTextColor(themeColor) // REMOVED
|
|
||||||
// Apply to portrait container as well if needed (assuming root or specific container)
|
// Apply to portrait container as well if needed (assuming root or specific container)
|
||||||
applyThemeColorToTextViews(binding.pauseContainer, themeColor) // Apply to main container
|
applyThemeColorToTextViews(binding.pauseContainer, themeColor) // Apply to main container
|
||||||
}
|
|
||||||
|
|
||||||
// Update theme selector instances
|
|
||||||
val currentUnlockedThemes = progressionManager.getUnlockedThemes()
|
|
||||||
binding.themeSelector.updateThemes(currentUnlockedThemes, currentTheme)
|
|
||||||
findViewById<ThemeSelector>(R.id.inPauseThemeSelector)?.updateThemes(currentUnlockedThemes, currentTheme)
|
|
||||||
|
|
||||||
// Update block skin selector instances
|
|
||||||
val currentBlockSkin = gameView.getCurrentBlockSkin()
|
|
||||||
val currentUnlockedBlocks = progressionManager.getUnlockedBlocks()
|
|
||||||
val currentLevel = progressionManager.getPlayerLevel()
|
|
||||||
binding.blockSkinSelector.updateBlockSkins(currentUnlockedBlocks, currentBlockSkin, currentLevel)
|
|
||||||
findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector)?.updateBlockSkins(currentUnlockedBlocks, currentBlockSkin, currentLevel)
|
|
||||||
|
|
||||||
// Reset scroll position
|
// Reset scroll position
|
||||||
binding.pauseMenuScrollView?.scrollTo(0, 0)
|
binding.pauseMenuScrollView?.scrollTo(0, 0)
|
||||||
|
@ -913,7 +972,7 @@ class MainActivity : AppCompatActivity(),
|
||||||
* Update the theme selector with unlocked themes
|
* Update the theme selector with unlocked themes
|
||||||
*/
|
*/
|
||||||
private fun updateThemeSelector() {
|
private fun updateThemeSelector() {
|
||||||
binding.themeSelector.updateThemes(
|
binding.customizationThemeSelector?.updateThemes(
|
||||||
unlockedThemes = progressionManager.getUnlockedThemes(),
|
unlockedThemes = progressionManager.getUnlockedThemes(),
|
||||||
currentTheme = currentTheme
|
currentTheme = currentTheme
|
||||||
)
|
)
|
||||||
|
@ -1224,8 +1283,10 @@ class MainActivity : AppCompatActivity(),
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||||
moveMenuSelectionUp()
|
moveMenuSelectionUp()
|
||||||
// Ensure the selected item is visible
|
|
||||||
scrollToSelectedItem()
|
scrollToSelectedItem()
|
||||||
|
} else if (binding.customizationContainer.visibility == View.VISIBLE) {
|
||||||
|
moveCustomizationMenuSelectionUp()
|
||||||
|
scrollToSelectedCustomizationItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1234,8 +1295,10 @@ class MainActivity : AppCompatActivity(),
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
if (binding.pauseContainer.visibility == View.VISIBLE) {
|
||||||
moveMenuSelectionDown()
|
moveMenuSelectionDown()
|
||||||
// Ensure the selected item is visible
|
|
||||||
scrollToSelectedItem()
|
scrollToSelectedItem()
|
||||||
|
} else if (binding.customizationContainer.visibility == View.VISIBLE) {
|
||||||
|
moveCustomizationMenuSelectionDown()
|
||||||
|
scrollToSelectedCustomizationItem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1248,6 +1311,12 @@ class MainActivity : AppCompatActivity(),
|
||||||
is ThemeSelector -> selectedItem.focusPreviousItem()
|
is ThemeSelector -> selectedItem.focusPreviousItem()
|
||||||
is BlockSkinSelector -> selectedItem.focusPreviousItem()
|
is BlockSkinSelector -> selectedItem.focusPreviousItem()
|
||||||
}
|
}
|
||||||
|
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
|
||||||
|
val selectedItem = customizationMenuItems.getOrNull(currentCustomizationMenuSelection)
|
||||||
|
when (selectedItem) {
|
||||||
|
is ThemeSelector -> selectedItem.focusPreviousItem()
|
||||||
|
is BlockSkinSelector -> selectedItem.focusPreviousItem()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1260,6 +1329,12 @@ class MainActivity : AppCompatActivity(),
|
||||||
is ThemeSelector -> selectedItem.focusNextItem()
|
is ThemeSelector -> selectedItem.focusNextItem()
|
||||||
is BlockSkinSelector -> selectedItem.focusNextItem()
|
is BlockSkinSelector -> selectedItem.focusNextItem()
|
||||||
}
|
}
|
||||||
|
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
|
||||||
|
val selectedItem = customizationMenuItems.getOrNull(currentCustomizationMenuSelection)
|
||||||
|
when (selectedItem) {
|
||||||
|
is ThemeSelector -> selectedItem.focusNextItem()
|
||||||
|
is BlockSkinSelector -> selectedItem.focusNextItem()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1267,7 +1342,9 @@ class MainActivity : AppCompatActivity(),
|
||||||
override fun onMenuSelect() {
|
override fun onMenuSelect() {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (binding.pauseContainer.visibility == View.VISIBLE && pauseMenuItems.isNotEmpty()) {
|
if (binding.pauseContainer.visibility == View.VISIBLE && pauseMenuItems.isNotEmpty()) {
|
||||||
activateSelectedMenuItem() // Handles selectors and other items
|
activateSelectedMenuItem()
|
||||||
|
} else if (binding.customizationContainer.visibility == View.VISIBLE && customizationMenuItems.isNotEmpty()) {
|
||||||
|
activateSelectedCustomizationMenuItem()
|
||||||
} else if (titleScreen.visibility == View.VISIBLE) {
|
} else if (titleScreen.visibility == View.VISIBLE) {
|
||||||
titleScreen.performClick()
|
titleScreen.performClick()
|
||||||
} else if (progressionScreen.visibility == View.VISIBLE) {
|
} else if (progressionScreen.visibility == View.VISIBLE) {
|
||||||
|
@ -1295,12 +1372,9 @@ class MainActivity : AppCompatActivity(),
|
||||||
orderedViews.add(binding.resumeButton)
|
orderedViews.add(binding.resumeButton)
|
||||||
}
|
}
|
||||||
if (binding.pauseStartButton.visibility == View.VISIBLE) {
|
if (binding.pauseStartButton.visibility == View.VISIBLE) {
|
||||||
// In pause menu, the 'Start New Game' button might be visible if paused from title?
|
|
||||||
// Let's assume pauseRestartButton is the primary one during active pause.
|
|
||||||
// Keep the logic simple: add if visible.
|
|
||||||
orderedViews.add(binding.pauseStartButton)
|
orderedViews.add(binding.pauseStartButton)
|
||||||
}
|
}
|
||||||
orderedViews.add(binding.pauseRestartButton) // Always add restart
|
orderedViews.add(binding.pauseRestartButton)
|
||||||
|
|
||||||
// Group 2: Stats and Scoring
|
// Group 2: Stats and Scoring
|
||||||
orderedViews.add(binding.highScoresButton)
|
orderedViews.add(binding.highScoresButton)
|
||||||
|
@ -1314,24 +1388,11 @@ class MainActivity : AppCompatActivity(),
|
||||||
orderedViews.add(binding.settingsButton) // Sound toggle (Button)
|
orderedViews.add(binding.settingsButton) // Sound toggle (Button)
|
||||||
orderedViews.add(binding.musicToggle) // Music toggle (ImageButton)
|
orderedViews.add(binding.musicToggle) // Music toggle (ImageButton)
|
||||||
|
|
||||||
// Group 5: Visual customization (Theme and Block Skins)
|
// Group 5: Customization Menu
|
||||||
// Choose the correct instance based on orientation and visibility
|
orderedViews.add(binding.customizationButton) // Add the customization button
|
||||||
val themeSelectorInstance = if (isLandscape) findViewById<ThemeSelector>(R.id.inPauseThemeSelector) else binding.themeSelector
|
|
||||||
val blockSkinSelectorInstance = if (isLandscape) findViewById<BlockSkinSelector>(R.id.inPauseBlockSkinSelector) else binding.blockSkinSelector
|
|
||||||
|
|
||||||
// Only add the selectors if they are actually visible in the current layout
|
|
||||||
if (themeSelectorInstance?.visibility == View.VISIBLE) {
|
|
||||||
orderedViews.add(themeSelectorInstance)
|
|
||||||
}
|
|
||||||
if (blockSkinSelectorInstance?.visibility == View.VISIBLE) {
|
|
||||||
orderedViews.add(blockSkinSelectorInstance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all non-null, visible items from the defined order to the final navigation list
|
// Add all non-null, visible items from the defined order to the final navigation list
|
||||||
pauseMenuItems.addAll(orderedViews.filterNotNull().filter { it.visibility == View.VISIBLE })
|
pauseMenuItems.addAll(orderedViews.filterNotNull().filter { it.visibility == View.VISIBLE })
|
||||||
|
|
||||||
// Reset selection index (will be set and highlighted in showPauseMenu)
|
|
||||||
// currentMenuSelection = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1609,4 +1670,182 @@ class MainActivity : AppCompatActivity(),
|
||||||
// If no landscape ScrollView is found, fall back to the default one
|
// If no landscape ScrollView is found, fall back to the default one
|
||||||
return binding.pauseMenuScrollView
|
return binding.pauseMenuScrollView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add these new methods for customization menu navigation
|
||||||
|
private fun moveCustomizationMenuSelectionUp() {
|
||||||
|
if (customizationMenuItems.isEmpty()) return
|
||||||
|
|
||||||
|
val previousSelection = currentCustomizationMenuSelection
|
||||||
|
currentCustomizationMenuSelection = (currentCustomizationMenuSelection - 1)
|
||||||
|
|
||||||
|
if (currentCustomizationMenuSelection < 0) {
|
||||||
|
currentCustomizationMenuSelection = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousSelection != currentCustomizationMenuSelection) {
|
||||||
|
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moveCustomizationMenuSelectionDown() {
|
||||||
|
if (customizationMenuItems.isEmpty()) return
|
||||||
|
|
||||||
|
val previousSelection = currentCustomizationMenuSelection
|
||||||
|
currentCustomizationMenuSelection = (currentCustomizationMenuSelection + 1)
|
||||||
|
|
||||||
|
if (currentCustomizationMenuSelection >= customizationMenuItems.size) {
|
||||||
|
currentCustomizationMenuSelection = customizationMenuItems.size - 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousSelection != currentCustomizationMenuSelection) {
|
||||||
|
highlightCustomizationMenuItem(currentCustomizationMenuSelection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun highlightCustomizationMenuItem(index: Int) {
|
||||||
|
if (customizationMenuItems.isEmpty() || index < 0 || index >= customizationMenuItems.size) {
|
||||||
|
Log.w(TAG, "highlightCustomizationMenuItem called with invalid index: $index or empty items")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val themeColor = getThemeColor(currentTheme)
|
||||||
|
val highlightBorderColor = themeColor
|
||||||
|
|
||||||
|
customizationMenuItems.forEachIndexed { i, item ->
|
||||||
|
val isSelected = (i == index)
|
||||||
|
|
||||||
|
item.alpha = if (isSelected) 1.0f else 0.7f
|
||||||
|
if (item is Button || item is ImageButton) {
|
||||||
|
item.scaleX = 1.0f
|
||||||
|
item.scaleY = 1.0f
|
||||||
|
}
|
||||||
|
|
||||||
|
when (item) {
|
||||||
|
is Button -> item.background = ColorDrawable(Color.TRANSPARENT)
|
||||||
|
is ImageButton -> item.background = null
|
||||||
|
is ThemeSelector -> {
|
||||||
|
item.background = ColorDrawable(Color.TRANSPARENT)
|
||||||
|
item.setHasFocus(false)
|
||||||
|
}
|
||||||
|
is BlockSkinSelector -> {
|
||||||
|
item.background = ColorDrawable(Color.TRANSPARENT)
|
||||||
|
item.setHasFocus(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
if (item is Button || item is ImageButton) {
|
||||||
|
item.scaleX = 1.1f
|
||||||
|
item.scaleY = 1.1f
|
||||||
|
}
|
||||||
|
|
||||||
|
val borderWidth = 4
|
||||||
|
val cornerRadius = 12f
|
||||||
|
|
||||||
|
when (item) {
|
||||||
|
is Button -> {
|
||||||
|
ContextCompat.getDrawable(this, R.drawable.menu_item_selected)?.mutate()?.let {
|
||||||
|
if (it is GradientDrawable) {
|
||||||
|
it.setStroke(borderWidth, highlightBorderColor)
|
||||||
|
item.background = it
|
||||||
|
} else {
|
||||||
|
it.setTint(highlightBorderColor)
|
||||||
|
item.background = it
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
item.background = GradientDrawable().apply {
|
||||||
|
setColor(Color.TRANSPARENT)
|
||||||
|
setStroke(borderWidth, highlightBorderColor)
|
||||||
|
setCornerRadius(cornerRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ImageButton -> {
|
||||||
|
item.background = GradientDrawable().apply {
|
||||||
|
setColor(Color.TRANSPARENT)
|
||||||
|
setStroke(borderWidth, highlightBorderColor)
|
||||||
|
setCornerRadius(cornerRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ThemeSelector -> {
|
||||||
|
item.background = GradientDrawable().apply {
|
||||||
|
setColor(Color.TRANSPARENT)
|
||||||
|
setStroke(borderWidth, highlightBorderColor)
|
||||||
|
setCornerRadius(cornerRadius)
|
||||||
|
}
|
||||||
|
item.setHasFocus(true)
|
||||||
|
}
|
||||||
|
is BlockSkinSelector -> {
|
||||||
|
item.background = GradientDrawable().apply {
|
||||||
|
setColor(Color.TRANSPARENT)
|
||||||
|
setStroke(borderWidth, highlightBorderColor)
|
||||||
|
setCornerRadius(cornerRadius)
|
||||||
|
}
|
||||||
|
item.setHasFocus(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gameHaptics.vibrateForPieceMove()
|
||||||
|
scrollToSelectedCustomizationItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scrollToSelectedCustomizationItem() {
|
||||||
|
if (customizationMenuItems.isEmpty() || currentCustomizationMenuSelection < 0 || currentCustomizationMenuSelection >= customizationMenuItems.size) return
|
||||||
|
|
||||||
|
val selectedItem = customizationMenuItems[currentCustomizationMenuSelection]
|
||||||
|
val scrollView = binding.customizationMenuScrollView
|
||||||
|
|
||||||
|
scrollView.post {
|
||||||
|
try {
|
||||||
|
val scrollBounds = Rect()
|
||||||
|
scrollView.getHitRect(scrollBounds)
|
||||||
|
|
||||||
|
val itemLocation = IntArray(2)
|
||||||
|
selectedItem.getLocationOnScreen(itemLocation)
|
||||||
|
|
||||||
|
val scrollLocation = IntArray(2)
|
||||||
|
scrollView.getLocationOnScreen(scrollLocation)
|
||||||
|
|
||||||
|
val itemY = itemLocation[1] - scrollLocation[1]
|
||||||
|
val itemTop = itemY
|
||||||
|
val itemBottom = itemY + selectedItem.height
|
||||||
|
val visibleHeight = scrollView.height
|
||||||
|
val padding = 50
|
||||||
|
val scrollY = scrollView.scrollY
|
||||||
|
|
||||||
|
if (itemTop < padding) {
|
||||||
|
scrollView.smoothScrollTo(0, scrollY + (itemTop - padding))
|
||||||
|
} else if (itemBottom > visibleHeight - padding) {
|
||||||
|
scrollView.smoothScrollTo(0, scrollY + (itemBottom - visibleHeight + padding))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error scrolling to selected customization item", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun activateSelectedCustomizationMenuItem() {
|
||||||
|
if (customizationMenuItems.isEmpty() || currentCustomizationMenuSelection < 0 || currentCustomizationMenuSelection >= customizationMenuItems.size) return
|
||||||
|
|
||||||
|
val selectedItem = customizationMenuItems[currentCustomizationMenuSelection]
|
||||||
|
gameHaptics.vibrateForPieceLock()
|
||||||
|
|
||||||
|
when (selectedItem) {
|
||||||
|
is ThemeSelector -> {
|
||||||
|
selectedItem.confirmSelection()
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({ showCustomizationMenu() }, 100)
|
||||||
|
}
|
||||||
|
is BlockSkinSelector -> {
|
||||||
|
selectedItem.confirmSelection()
|
||||||
|
// The onBlockSkinSelected callback will handle updating the game view and saving the selection
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
selectedItem.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
19
app/src/main/java/com/mintris/ThemeManager.kt
Normal file
19
app/src/main/java/com/mintris/ThemeManager.kt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.mintris
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
|
||||||
|
object ThemeManager {
|
||||||
|
data class ThemeColors(
|
||||||
|
val background: Int,
|
||||||
|
val text: Int,
|
||||||
|
val accent: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getThemeColors(): ThemeColors {
|
||||||
|
return ThemeColors(
|
||||||
|
background = Color.BLACK,
|
||||||
|
text = Color.WHITE,
|
||||||
|
accent = Color.WHITE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
6
app/src/main/res/drawable/menu_section_background.xml
Normal file
6
app/src/main/res/drawable/menu_section_background.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#80000000" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
</shape>
|
|
@ -422,21 +422,22 @@
|
||||||
android:contentDescription="@string/music" />
|
android:contentDescription="@string/music" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Theme Selector -->
|
<!-- Customization Button -->
|
||||||
<com.mintris.ui.ThemeSelector
|
<Button
|
||||||
android:id="@+id/inPauseThemeSelector"
|
android:id="@+id/customizationButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="200dp"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_marginBottom="16dp" />
|
|
||||||
|
|
||||||
<!-- Block Skin Selector -->
|
|
||||||
<com.mintris.ui.BlockSkinSelector
|
|
||||||
android:id="@+id/inPauseBlockSkinSelector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="16dp" />
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/customization"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -623,4 +624,97 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Customization Menu overlay -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/customizationContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/customizationTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/customization"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<com.mintris.ui.LevelBadge
|
||||||
|
android:id="@+id/customizationLevelBadge"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/customizationMenuScrollView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingBottom="32dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="32dp">
|
||||||
|
|
||||||
|
<!-- Theme Selector -->
|
||||||
|
<com.mintris.ui.ThemeSelector
|
||||||
|
android:id="@+id/customizationThemeSelector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
|
|
||||||
|
<!-- Block Skin Selector -->
|
||||||
|
<com.mintris.ui.BlockSkinSelector
|
||||||
|
android:id="@+id/customizationBlockSkinSelector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
|
|
||||||
|
<!-- Back Button -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/customizationBackButton"
|
||||||
|
android:layout_width="200dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/back"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -519,21 +519,103 @@
|
||||||
android:contentDescription="@string/music" />
|
android:contentDescription="@string/music" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Theme Selector -->
|
<!-- Customization Button -->
|
||||||
<com.mintris.ui.ThemeSelector
|
<Button
|
||||||
android:id="@+id/themeSelector"
|
android:id="@+id/customizationButton"
|
||||||
|
android:layout_width="200dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/customization"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:textAllCaps="false" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Customization Menu overlay -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/customizationContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/customizationTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/customization"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<com.mintris.ui.LevelBadge
|
||||||
|
android:id="@+id/customizationLevelBadge"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/customizationMenuScrollView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:fillViewport="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="32dp">
|
||||||
|
|
||||||
|
<!-- Theme Selector -->
|
||||||
|
<com.mintris.ui.ThemeSelector
|
||||||
|
android:id="@+id/customizationThemeSelector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_marginBottom="16dp" />
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
<!-- Block Skin Selector -->
|
<!-- Block Skin Selector -->
|
||||||
<com.mintris.ui.BlockSkinSelector
|
<com.mintris.ui.BlockSkinSelector
|
||||||
android:id="@+id/blockSkinSelector"
|
android:id="@+id/customizationBlockSkinSelector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<!-- Back Button -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/customizationBackButton"
|
||||||
|
android:layout_width="200dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="32dp"
|
||||||
android:layout_marginBottom="16dp" />
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/back"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:textAllCaps="false" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -47,4 +47,5 @@
|
||||||
<string name="tetrises">tetrises: %d</string>
|
<string name="tetrises">tetrises: %d</string>
|
||||||
<string name="reset_stats">reset stats</string>
|
<string name="reset_stats">reset stats</string>
|
||||||
<string name="music">music</string>
|
<string name="music">music</string>
|
||||||
|
<string name="customization">Customization</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue