8e1d11e9de8d0b27b4d31169e7cf2994d3821533 hiram Mon Jan 25 13:43:14 2021 -0800 correctly measuring size of text for popUp sizing refs #21980 diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index fadcbd4..29f4f96 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -4467,30 +4467,33 @@ /////////////////////////////////////// var mouseOver = { items: {}, // items[trackName][] - data for each item in this track visible: false, // keeping track of popUp window visibility tracks: {}, // tracks[trackName] - number of data items for this track trackType: {}, // key is track name, value is track type from hgTracks jsonUrl: {}, // list of json files from hidden DIV elements maximumWidth: {}, // maximumWidth[trackName] - largest string to display popUpDelay: 200, // 0.2 second delay before popUp appears popUpTimer: null,// handle from setTimeout to use in clearTimout(popUpTimer) delayDone: true, // mouse has not left element, still receiving move evts delayInProgress: false, // true while waiting for delay timer mostRecentMouseEvt: null, // to use when mouse delay is finished browserTextSize: 12, // default if not found otherwise + measureTextBox: null, + noDataString: "no data", // message for no data at this position + noDataSize: 0, // will be set to size of text 'no data' // items{} - key name is track name, value is an array of data items // where the format of each item can be different for different // data tracks. For example, the wiggle track is an array of: // objects: {x1, x2, v, c} // where [x1..x2) is the array index where the value 'v' // is found, and 'c' is the data value count in this value // i.e. when c > 1 the value is a 'mean' of 'c' data values // visible - keep track of window visible or not, value: true|false // shouldn't need to do this here, the window knows if it // is visible or not, just ask it for status // tracks{} - tracks that were set up initially, key is track name // value is the number of items (for debugging) // maximumWidth{} - key is track name, value is length of longest // number string as measured when rendered @@ -4630,31 +4633,31 @@ // the graphOffset is the index (x coordinate) into the 'items' definitions // of the data value boxes for the graph. The magic number three // is used elsewhere in this code, note the comment on the constant // LEFTADD. var graphOffset = Math.max(0, clientX - tdLeft - 3); if (hgTracks.revCmplDisp) { graphOffset = Math.max(0, rightSide - clientX); } var windowUp = false; // see if window is supposed to become visible var foundIdx = -1; if (mouseOver.items[trackName]) { foundIdx = mouseOver.findRange(graphOffset, mouseOver.items[trackName]); } // can show 'no data' when not found - var mouseOverValue = "no data"; + var mouseOverValue = mouseOver.noDataString; if (foundIdx > -1) { // value to display if (mouseOver.items[trackName][foundIdx].c > 1) { mouseOverValue = " μ " + mouseOver.items[trackName][foundIdx].v + " "; } else { mouseOverValue = " " + mouseOver.items[trackName][foundIdx].v + " "; } } $('#mouseOverText').html(mouseOverValue); var msgWidth = mouseOver.maximumWidth[trackName]; $('#mouseOverText').width(msgWidth); var msgHeight = Math.ceil($('#mouseOverText').height()); var lineHeight = Math.max(0, tdHeight - msgHeight); var lineTop = Math.max(0, tdTop + msgHeight); var msgLeft = Math.max(tdLeft, clientX - (msgWidth/2) - 3); // with magic 3 msgLeft = Math.min(msgLeft, rightSide - msgWidth); // right border limit @@ -4698,30 +4701,44 @@ mouseOver.mouseInTrackImage(evt); // OK to trigger event now return; } else { return; // wait for delay to be done } } mouseOver.delayDone = false; mouseOver.delayInProgress = true; if (mouseOver.popUpTimer) { clearTimeout(mouseOver.popUpTimer); mouseOver.popUpTimer = null; } mouseOver.popUpTimer = setTimeout(mouseOver.delayCompleted, mouseOver.popUpDelay); }, + // given a string of text, return width of rendered text size + // using an off-screen span element that is created here first time through + getWidthOfText: function (measureThis) + { + if(mouseOver.measureTextBox === null){ // set up first time only + mouseOver.measureTextBox = document.createElement('span'); + var cssText = "position: fixed; width: auto; display: block; text-align: right; left:-999px; top:-999px; font-style:normal; font-size:" + mouseOver.browserTextSize + "px; font-family:" + jQuery('body').css('font-family'); + mouseOver.measureTextBox.style.cssText = cssText; + document.body.appendChild(mouseOver.measureTextBox); + } + mouseOver.measureTextBox.innerHTML = measureThis; + return Math.ceil(mouseOver.measureTextBox.clientWidth); + }, + // ======================================================================= // receiveData() callback for successful JSON request, receives incoming // JSON data and gets it into global variables for use here. // The incoming 'arr' is a a set of objects where the object key name is // the track name, used here as an array reference: arr[trackName] // (currently only one object per json file, one file for each track, // this may change to have multiple tracks in one json file.) // The value associated with each track name // is an array of span definitions, where each element in the array is a // mapBox definition object: // {x1:n, x2:n, value:s} // where n is an integer in the range: 0..width, // and s is the value string to display // Will need to get them sorted on x1 for efficient searching as // they accumulate in the local data structure here. @@ -4761,42 +4778,37 @@ if (lenV > lengthLongestNumberString) { lengthLongestNumberString = lenV; longestNumber = arr[trackName].d[datum].v; } mouseOver.items[trackName].push(arr[trackName].d[datum]); ++itemCount; } mouseOver.tracks[trackName] = itemCount; // != 0 -> indicates valid track var mouseOverValue = ""; if (hasMean) { mouseOverValue = " μ " + longestNumber + " "; } else { mouseOverValue = " " + longestNumber + " "; } $('#mouseOverText').css('fontSize',mouseOver.browserTextSize); - $('#mouseOverText').html(mouseOverValue); // see how big as rendered - var maximumWidth = Math.ceil($('#mouseOverText').width()); - $('#mouseOverText').html("no data"); // might be bigger - if (Math.ceil($('#mouseOverText').width() > maximumWidth)) { - maximumWidth = Math.ceil($('#mouseOverText').width()); - } - // XXX this is not working when there are two wiggle tracks in display - // and the window is dragged left or right. The track where the - // drag takes place seems to be dominate somehow and it's width - // calculation overrides the other track width calculation - // even though the .html() has been set here correctly, the - // width() comes back with the dragged track value ? + var maximumWidth = mouseOver.getWidthOfText(mouseOverValue); + if ( 0 === mouseOver.noDataSize) { // only need to do this once + mouseOver.noDataSize = mouseOver.getWidthOfText(mouseOver.noDataString); + } + if (mouseOver.noDataSize > maximumWidth) { + maximumWidth = mouseOver.noDataSize; + } mouseOver.maximumWidth[trackName] = maximumWidth; } }, // receiveData: function (arr) failedRequest: function(url) { // failed request to get json data, remove it from the URL list if (mouseOver.jsonUrl[url]) { delete mouseOver.jsonUrl[url]; } }, // ========================================================================= // fetchJsonData() sends JSON request, callback to receiveData() upon return // ========================================================================= fetchJsonData: function (url)