Browse Source

feat: implement adaptive menu positioning and layout flipping

- Add automatic menu side detection based on FAB center position
- Menu appears on right when FAB is on left side of screen (and vice versa)
- Flip menu item layout order based on FAB position:
  - Left side: [MiniFAB][Label] (mini FAB closest to main FAB)
  - Right side: [Label][MiniFAB] (mini FAB closest to main FAB)
- Adjust container gravity and item margins for proper alignment
- Add debug logging for layout decisions

This creates a symmetrical UX where mini FABs are always closest
to the main FAB and labels extend outward, preventing off-screen
menu items regardless of FAB position.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
feature/modern-capture-ui
Quildra 5 months ago
parent
commit
dd9554dfe3
  1. 64
      app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt

64
app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt

@ -160,13 +160,9 @@ class EnhancedFloatingFAB(
PixelFormat.TRANSLUCENT PixelFormat.TRANSLUCENT
).apply { ).apply {
gravity = Gravity.TOP or Gravity.START gravity = Gravity.TOP or Gravity.START
// Position menu to the left of FAB with proper spacing // Initial positioning will be updated by updateMenuPosition() when menu is shown
val menuWidth = 200 x = 0
val padding = dpToPx(8) // 8dp padding between menu and FAB y = this@EnhancedFloatingFAB.currentY
// Menu's right edge should align with FAB's left edge minus padding
x = maxOf(0, this@EnhancedFloatingFAB.currentX - menuWidth - padding)
y = this@EnhancedFloatingFAB.currentY // Same vertical position as FAB
} }
// Add main FAB window // Add main FAB window
@ -217,9 +213,17 @@ class EnhancedFloatingFAB(
// Clear existing items // Clear existing items
container.removeAllViews() container.removeAllViews()
// Menu items should align to the right edge of the menu container // Determine which side of screen the FAB is on for layout decisions
// so they appear closest to the main FAB val screenWidth = getScreenSize().first
(container as LinearLayout).gravity = Gravity.TOP or Gravity.END val fabCenterX = currentX + dpToPx(FAB_SIZE_DP) / 2
val isOnLeftSide = fabCenterX < screenWidth / 2
// Menu items should align appropriately based on FAB position
(container as LinearLayout).gravity = if (isOnLeftSide) {
Gravity.TOP or Gravity.START // Left align when menu is on right
} else {
Gravity.TOP or Gravity.END // Right align when menu is on left
}
// Add menu items with appropriate layout // Add menu items with appropriate layout
val menuItems = listOf( val menuItems = listOf(
@ -245,13 +249,13 @@ class EnhancedFloatingFAB(
) )
menuItems.forEach { item -> menuItems.forEach { item ->
val menuRow = createMenuRow(item) val menuRow = createMenuRow(item, isOnLeftSide)
container.addView(menuRow) container.addView(menuRow)
} }
} }
} }
private fun createMenuRow(item: MenuItemData): LinearLayout { private fun createMenuRow(item: MenuItemData, isOnLeftSide: Boolean): LinearLayout {
return LinearLayout(context).apply { return LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL gravity = Gravity.CENTER_VERTICAL
@ -267,7 +271,12 @@ class EnhancedFloatingFAB(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
).apply { ).apply {
setMargins(0, 0, dpToPx(8), 0) // 8dp space between label and mini FAB // Adjust margins based on layout direction
if (isOnLeftSide) {
setMargins(dpToPx(8), 0, 0, 0) // Space on left when FAB comes first
} else {
setMargins(0, 0, dpToPx(8), 0) // Space on right when label comes first
}
} }
} }
@ -290,9 +299,18 @@ class EnhancedFloatingFAB(
) )
} }
// Always: LABEL → FAB (label on left, mini FAB on right, closest to main FAB) // Layout order depends on FAB position
if (isOnLeftSide) {
// FAB on left side: FAB → LABEL (mini FAB closest to main FAB)
addView(miniFAB)
addView(label)
Log.d(TAG, "Menu row: FAB → LABEL (FAB on left side)")
} else {
// FAB on right side: LABEL → FAB (mini FAB closest to main FAB)
addView(label) addView(label)
addView(miniFAB) addView(miniFAB)
Log.d(TAG, "Menu row: LABEL → FAB (FAB on right side)")
}
layoutParams = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
@ -405,12 +423,26 @@ class EnhancedFloatingFAB(
) )
val menuWidth = menu.measuredWidth val menuWidth = menu.measuredWidth
val padding = dpToPx(8) // 8dp padding between menu and FAB val padding = dpToPx(8) // 8dp padding between menu and FAB
val screenWidth = getScreenSize().first
val fabCenterX = currentX + dpToPx(FAB_SIZE_DP) / 2
// Menu's right edge should align with FAB's left edge minus padding // Determine if FAB is on left or right side of screen
val isOnLeftSide = fabCenterX < screenWidth / 2
if (isOnLeftSide) {
// FAB on left side - menu appears to the right
val fabRightEdge = currentX + dpToPx(FAB_SIZE_DP)
params.x = fabRightEdge + padding
Log.d(TAG, "FAB on left side - menu to right at x=${params.x}")
} else {
// FAB on right side - menu appears to the left
params.x = maxOf(0, currentX - menuWidth - padding) params.x = maxOf(0, currentX - menuWidth - padding)
Log.d(TAG, "FAB on right side - menu to left at x=${params.x}")
}
params.y = currentY // Same vertical position as FAB (top alignment) params.y = currentY // Same vertical position as FAB (top alignment)
Log.d(TAG, "Menu position: x=${params.x}, y=${params.y}, measured width=${menuWidth}, FAB at: x=$currentX, y=$currentY") Log.d(TAG, "Menu position: x=${params.x}, y=${params.y}, measured width=${menuWidth}, FAB center: x=$fabCenterX, screen width: $screenWidth")
try { try {
windowManager?.updateViewLayout(menu, params) windowManager?.updateViewLayout(menu, params)
} catch (e: Exception) { } catch (e: Exception) {

Loading…
Cancel
Save