Fossil SCM

Proof-of-concept for how to pop up a tooltip when mousing over one of the branch arrows in a timeline graph.

drh 2019-05-17 16:45 tooltips
Commit 8e922935feb9445af9eedc88f086ac6810f95d238e56870cda64ba0e111a1bfc
--- src/default_css.txt
+++ src/default_css.txt
@@ -195,10 +195,21 @@
195195
width: 0px;
196196
border-left-width: 2px;
197197
border-left-style: dotted;
198198
background: rgba(255,255,255,0);
199199
}
200
+.tl-tooltip {
201
+ background-color: #fecd4b;
202
+ color: black;
203
+ text-align: center;
204
+ padding: 5px 1em;
205
+ border: 1px solid black;
206
+ border-radius: 6px;
207
+ position: absolute;
208
+ z-index: 100;
209
+}
210
+
200211
span.tagDsp {
201212
font-weight: bold;
202213
}
203214
span.wikiError {
204215
font-weight: bold;
205216
--- src/default_css.txt
+++ src/default_css.txt
@@ -195,10 +195,21 @@
195 width: 0px;
196 border-left-width: 2px;
197 border-left-style: dotted;
198 background: rgba(255,255,255,0);
199 }
 
 
 
 
 
 
 
 
 
 
 
200 span.tagDsp {
201 font-weight: bold;
202 }
203 span.wikiError {
204 font-weight: bold;
205
--- src/default_css.txt
+++ src/default_css.txt
@@ -195,10 +195,21 @@
195 width: 0px;
196 border-left-width: 2px;
197 border-left-style: dotted;
198 background: rgba(255,255,255,0);
199 }
200 .tl-tooltip {
201 background-color: #fecd4b;
202 color: black;
203 text-align: center;
204 padding: 5px 1em;
205 border: 1px solid black;
206 border-radius: 6px;
207 position: absolute;
208 z-index: 100;
209 }
210
211 span.tagDsp {
212 font-weight: bold;
213 }
214 span.wikiError {
215 font-weight: bold;
216
+32 -11
--- src/graph.js
+++ src/graph.js
@@ -72,10 +72,14 @@
7272
style.textContent = css;
7373
document.querySelector("head").appendChild(style);
7474
}
7575
amendCssOnce = 0;
7676
}
77
+var tooltipObj = document.createElement("span");
78
+tooltipObj.className = "tl-tooltip";
79
+tooltipObj.style.visibility = "hidden";
80
+document.getElementsByTagName("BODY")[0].appendChild(tooltipObj);
7781
7882
function TimelineGraph(tx){
7983
var topObj = document.getElementById("timelineTable"+tx.iTableId);
8084
amendCss(tx.circleNodes, tx.showArrowheads);
8185
var canvasDiv;
@@ -185,20 +189,23 @@
185189
y1 = y0+elem.w;
186190
cls += "h";
187191
}
188192
return drawBox(cls,color,x0,y0,x1,y1);
189193
}
190
- function drawUpArrow(from,to,color){
194
+ function drawUpArrow(from,to,color,id){
191195
var y = to.y + node.h;
192196
var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
193197
var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
194198
var x = to.x + (node.w-line.w)/2;
195199
var y0 = from.y + node.h/2;
196200
var y1 = Math.ceil(to.y + node.h + arw.h/2);
197
- drawLine(line,color,x,y0,null,y1);
201
+ var n = drawLine(line,color,x,y0,null,y1);
202
+ n.onmouseenter = tooltipEnter
203
+ n.onmouseleave = tooltipLeave
204
+ n.setAttribute("data-id",id)
198205
x = to.x + (node.w-arw.w)/2;
199
- var n = drawBox(arw.cls,null,x,y);
206
+ n = drawBox(arw.cls,null,x,y);
200207
if(color) n.style.borderBottomColor = color;
201208
}
202209
function drawDotted(from,to,color){
203210
var x = to.x + (node.w-line.w)/2;
204211
var y0 = from.y + node.h/2;
@@ -243,12 +250,12 @@
243250
if(e) e.style.backgroundColor = p.bg;
244251
e = document.getElementById("md"+p.id);
245252
if(e) e.style.backgroundColor = p.bg;
246253
}
247254
if( p.r<0 ) return;
248
- if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
249
- if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg);
255
+ if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg,p.id);
256
+ if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg,p.id);
250257
var cls = node.cls;
251258
if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
252259
if( p.f&1 ) cls += " leaf";
253260
var n = drawBox(cls,p.bg,p.x,p.y);
254261
n.id = "tln"+p.id;
@@ -257,22 +264,22 @@
257264
if( !tx.omitDescenders ){
258265
if( p.u==0 ){
259266
if( p.hasOwnProperty('mo') && p.r==p.mo ){
260267
var ix = p.hasOwnProperty('cu') ? p.cu : p.mu;
261268
var top = tx.rowinfo[ix-tx.iTopRow]
262
- drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg);
269
+ drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg, p.id);
263270
}else if( p.y>100 ){
264
- drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg);
271
+ drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg, p.id);
265272
}else{
266
- drawUpArrow(p,{x: p.x, y: 0},p.fg);
273
+ drawUpArrow(p,{x: p.x, y: 0},p.fg, p.id);
267274
}
268275
}
269276
if( p.hasOwnProperty('d') ){
270277
if( p.y + 150 >= btm ){
271
- drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg);
278
+ drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg,p.id);
272279
}else{
273
- drawUpArrow({x: p.x, y: p.y+50},p,p.fg);
280
+ drawUpArrow({x: p.x, y: p.y+50},p,p.fg,p.id);
274281
drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},p.fg);
275282
}
276283
}
277284
}
278285
if( p.hasOwnProperty('mo') ){
@@ -326,11 +333,11 @@
326333
}
327334
var y0 = p.y + (node.h-line.w)/2;
328335
var u = tx.rowinfo[p.au[i+1]-tx.iTopRow];
329336
if( u.id<p.id ){
330337
drawLine(line,u.fg,x0,y0,x1,null);
331
- drawUpArrow(p,u,u.fg);
338
+ drawUpArrow(p,u,u.fg,u.id);
332339
}else{
333340
var y1 = u.y + (node.h-line.w)/2;
334341
drawLine(wLine,u.fg,x0,y0,x1,null);
335342
drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
336343
drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
@@ -408,10 +415,24 @@
408415
}else{
409416
location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
410417
}
411418
}
412419
}
420
+ function tooltipEnter(e){
421
+ var id = this.getAttribute("data-id")
422
+ tooltipObj.textContent = tx.rowinfo[id-tx.iTopRow].br
423
+ tooltipObj.style.display = "inline"
424
+ tooltipObj.style.position = "absolute"
425
+ var x = e.x + 4 + window.pageXOffset
426
+ tooltipObj.style.left = x+"px"
427
+ var y = e.y + window.pageYOffset - tooltipObj.clientHeight - 4
428
+ tooltipObj.style.top = y+"px"
429
+ tooltipObj.style.visibility = "visible"
430
+ }
431
+ function tooltipLeave(e){
432
+ tooltipObj.style.display = "none"
433
+ }
413434
function changeDisplay(selector,value){
414435
var x = document.getElementsByClassName(selector);
415436
var n = x.length;
416437
for(var i=0; i<n; i++) {x[i].style.display = value;}
417438
}
418439
--- src/graph.js
+++ src/graph.js
@@ -72,10 +72,14 @@
72 style.textContent = css;
73 document.querySelector("head").appendChild(style);
74 }
75 amendCssOnce = 0;
76 }
 
 
 
 
77
78 function TimelineGraph(tx){
79 var topObj = document.getElementById("timelineTable"+tx.iTableId);
80 amendCss(tx.circleNodes, tx.showArrowheads);
81 var canvasDiv;
@@ -185,20 +189,23 @@
185 y1 = y0+elem.w;
186 cls += "h";
187 }
188 return drawBox(cls,color,x0,y0,x1,y1);
189 }
190 function drawUpArrow(from,to,color){
191 var y = to.y + node.h;
192 var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
193 var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
194 var x = to.x + (node.w-line.w)/2;
195 var y0 = from.y + node.h/2;
196 var y1 = Math.ceil(to.y + node.h + arw.h/2);
197 drawLine(line,color,x,y0,null,y1);
 
 
 
198 x = to.x + (node.w-arw.w)/2;
199 var n = drawBox(arw.cls,null,x,y);
200 if(color) n.style.borderBottomColor = color;
201 }
202 function drawDotted(from,to,color){
203 var x = to.x + (node.w-line.w)/2;
204 var y0 = from.y + node.h/2;
@@ -243,12 +250,12 @@
243 if(e) e.style.backgroundColor = p.bg;
244 e = document.getElementById("md"+p.id);
245 if(e) e.style.backgroundColor = p.bg;
246 }
247 if( p.r<0 ) return;
248 if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
249 if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg);
250 var cls = node.cls;
251 if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
252 if( p.f&1 ) cls += " leaf";
253 var n = drawBox(cls,p.bg,p.x,p.y);
254 n.id = "tln"+p.id;
@@ -257,22 +264,22 @@
257 if( !tx.omitDescenders ){
258 if( p.u==0 ){
259 if( p.hasOwnProperty('mo') && p.r==p.mo ){
260 var ix = p.hasOwnProperty('cu') ? p.cu : p.mu;
261 var top = tx.rowinfo[ix-tx.iTopRow]
262 drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg);
263 }else if( p.y>100 ){
264 drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg);
265 }else{
266 drawUpArrow(p,{x: p.x, y: 0},p.fg);
267 }
268 }
269 if( p.hasOwnProperty('d') ){
270 if( p.y + 150 >= btm ){
271 drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg);
272 }else{
273 drawUpArrow({x: p.x, y: p.y+50},p,p.fg);
274 drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},p.fg);
275 }
276 }
277 }
278 if( p.hasOwnProperty('mo') ){
@@ -326,11 +333,11 @@
326 }
327 var y0 = p.y + (node.h-line.w)/2;
328 var u = tx.rowinfo[p.au[i+1]-tx.iTopRow];
329 if( u.id<p.id ){
330 drawLine(line,u.fg,x0,y0,x1,null);
331 drawUpArrow(p,u,u.fg);
332 }else{
333 var y1 = u.y + (node.h-line.w)/2;
334 drawLine(wLine,u.fg,x0,y0,x1,null);
335 drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
336 drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
@@ -408,10 +415,24 @@
408 }else{
409 location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
410 }
411 }
412 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413 function changeDisplay(selector,value){
414 var x = document.getElementsByClassName(selector);
415 var n = x.length;
416 for(var i=0; i<n; i++) {x[i].style.display = value;}
417 }
418
--- src/graph.js
+++ src/graph.js
@@ -72,10 +72,14 @@
72 style.textContent = css;
73 document.querySelector("head").appendChild(style);
74 }
75 amendCssOnce = 0;
76 }
77 var tooltipObj = document.createElement("span");
78 tooltipObj.className = "tl-tooltip";
79 tooltipObj.style.visibility = "hidden";
80 document.getElementsByTagName("BODY")[0].appendChild(tooltipObj);
81
82 function TimelineGraph(tx){
83 var topObj = document.getElementById("timelineTable"+tx.iTableId);
84 amendCss(tx.circleNodes, tx.showArrowheads);
85 var canvasDiv;
@@ -185,20 +189,23 @@
189 y1 = y0+elem.w;
190 cls += "h";
191 }
192 return drawBox(cls,color,x0,y0,x1,y1);
193 }
194 function drawUpArrow(from,to,color,id){
195 var y = to.y + node.h;
196 var arrowSpace = from.y - y + (!from.id || from.r!=to.r ? node.h/2 : 0);
197 var arw = arrowSpace < arrow.h*1.5 ? arrowSmall : arrow;
198 var x = to.x + (node.w-line.w)/2;
199 var y0 = from.y + node.h/2;
200 var y1 = Math.ceil(to.y + node.h + arw.h/2);
201 var n = drawLine(line,color,x,y0,null,y1);
202 n.onmouseenter = tooltipEnter
203 n.onmouseleave = tooltipLeave
204 n.setAttribute("data-id",id)
205 x = to.x + (node.w-arw.w)/2;
206 n = drawBox(arw.cls,null,x,y);
207 if(color) n.style.borderBottomColor = color;
208 }
209 function drawDotted(from,to,color){
210 var x = to.x + (node.w-line.w)/2;
211 var y0 = from.y + node.h/2;
@@ -243,12 +250,12 @@
250 if(e) e.style.backgroundColor = p.bg;
251 e = document.getElementById("md"+p.id);
252 if(e) e.style.backgroundColor = p.bg;
253 }
254 if( p.r<0 ) return;
255 if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg,p.id);
256 if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],p.fg,p.id);
257 var cls = node.cls;
258 if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
259 if( p.f&1 ) cls += " leaf";
260 var n = drawBox(cls,p.bg,p.x,p.y);
261 n.id = "tln"+p.id;
@@ -257,22 +264,22 @@
264 if( !tx.omitDescenders ){
265 if( p.u==0 ){
266 if( p.hasOwnProperty('mo') && p.r==p.mo ){
267 var ix = p.hasOwnProperty('cu') ? p.cu : p.mu;
268 var top = tx.rowinfo[ix-tx.iTopRow]
269 drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg, p.id);
270 }else if( p.y>100 ){
271 drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg, p.id);
272 }else{
273 drawUpArrow(p,{x: p.x, y: 0},p.fg, p.id);
274 }
275 }
276 if( p.hasOwnProperty('d') ){
277 if( p.y + 150 >= btm ){
278 drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg,p.id);
279 }else{
280 drawUpArrow({x: p.x, y: p.y+50},p,p.fg,p.id);
281 drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},p.fg);
282 }
283 }
284 }
285 if( p.hasOwnProperty('mo') ){
@@ -326,11 +333,11 @@
333 }
334 var y0 = p.y + (node.h-line.w)/2;
335 var u = tx.rowinfo[p.au[i+1]-tx.iTopRow];
336 if( u.id<p.id ){
337 drawLine(line,u.fg,x0,y0,x1,null);
338 drawUpArrow(p,u,u.fg,u.id);
339 }else{
340 var y1 = u.y + (node.h-line.w)/2;
341 drawLine(wLine,u.fg,x0,y0,x1,null);
342 drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
343 drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
@@ -408,10 +415,24 @@
415 }else{
416 location.href=tx.baseUrl + "/vdiff?from="+selRow.h+"&to="+p.h
417 }
418 }
419 }
420 function tooltipEnter(e){
421 var id = this.getAttribute("data-id")
422 tooltipObj.textContent = tx.rowinfo[id-tx.iTopRow].br
423 tooltipObj.style.display = "inline"
424 tooltipObj.style.position = "absolute"
425 var x = e.x + 4 + window.pageXOffset
426 tooltipObj.style.left = x+"px"
427 var y = e.y + window.pageYOffset - tooltipObj.clientHeight - 4
428 tooltipObj.style.top = y+"px"
429 tooltipObj.style.visibility = "visible"
430 }
431 function tooltipLeave(e){
432 tooltipObj.style.display = "none"
433 }
434 function changeDisplay(selector,value){
435 var x = document.getElementsByClassName(selector);
436 var n = x.length;
437 for(var i=0; i<n; i++) {x[i].style.display = value;}
438 }
439
--- src/timeline.c
+++ src/timeline.c
@@ -902,10 +902,11 @@
902902
** the screen. This array is omitted if there are no inbound
903903
** merges.
904904
** ci: "cherrypick-in". Like "mi" except for cherrypick merges.
905905
** omitted if there are no cherrypick merges.
906906
** h: The artifact hash of the object being graphed
907
+ * br: The branch to which the artifact belongs
907908
*/
908909
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
909910
int k = 0;
910911
cgi_printf("{\"id\":%d,", pRow->idx);
911912
cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
@@ -975,10 +976,11 @@
975976
cgi_printf("%c%d", cSep, mi);
976977
cSep = ',';
977978
}
978979
}
979980
if( k ) cgi_printf("],");
981
+ cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : "");
980982
cgi_printf("\"h\":\"%!S\"}%s",
981983
pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
982984
}
983985
@ }</script>
984986
style_graph_generator();
985987
--- src/timeline.c
+++ src/timeline.c
@@ -902,10 +902,11 @@
902 ** the screen. This array is omitted if there are no inbound
903 ** merges.
904 ** ci: "cherrypick-in". Like "mi" except for cherrypick merges.
905 ** omitted if there are no cherrypick merges.
906 ** h: The artifact hash of the object being graphed
 
907 */
908 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
909 int k = 0;
910 cgi_printf("{\"id\":%d,", pRow->idx);
911 cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
@@ -975,10 +976,11 @@
975 cgi_printf("%c%d", cSep, mi);
976 cSep = ',';
977 }
978 }
979 if( k ) cgi_printf("],");
 
980 cgi_printf("\"h\":\"%!S\"}%s",
981 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
982 }
983 @ }</script>
984 style_graph_generator();
985
--- src/timeline.c
+++ src/timeline.c
@@ -902,10 +902,11 @@
902 ** the screen. This array is omitted if there are no inbound
903 ** merges.
904 ** ci: "cherrypick-in". Like "mi" except for cherrypick merges.
905 ** omitted if there are no cherrypick merges.
906 ** h: The artifact hash of the object being graphed
907 * br: The branch to which the artifact belongs
908 */
909 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
910 int k = 0;
911 cgi_printf("{\"id\":%d,", pRow->idx);
912 cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
@@ -975,10 +976,11 @@
976 cgi_printf("%c%d", cSep, mi);
977 cSep = ',';
978 }
979 }
980 if( k ) cgi_printf("],");
981 cgi_printf("\"br\":\"%j\",", pRow->zBranch ? pRow->zBranch : "");
982 cgi_printf("\"h\":\"%!S\"}%s",
983 pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
984 }
985 @ }</script>
986 style_graph_generator();
987

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button