From dd9554dfe3fa6390b9aa40263242c791b81d72fe Mon Sep 17 00:00:00 2001 From: Quildra Date: Fri, 1 Aug 2025 13:49:36 +0100 Subject: [PATCH] feat: implement adaptive menu positioning and layout flipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .../pokegoalshelper/ui/EnhancedFloatingFAB.kt | 70 ++++++++++++++----- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt b/app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt index ac49cd0..12c17f9 100644 --- a/app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt +++ b/app/src/main/java/com/quillstudios/pokegoalshelper/ui/EnhancedFloatingFAB.kt @@ -160,13 +160,9 @@ class EnhancedFloatingFAB( PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.START - // Position menu to the left of FAB with proper spacing - val menuWidth = 200 - val padding = dpToPx(8) // 8dp padding between menu and FAB - - // 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 + // Initial positioning will be updated by updateMenuPosition() when menu is shown + x = 0 + y = this@EnhancedFloatingFAB.currentY } // Add main FAB window @@ -217,9 +213,17 @@ class EnhancedFloatingFAB( // Clear existing items container.removeAllViews() - // Menu items should align to the right edge of the menu container - // so they appear closest to the main FAB - (container as LinearLayout).gravity = Gravity.TOP or Gravity.END + // Determine which side of screen the FAB is on for layout decisions + val screenWidth = getScreenSize().first + 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 val menuItems = listOf( @@ -245,13 +249,13 @@ class EnhancedFloatingFAB( ) menuItems.forEach { item -> - val menuRow = createMenuRow(item) + val menuRow = createMenuRow(item, isOnLeftSide) container.addView(menuRow) } } } - private fun createMenuRow(item: MenuItemData): LinearLayout { + private fun createMenuRow(item: MenuItemData, isOnLeftSide: Boolean): LinearLayout { return LinearLayout(context).apply { orientation = LinearLayout.HORIZONTAL gravity = Gravity.CENTER_VERTICAL @@ -267,7 +271,12 @@ class EnhancedFloatingFAB( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ).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) - addView(label) - addView(miniFAB) + // 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(miniFAB) + Log.d(TAG, "Menu row: LABEL → FAB (FAB on right side)") + } layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, @@ -405,12 +423,26 @@ class EnhancedFloatingFAB( ) val menuWidth = menu.measuredWidth val padding = dpToPx(8) // 8dp padding between menu and FAB + val screenWidth = getScreenSize().first + val fabCenterX = currentX + dpToPx(FAB_SIZE_DP) / 2 + + // 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) + Log.d(TAG, "FAB on right side - menu to left at x=${params.x}") + } - // Menu's right edge should align with FAB's left edge minus padding - params.x = maxOf(0, currentX - menuWidth - padding) 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 { windowManager?.updateViewLayout(menu, params) } catch (e: Exception) {