c11fa0744b12d0a369c0b73722afff9ced9764c5
chmalee
  Thu Nov 30 13:53:31 2023 -0800
Fix over correction of timer controlling whether a second tooltip should show up. Make code for determining y position of tooltip aware of which direction the mouse enters from so as not to obscure the element to be moused over, refs Max email

diff --git src/hg/js/utils.js src/hg/js/utils.js
index 4b07a5e..1c3500b 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -3863,37 +3863,41 @@
     /* The actual mouseover positioning function.
     * refEl is an already existing element with coords that we use to position popUpEl.
     * popUpEl will try to be as close to the right/above the refEl, except when:
     *     it would extend past the screen in which case it would go left/below appropriately.
     *     the refEl takes up the whole screen, in which case we can cover the refEl
     *     with no consequence */
     rect = boundingRect(refEl);
     refX = rect.x; refY = rect.y;
     refWidth = rect.width; refHeight = rect.height;
     refRight = rect.right; refLeft = rect.left;
     refTop = rect.top; refBottom = rect.bottom;
     let windowWidth = window.innerWidth;
     let windowHeight = window.innerHeight;
 
     // figure out how large the mouseover will be
-    // use firstChild because popUpEl should be a div with a sole child in it
-    let popUpRect = popUpEl.firstChild.getBoundingClientRect();
+    let popUpRect = popUpEl.getBoundingClientRect();
     // position the popUp to the right and above the cursor by default
+    // tricky: when the mouse enters the element from the top, we want the tooltip
+    // relatively close to the element itself, because the mouse will already be
+    // on top of it, leaving it clickable or interactable. But if we are entering
+    // the element from the bottom, if we position the tooltip close to the mouse,
+    // we obscure the element itself, so we need to leave a bit of extra room
     let topOffset;
-    if (refEl.coords !== undefined && refEl.coords.length > 0 && refEl.coords.split(",").length == 4) {
-        // boundingRect has determined exactly as close as we can get to the item without covering
-        topOffset = refTop - popUpRect.height;
+    if (Math.abs(mouseY - refBottom) < Math.abs(mouseY - refTop)) {
+        // just use the mouseY position for placement, the -15 accounts for enough room
+        topOffset = mouseY - window.scrollY - popUpRect.height - 15;
     } else {
         // just use the mouseY position for placement, the -5 accounts for cursor size
         topOffset = mouseY - window.scrollY - popUpRect.height - 5;
     }
     let leftOffset = mouseX + 15; // add 15 for large cursor sizes
 
     // first case, refEl takes the whole width of the image, so not a big deal to cover some of it
     // this is most common for the track labels
     if (mouseX + popUpRect.width >= (windowWidth - 25)) {
         // move to the left
         leftOffset = mouseX - popUpRect.width;
     }
     // or the mouse is on the right third of the screen
     if (mouseX > (windowWidth* 0.66)) {
         // move to the left
@@ -3963,31 +3967,31 @@
 }
 
 function mousemoveTimerHelper(e) {
     /* user has moved the mouse and then stopped moving for long enough. */
     clearTimeout(mousemoveTimer);
     canShowNewMouseover = true;
     mousemoveTimer = undefined;
 }
 
 function mousemoveHelper(e) {
     /* Helper function for deciding whether to keep a tooltip visible upon a mousemove event */
     if (mousemoveTimer === undefined) {
         // if we are over another mouseable element we want to show that one instead
         // use this timer to do so
         let callback = mousemoveTimerHelper.bind(mouseoverContainer);
-        mousemoveTimer = setTimeout(callback, 150, e);
+        mousemoveTimer = setTimeout(callback, 300, e);
     }
 
     if (mousedNewItem && canShowNewMouseover && !mouseIsOverPopup(e, this, 0)) {
         // the !mouseIsOverPopup() check catches the corner case where the mouse
         // was moved slowly enough to the popup to set off the mousemoveTimer, but
         // is now over the pop up itself
         hideMouseoverText(this);
         mousemoveController.abort();
     } else {
         if (mouseIsOverPopup(e, this) || mouseIsOverItem(e, this)) {
             // the mouse is in the general area of the popup/item
             // the mouse needs to stop moving and then the flag will
             // get set by the mousemoveTimer
             canShowNewMouseover = false;
             return;