From dfb066eebbdb049a35a85b9986f30b5957461320 Mon Sep 17 00:00:00 2001 From: John Trujillo Date: Tue, 7 Apr 2026 17:04:43 -0500 Subject: [PATCH] fix(tooltip): prevent focus stealing by adding requestFocus parameter Extracts hover delay constant and disables focus request for editor action tooltips. --- .../editor/EditorHandlerActivity.kt | 3 ++- .../androidide/ui/ProjectActionsToolbar.kt | 7 +++-- .../androidide/idetooltips/ToolTipManager.kt | 26 +++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt index 4b0b9d3400..1d86a1db28 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt @@ -450,7 +450,8 @@ open class EditorHandlerActivity : TooltipManager.showIdeCategoryTooltip( context = this@EditorHandlerActivity, anchorView = anchor, - tag = action.retrieveTooltipTag(false) + tag = action.retrieveTooltipTag(false), + requestFocus = false, ) }, onHoverExit = { diff --git a/common/src/main/java/com/itsaky/androidide/ui/ProjectActionsToolbar.kt b/common/src/main/java/com/itsaky/androidide/ui/ProjectActionsToolbar.kt index ea3ea3a905..9ee26c218b 100644 --- a/common/src/main/java/com/itsaky/androidide/ui/ProjectActionsToolbar.kt +++ b/common/src/main/java/com/itsaky/androidide/ui/ProjectActionsToolbar.kt @@ -20,6 +20,10 @@ class ProjectActionsToolbar @JvmOverloads constructor( var onNavIconLongClick: (() -> Unit)? = null ) : MaterialToolbar(context, attrs) { + companion object { + private const val TOOLTIP_HOVER_SHOW_DELAY_MS = 600L + } + init { // Navigation icon is no longer used in ProjectActionsToolbar // It's now handled by the title toolbar @@ -71,7 +75,7 @@ class ProjectActionsToolbar @JvmOverloads constructor( MotionEvent.ACTION_HOVER_ENTER -> { hoverRunnable?.let { view.removeCallbacks(it) } hoverRunnable = Runnable { onHover?.invoke(view) } - view.postDelayed(hoverRunnable, 600L) + view.postDelayed(hoverRunnable, TOOLTIP_HOVER_SHOW_DELAY_MS) } MotionEvent.ACTION_HOVER_EXIT -> { hoverRunnable?.let { view.removeCallbacks(it) } @@ -102,4 +106,3 @@ class ProjectActionsToolbar @JvmOverloads constructor( this.onNavIconLongClick = listener } } - diff --git a/idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt b/idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt index 8180003054..6b00659065 100644 --- a/idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt +++ b/idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt @@ -180,17 +180,29 @@ object TooltipManager { // Displays a tooltip for category [TooltipCategory.CATEGORY_IDE] in a particular context // (An Activity, Fragment, Dialog etc) - fun showIdeCategoryTooltip(context: Context, anchorView: View, tag: String) { + fun showIdeCategoryTooltip( + context: Context, + anchorView: View, + tag: String, + requestFocus: Boolean = true, + ) { showTooltip( context = context, anchorView = anchorView, category = TooltipCategory.CATEGORY_IDE, - tag = tag + tag = tag, + requestFocus = requestFocus, ) } // Displays a tooltip in a particular context with a specific category - fun showTooltip(context: Context, anchorView: View, category: String, tag: String) { + fun showTooltip( + context: Context, + anchorView: View, + category: String, + tag: String, + requestFocus: Boolean = true, + ) { CoroutineScope(Dispatchers.Main).launch { val tooltipItem = getTooltip( context, @@ -203,6 +215,7 @@ object TooltipManager { anchorView = anchorView, level = 0, tooltipItem = tooltipItem, + requestFocus = requestFocus, onHelpLinkClicked = { context, url, title -> val intent = Intent(context, HelpActivity::class.java).apply { @@ -229,6 +242,7 @@ object TooltipManager { anchorView: View, level: Int, tooltipItem: IDETooltipItem, + requestFocus: Boolean, onHelpLinkClicked: (context: Context, url: String, title: String) -> Unit ) { setupAndShowTooltipPopup( @@ -236,13 +250,14 @@ object TooltipManager { anchorView = anchorView, level = level, tooltipItem = tooltipItem, + requestFocus = requestFocus, onActionButtonClick = { popupWindow, urlContent -> popupWindow.dismiss() onHelpLinkClicked(context, urlContent.first, urlContent.second) }, onSeeMoreClicked = { popupWindow, nextLevel, item -> popupWindow.dismiss() - showTooltipPopup(context, anchorView, nextLevel, item, onHelpLinkClicked) + showTooltipPopup(context, anchorView, nextLevel, item, requestFocus, onHelpLinkClicked) } ) } @@ -279,6 +294,7 @@ object TooltipManager { anchorView: View, level: Int, tooltipItem: IDETooltipItem, + requestFocus: Boolean, onActionButtonClick: (popupWindow: PopupWindow, url: Pair) -> Unit, onSeeMoreClicked: (popupWindow: PopupWindow, nextLevel: Int, tooltipItem: IDETooltipItem) -> Unit, ) { @@ -394,7 +410,7 @@ object TooltipManager { } } - popupWindow.isFocusable = true + popupWindow.isFocusable = requestFocus popupWindow.isOutsideTouchable = true if (anchorView.isInOverlayWindow()) { showOverlayTooltip(popupWindow, popupView, anchorView)