Index: src/graph.js ================================================================== --- src/graph.js +++ src/graph.js @@ -116,10 +116,11 @@ ** over a graph node. Or -1 when the mouse is not ** over anything. */ ixActive: -1, /* The item shown in the tooltip is tx.rowinfo[ixActive]. ** ixActive is -1 if the tooltip is not visible */ nodeHover: null, /* Graph node under mouse when ixHover==-2 */ + idNodeActive: 0, /* Element ID of the graph node with the tooltip. */ posX: 0, posY: 0 /* The last mouse position. */ }; /* Functions used to control the tooltip popup and its timer */ function onKeyDown(event){ /* Hide the tooltip when ESC key pressed */ @@ -132,10 +133,11 @@ function hideGraphTooltip(){ /* Hide the tooltip */ document.removeEventListener('keydown',onKeyDown,/* useCapture == */true); stopCloseTimer(); tooltipObj.style.display = "none"; tooltipInfo.ixActive = -1; + tooltipInfo.idNodeActive = 0; } document.body.onunload = hideGraphTooltip function stopDwellTimer(){ if(tooltipInfo.idTimer!=0){ clearTimeout(tooltipInfo.idTimer); @@ -169,36 +171,11 @@ topObj.onclick = clickOnGraph topObj.ondblclick = dblclickOnGraph topObj.onmousemove = function(e) { var ix = findTxIndex(e); topObj.style.cursor = (ix<0) ? "" : "pointer" - /* Keep the already visible tooltip at a constant position, as long as the - ** mouse is over the same element. */ - if(tooltipObj.style.display != "none"){ - if(ix == tooltipInfo.ixHover) return; - } - /* The tooltip is either not visible, or the mouse is over a different - ** element, so clear the dwell timer, and record the new element id and - ** mouse position. */ - stopDwellTimer(); - if(ix >= 0){ - tooltipInfo.ixHover = ix; - tooltipInfo.posX = e.clientX; - tooltipInfo.posY = e.clientY; - stopCloseTimer(); - if(tooltipInfo.dwellTimeout>0){ - tooltipInfo.idTimer = setTimeout(function() { - tooltipInfo.idTimer = 0; - stopCloseTimer(); - showGraphTooltip(); - },tooltipInfo.dwellTimeout); - } - }else{ - /* The mouse is not over an element with a tooltip */ - tooltipInfo.ixHover = -1; - resumeCloseTimer(); - } + mouseOverGraph(e,ix,null); }; topObj.onmouseleave = function(e) { /* Hide the tooltip if the mouse is outside the "timelineTableN" element, ** and outside the tooltip. */ if(e.relatedTarget && e.relatedTarget != tooltipObj){ @@ -208,18 +185,26 @@ stopCloseTimer(); } }; function mouseOverNode(e){ /* Invoked by mousemove events over a graph node */ e.stopPropagation() - if(tooltipInfo.ixHover==-2) return - tooltipInfo.ixHover = -2 - tooltipInfo.posX = e.clientX - tooltipInfo.posY = e.clientY - tooltipInfo.nodeHover = this - stopCloseTimer(); - if(tooltipInfo.dwellTimeout>0){ - tooltipInfo.idTimer = setTimeout(function() { + mouseOverGraph(e,-2,this) + } + /* Combined mousemove handler for graph nodes and rails. */ + function mouseOverGraph(e,ix,node){ + stopDwellTimer(); // Mouse movement: reset the dwell timer. + var ownTooltip = // Check if the hovered element already has the tooltip. + (ix>=0 && ix==tooltipInfo.ixActive) || + (ix==-2 && tooltipInfo.idNodeActive==node.id); + if(ownTooltip) stopCloseTimer(); // ownTooltip: clear the close timer. + else resumeCloseTimer(); // !ownTooltip: resume the close timer. + tooltipInfo.ixHover = ix; + tooltipInfo.nodeHover = node; + tooltipInfo.posX = e.clientX; + tooltipInfo.posY = e.clientY; + if(ix!=-1 && !ownTooltip && tooltipInfo.dwellTimeout>0){ // Go dwell timer. + tooltipInfo.idTimer = setTimeout(function(){ tooltipInfo.idTimer = 0; stopCloseTimer(); showGraphTooltip(); },tooltipInfo.dwellTimeout); } @@ -605,10 +590,12 @@ dest += tx.fileDiff ? "&m&cf=" : "&m&c=" dest += encodeURIComponent(tx.rowinfo[ix].h) return dest } function clickOnGraph(e){ + stopCloseTimer(); + stopDwellTimer(); tooltipInfo.ixHover = findTxIndex(e); tooltipInfo.posX = e.clientX; tooltipInfo.posY = e.clientY; showGraphTooltip(); } @@ -624,10 +611,11 @@ html = "artifact "+h+"" }else{ html = "check-in "+h+"" } tooltipInfo.ixActive = -2; + tooltipInfo.idNodeActive = tooltipInfo.nodeHover.id; }else if( tooltipInfo.ixHover>=0 ){ ix = tooltipInfo.ixHover var br = tx.rowinfo[ix].br var dest = branchHyperlink(ix) var hbr = br.replace(/&/g, "&") @@ -635,10 +623,11 @@ .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); html = "branch "+hbr+"" tooltipInfo.ixActive = ix; + tooltipInfo.idNodeActive = 0; } if( html ){ /* Setup while hidden, to ensure proper dimensions. */ var s = getComputedStyle(document.body) if( tx.rowinfo[ix].bg.length ){ Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -870,11 +870,11 @@ @ "scrollToSelect": %d(scrollToSelect), @ "nrail": %d(pGraph->mxRail+1), @ "baseUrl": "%R", @ "dwellTimeout": %d(dwellTimeout), @ "closeTimeout": %d(closeTimeout), - @ "hashDigit": %d(hash_digits(1)), + @ "hashDigits": %d(hash_digits(1)), @ "bottomRowId": "btm-%d(iTableId)", if( pGraph->nRow==0 ){ @ "rowinfo": null }else{ @ "rowinfo": [