Fossil SCM

Shortcut "." (period) to set focus to the entry closest to the center of the viewport. (The keys need to be reassigned later, since non-letter keys don't produce the same characters with or without SHIFT pressed on all keyboard layouts.) Thanks @rouilj for the suggestion and the hint!

florian 2022-08-11 05:57 timeline-keyboard-navigation
Commit e1796f2df2e55e1357d9b67a6028b1afa89fcba46d90cbcafbeea43601c61501
2 files changed +43 -1 +1
+43 -1
--- src/graph.js
+++ src/graph.js
@@ -879,10 +879,41 @@
879879
if( dx<-1 ) return focusFirstId(id);
880880
if( dx>+1 ) return focusLastId(id);
881881
var m = /^m(\d+)$/.exec(id);
882882
return m!==null ? 'm' + (parseInt(m[1]) + dx) : null;
883883
}
884
+ // NOTE: This code relies on `getElementsByClassName()' returning elements
885
+ // in document order; however, attempts to use binary searching (using the
886
+ // signed distance to the vertical center) or 2-pass searching (to get the
887
+ // 1000-block first) didn't yield any significant speedups -- probably the
888
+ // calls to `getBoundingClientRect()' are cached because `initGraph()' has
889
+ // already done so, or because they depend on their previous siblings, and
890
+ // so `e(N).getBoundingClientRect()' ≈ `e(0..N).getBoundingClientRect()'?
891
+ function focusViewportCenterId(){
892
+ var
893
+ fe = null,
894
+ fd = 0,
895
+ wt = 0,
896
+ wb = wt + window.innerHeight,
897
+ wc = wt + window.innerHeight/2,
898
+ el = document.getElementsByClassName('timelineGraph');
899
+ for( var i=0; i<el.length; i++ ){
900
+ var
901
+ rc = el[i].getBoundingClientRect(),
902
+ et = rc.top,
903
+ eb = rc.bottom;
904
+ if( (et>=wt && et<=wb) || (eb>=wt && eb<=wb) ){
905
+ var ed = Math.min(Math.abs(et-wc),Math.abs(eb-wc));
906
+ if( !fe || ed<fd ){
907
+ fe = el[i];
908
+ fd = ed;
909
+ }
910
+ else break;
911
+ }
912
+ }
913
+ return fe ? fe.querySelector('.tl-nodemark').id : null;
914
+ }
884915
function timelineGetDataBlock(i){
885916
var tb = document.getElementById('timeline-data-' + i);
886917
return tb ? JSON.parse(tb.textContent || tb.innerText) : null;
887918
}
888919
function timelineGetRowInfo(id){
@@ -959,10 +990,11 @@
959990
kFRST = mSHIFT | 78 /* SHIFT+N */,
960991
kNEXT = 78 /* N */,
961992
kPREV = 77 /* M */,
962993
kLAST = mSHIFT | 77 /* SHIFT+M */,
963994
kCYCL = 72 /* H */,
995
+ kCNTR = 190 /* . */,
964996
kSCRL = mSHIFT | 72 /* H */,
965997
kTICK = 188 /* , */,
966998
kUNTK = mSHIFT | 188 /* , */,
967999
kCPYH = 66 /* B */,
9681000
kCPYB = mSHIFT | 66 /* SHIFT+B */,
@@ -977,10 +1009,11 @@
9771009
case kFRST: dx = -2; break;
9781010
case kNEXT: dx = -1; break;
9791011
case kPREV: dx = +1; break;
9801012
case kLAST: dx = +2; break;
9811013
case kCYCL:
1014
+ case kCNTR:
9821015
case kSCRL:
9831016
case kTICK:
9841017
case kUNTK:
9851018
case kCPYH:
9861019
case kCPYB:
@@ -988,11 +1021,20 @@
9881021
case kTMLB:
9891022
case kVIEW:
9901023
case kDONE: break;
9911024
default: return;
9921025
}
993
- if( key==kSCRL ){
1026
+ if( key==kCNTR ){
1027
+ var cid = focusViewportCenterId();
1028
+ if( cid ){
1029
+ focusCacheSet(cid);
1030
+ focusVisualize(cid,false);
1031
+ focusCookieInit();
1032
+ }
1033
+ return;
1034
+ }
1035
+ else if( key==kSCRL ){
9941036
var td = document.querySelector('.timelineFocused');
9951037
if( td ) fossilScrollIntoView(td);
9961038
return;
9971039
}
9981040
else if( key==kUNTK ){
9991041
--- src/graph.js
+++ src/graph.js
@@ -879,10 +879,41 @@
879 if( dx<-1 ) return focusFirstId(id);
880 if( dx>+1 ) return focusLastId(id);
881 var m = /^m(\d+)$/.exec(id);
882 return m!==null ? 'm' + (parseInt(m[1]) + dx) : null;
883 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884 function timelineGetDataBlock(i){
885 var tb = document.getElementById('timeline-data-' + i);
886 return tb ? JSON.parse(tb.textContent || tb.innerText) : null;
887 }
888 function timelineGetRowInfo(id){
@@ -959,10 +990,11 @@
959 kFRST = mSHIFT | 78 /* SHIFT+N */,
960 kNEXT = 78 /* N */,
961 kPREV = 77 /* M */,
962 kLAST = mSHIFT | 77 /* SHIFT+M */,
963 kCYCL = 72 /* H */,
 
964 kSCRL = mSHIFT | 72 /* H */,
965 kTICK = 188 /* , */,
966 kUNTK = mSHIFT | 188 /* , */,
967 kCPYH = 66 /* B */,
968 kCPYB = mSHIFT | 66 /* SHIFT+B */,
@@ -977,10 +1009,11 @@
977 case kFRST: dx = -2; break;
978 case kNEXT: dx = -1; break;
979 case kPREV: dx = +1; break;
980 case kLAST: dx = +2; break;
981 case kCYCL:
 
982 case kSCRL:
983 case kTICK:
984 case kUNTK:
985 case kCPYH:
986 case kCPYB:
@@ -988,11 +1021,20 @@
988 case kTMLB:
989 case kVIEW:
990 case kDONE: break;
991 default: return;
992 }
993 if( key==kSCRL ){
 
 
 
 
 
 
 
 
 
994 var td = document.querySelector('.timelineFocused');
995 if( td ) fossilScrollIntoView(td);
996 return;
997 }
998 else if( key==kUNTK ){
999
--- src/graph.js
+++ src/graph.js
@@ -879,10 +879,41 @@
879 if( dx<-1 ) return focusFirstId(id);
880 if( dx>+1 ) return focusLastId(id);
881 var m = /^m(\d+)$/.exec(id);
882 return m!==null ? 'm' + (parseInt(m[1]) + dx) : null;
883 }
884 // NOTE: This code relies on `getElementsByClassName()' returning elements
885 // in document order; however, attempts to use binary searching (using the
886 // signed distance to the vertical center) or 2-pass searching (to get the
887 // 1000-block first) didn't yield any significant speedups -- probably the
888 // calls to `getBoundingClientRect()' are cached because `initGraph()' has
889 // already done so, or because they depend on their previous siblings, and
890 // so `e(N).getBoundingClientRect()' ≈ `e(0..N).getBoundingClientRect()'?
891 function focusViewportCenterId(){
892 var
893 fe = null,
894 fd = 0,
895 wt = 0,
896 wb = wt + window.innerHeight,
897 wc = wt + window.innerHeight/2,
898 el = document.getElementsByClassName('timelineGraph');
899 for( var i=0; i<el.length; i++ ){
900 var
901 rc = el[i].getBoundingClientRect(),
902 et = rc.top,
903 eb = rc.bottom;
904 if( (et>=wt && et<=wb) || (eb>=wt && eb<=wb) ){
905 var ed = Math.min(Math.abs(et-wc),Math.abs(eb-wc));
906 if( !fe || ed<fd ){
907 fe = el[i];
908 fd = ed;
909 }
910 else break;
911 }
912 }
913 return fe ? fe.querySelector('.tl-nodemark').id : null;
914 }
915 function timelineGetDataBlock(i){
916 var tb = document.getElementById('timeline-data-' + i);
917 return tb ? JSON.parse(tb.textContent || tb.innerText) : null;
918 }
919 function timelineGetRowInfo(id){
@@ -959,10 +990,11 @@
990 kFRST = mSHIFT | 78 /* SHIFT+N */,
991 kNEXT = 78 /* N */,
992 kPREV = 77 /* M */,
993 kLAST = mSHIFT | 77 /* SHIFT+M */,
994 kCYCL = 72 /* H */,
995 kCNTR = 190 /* . */,
996 kSCRL = mSHIFT | 72 /* H */,
997 kTICK = 188 /* , */,
998 kUNTK = mSHIFT | 188 /* , */,
999 kCPYH = 66 /* B */,
1000 kCPYB = mSHIFT | 66 /* SHIFT+B */,
@@ -977,10 +1009,11 @@
1009 case kFRST: dx = -2; break;
1010 case kNEXT: dx = -1; break;
1011 case kPREV: dx = +1; break;
1012 case kLAST: dx = +2; break;
1013 case kCYCL:
1014 case kCNTR:
1015 case kSCRL:
1016 case kTICK:
1017 case kUNTK:
1018 case kCPYH:
1019 case kCPYB:
@@ -988,11 +1021,20 @@
1021 case kTMLB:
1022 case kVIEW:
1023 case kDONE: break;
1024 default: return;
1025 }
1026 if( key==kCNTR ){
1027 var cid = focusViewportCenterId();
1028 if( cid ){
1029 focusCacheSet(cid);
1030 focusVisualize(cid,false);
1031 focusCookieInit();
1032 }
1033 return;
1034 }
1035 else if( key==kSCRL ){
1036 var td = document.querySelector('.timelineFocused');
1037 if( td ) fossilScrollIntoView(td);
1038 return;
1039 }
1040 else if( key==kUNTK ){
1041
--- src/timeline.c
+++ src/timeline.c
@@ -1635,10 +1635,11 @@
16351635
** N Focus first (newest) entry.
16361636
** n Focus next (newer) entry, or open next page.
16371637
** m Focus previous (older) entry, or open previous page.
16381638
** M Focus last (oldest) entry.
16391639
** h Move focus between selected, current (check-out) and ticked entries.
1640
+** . Focus entry closest to center of viewport.
16401641
** H Scroll to focused entry.
16411642
** , Tick/untick the node of the focused entry.
16421643
** ; Untick the nodes of all entries.
16431644
** b Copy the commit hash of the focused entry to clipboard.
16441645
** B Copy the branch name of the focused entry to clipboard.
16451646
--- src/timeline.c
+++ src/timeline.c
@@ -1635,10 +1635,11 @@
1635 ** N Focus first (newest) entry.
1636 ** n Focus next (newer) entry, or open next page.
1637 ** m Focus previous (older) entry, or open previous page.
1638 ** M Focus last (oldest) entry.
1639 ** h Move focus between selected, current (check-out) and ticked entries.
 
1640 ** H Scroll to focused entry.
1641 ** , Tick/untick the node of the focused entry.
1642 ** ; Untick the nodes of all entries.
1643 ** b Copy the commit hash of the focused entry to clipboard.
1644 ** B Copy the branch name of the focused entry to clipboard.
1645
--- src/timeline.c
+++ src/timeline.c
@@ -1635,10 +1635,11 @@
1635 ** N Focus first (newest) entry.
1636 ** n Focus next (newer) entry, or open next page.
1637 ** m Focus previous (older) entry, or open previous page.
1638 ** M Focus last (oldest) entry.
1639 ** h Move focus between selected, current (check-out) and ticked entries.
1640 ** . Focus entry closest to center of viewport.
1641 ** H Scroll to focused entry.
1642 ** , Tick/untick the node of the focused entry.
1643 ** ; Untick the nodes of all entries.
1644 ** b Copy the commit hash of the focused entry to clipboard.
1645 ** B Copy the branch name of the focused entry to clipboard.
1646

Keyboard Shortcuts

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