@@ -636,15 +636,19 @@ export class TabBar<T> extends Widget {
636
636
handleEvent ( event : Event ) : void {
637
637
switch ( event . type ) {
638
638
case 'pointerdown' :
639
- this . _evtPointerDown ( event as PointerEvent ) ;
639
+ this . _lastMouseEvent = event as MouseEvent ;
640
+ this . _evtPointerDown ( event as MouseEvent ) ;
640
641
break ;
641
642
case 'pointermove' :
642
- this . _evtPointerMove ( event as PointerEvent ) ;
643
+ this . _lastMouseEvent = event as MouseEvent ;
644
+ this . _evtPointerMove ( event as MouseEvent ) ;
643
645
break ;
644
646
case 'pointerup' :
645
- this . _evtPointerUp ( event as PointerEvent ) ;
647
+ this . _lastMouseEvent = event as MouseEvent ;
648
+ this . _evtPointerUp ( event as MouseEvent ) ;
646
649
break ;
647
650
case 'dblclick' :
651
+ this . _lastMouseEvent = event as MouseEvent ;
648
652
this . _evtDblClick ( event as MouseEvent ) ;
649
653
break ;
650
654
case 'keydown' :
@@ -657,6 +661,10 @@ export class TabBar<T> extends Widget {
657
661
case 'scroll' :
658
662
this . _evtScroll ( event ) ;
659
663
break ;
664
+ case 'wheel' :
665
+ this . _evtWheel ( event as WheelEvent ) ;
666
+ event . preventDefault ( ) ;
667
+ break ;
660
668
}
661
669
}
662
670
@@ -667,6 +675,7 @@ export class TabBar<T> extends Widget {
667
675
this . node . addEventListener ( 'pointerdown' , this ) ;
668
676
this . node . addEventListener ( 'dblclick' , this ) ;
669
677
this . contentNode . addEventListener ( 'scroll' , this ) ;
678
+ this . contentNode . addEventListener ( 'wheel' , this ) ;
670
679
}
671
680
672
681
/**
@@ -676,6 +685,7 @@ export class TabBar<T> extends Widget {
676
685
this . node . removeEventListener ( 'pointerdown' , this ) ;
677
686
this . node . removeEventListener ( 'dblclick' , this ) ;
678
687
this . contentNode . removeEventListener ( 'scroll' , this ) ;
688
+ this . contentNode . removeEventListener ( 'wheel' , this ) ;
679
689
this . _releaseMouse ( ) ;
680
690
}
681
691
@@ -797,6 +807,10 @@ export class TabBar<T> extends Widget {
797
807
this . updateScrollingHints ( this . _scrollState ) ;
798
808
}
799
809
810
+ private _evtWheel ( event : WheelEvent ) : void {
811
+ this . scrollBy ( event . deltaY ) ;
812
+ }
813
+
800
814
/**
801
815
* Handle the `'dblclick'` event for the tab bar.
802
816
*/
@@ -874,25 +888,47 @@ export class TabBar<T> extends Widget {
874
888
}
875
889
}
876
890
891
+ protected scrollBy ( change : number ) {
892
+ const orientation = this . orientation ;
893
+ const contentNode = this . contentNode ;
894
+
895
+ if ( orientation == 'horizontal' ) {
896
+ contentNode . scrollLeft += change ;
897
+ } else {
898
+ contentNode . scrollTop += change ;
899
+ }
900
+ // Force-update drag state by dispatching last recorded mouse event.
901
+ if ( this . _lastMouseEvent ) {
902
+ this . _evtPointerMove ( this . _lastMouseEvent ) ;
903
+ }
904
+ }
905
+
877
906
protected beginScrolling ( direction : '-' | '+' ) {
878
- const initialRate = 5 ;
879
- const rateIncrease = 1 ;
880
- const maxRate = 20 ;
881
- const intervalHandle = setInterval ( ( ) => {
907
+ // How many pixels should be scrolled per second initially?
908
+ const initialRate = 150 ;
909
+ // By how much should the scrolling rate increase per second?
910
+ const rateIncrease = 80 ;
911
+ // What should be the maximal scrolling speed (pixels/second?)
912
+ const maxRate = 450 ;
913
+
914
+ let previousTime = performance . now ( ) ;
915
+
916
+ const step = ( ) => {
882
917
if ( ! this . _scrollData ) {
883
918
this . stopScrolling ( ) ;
884
919
return ;
885
920
}
921
+ const stepTime = performance . now ( ) ;
922
+ const secondsChange = ( stepTime - previousTime ) / 1000 ;
923
+ previousTime = stepTime ;
886
924
const rate = this . _scrollData . rate ;
887
- const direction = this . _scrollData . scrollDirection ;
888
- const change = ( direction == '+' ? 1 : - 1 ) * rate ;
889
- if ( this . orientation == 'horizontal' ) {
890
- this . contentNode . scrollLeft += change ;
891
- } else {
892
- this . contentNode . scrollTop += change ;
893
- }
925
+ const direction = this . _scrollData . direction ;
926
+
927
+ const change = ( direction == '+' ? 1 : - 1 ) * rate * secondsChange ;
928
+
929
+ this . scrollBy ( change ) ;
894
930
this . _scrollData . rate = Math . min (
895
- this . _scrollData . rate + rateIncrease ,
931
+ this . _scrollData . rate + rateIncrease * secondsChange ,
896
932
maxRate
897
933
) ;
898
934
const state = this . _scrollState ;
@@ -902,18 +938,26 @@ export class TabBar<T> extends Widget {
902
938
state . totalSize == state . position + state . displayedSize )
903
939
) {
904
940
this . stopScrolling ( ) ;
941
+ return ;
905
942
}
906
- } , 50 ) ;
943
+ window . requestAnimationFrame ( step ) ;
944
+ } ;
945
+
946
+ const shouldRequest = ! this . _scrollData ;
947
+
907
948
this . _scrollData = {
908
- timerHandle : intervalHandle ,
909
- scrollDirection : direction ,
949
+ direction : direction ,
910
950
rate : initialRate
911
951
} ;
952
+
953
+ if ( shouldRequest ) {
954
+ window . requestAnimationFrame ( step ) ;
955
+ }
912
956
}
913
957
914
958
protected stopScrolling ( ) {
915
- if ( this . _scrollData ) {
916
- clearInterval ( this . _scrollData . timerHandle ) ;
959
+ if ( ! this . _scrollData ) {
960
+ return ;
917
961
}
918
962
this . _scrollData = null ;
919
963
const state = this . _scrollState ;
@@ -969,6 +1013,18 @@ export class TabBar<T> extends Widget {
969
1013
event . preventDefault ( ) ;
970
1014
event . stopPropagation ( ) ;
971
1015
1016
+ // Add the document mouse up listener.
1017
+ this . document . addEventListener ( 'pointerup' , this , true ) ;
1018
+
1019
+ // Do nothing else if the middle button or add button is clicked.
1020
+ if ( event . button === 1 || addButtonClicked ) {
1021
+ return ;
1022
+ }
1023
+ if ( scrollBeforeButtonClicked || scrollAfterButtonClicked ) {
1024
+ this . beginScrolling ( scrollBeforeButtonClicked ? '-' : '+' ) ;
1025
+ return ;
1026
+ }
1027
+
972
1028
// Initialize the non-measured parts of the drag data.
973
1029
this . _dragData = {
974
1030
tab : tabs [ index ] as HTMLElement ,
@@ -1039,6 +1095,21 @@ export class TabBar<T> extends Widget {
1039
1095
* Handle the `'pointermove'` event for the tab bar.
1040
1096
*/
1041
1097
private _evtPointerMove ( event : PointerEvent | MouseEvent ) : void {
1098
+ let overBeforeScrollButton =
1099
+ this . scrollingEnabled &&
1100
+ this . scrollBeforeButtonNode . contains ( event . target as HTMLElement ) ;
1101
+
1102
+ let overAfterScrollButton =
1103
+ this . scrollingEnabled &&
1104
+ this . scrollAfterButtonNode . contains ( event . target as HTMLElement ) ;
1105
+
1106
+ const isOverScrollButton = overBeforeScrollButton || overAfterScrollButton ;
1107
+
1108
+ if ( ! isOverScrollButton ) {
1109
+ // Stop scrolling if mouse is not over scroll buttons
1110
+ this . stopScrolling ( ) ;
1111
+ }
1112
+
1042
1113
// Do nothing if no drag is in progress.
1043
1114
let data = this . _dragData ;
1044
1115
if ( ! data ) {
@@ -1049,14 +1120,22 @@ export class TabBar<T> extends Widget {
1049
1120
event . preventDefault ( ) ;
1050
1121
event . stopPropagation ( ) ;
1051
1122
1052
- // Lookup the tab nodes.
1053
- let tabs = this . contentNode . children ;
1123
+ if ( isOverScrollButton ) {
1124
+ // Start scrolling if the mouse is over scroll buttons
1125
+ this . beginScrolling ( overBeforeScrollButton ? '-' : '+' ) ;
1126
+ }
1054
1127
1055
1128
// Bail early if the drag threshold has not been met.
1056
- if ( ! data . dragActive && ! Private . dragExceeded ( data , event ) ) {
1129
+ if (
1130
+ ! data . dragActive &&
1131
+ ! Private . dragExceeded ( data , event , this . _scrollState )
1132
+ ) {
1057
1133
return ;
1058
1134
}
1059
1135
1136
+ // Lookup the tab nodes.
1137
+ let tabs = this . contentNode . children ;
1138
+
1060
1139
// Activate the drag if necessary.
1061
1140
if ( ! data . dragActive ) {
1062
1141
// Fill in the rest of the drag data measurements.
@@ -1103,22 +1182,6 @@ export class TabBar<T> extends Widget {
1103
1182
}
1104
1183
}
1105
1184
1106
- let overBeforeScrollButton =
1107
- this . scrollingEnabled &&
1108
- this . scrollBeforeButtonNode . contains ( event . target as HTMLElement ) ;
1109
-
1110
- let overAfterScrollButton =
1111
- this . scrollingEnabled &&
1112
- this . scrollAfterButtonNode . contains ( event . target as HTMLElement ) ;
1113
-
1114
- if ( overBeforeScrollButton || overAfterScrollButton ) {
1115
- // Start scrolling if the mouse is over scroll buttons
1116
- this . beginScrolling ( overBeforeScrollButton ? '-' : '+' ) ;
1117
- } else {
1118
- // Stop scrolling if mouse is not over scroll buttons
1119
- this . stopScrolling ( ) ;
1120
- }
1121
-
1122
1185
// Update the positions of the tabs.
1123
1186
Private . layoutTabs ( tabs , data , event , this . _orientation , this . _scrollState ) ;
1124
1187
}
@@ -1135,6 +1198,9 @@ export class TabBar<T> extends Widget {
1135
1198
// Do nothing if no drag is in progress.
1136
1199
const data = this . _dragData ;
1137
1200
if ( ! data ) {
1201
+ if ( this . _scrollData ) {
1202
+ this . stopScrolling ( ) ;
1203
+ }
1138
1204
return ;
1139
1205
}
1140
1206
@@ -1450,6 +1516,7 @@ export class TabBar<T> extends Widget {
1450
1516
private _titles : Title < T > [ ] = [ ] ;
1451
1517
private _orientation : TabBar . Orientation ;
1452
1518
private _document : Document | ShadowRoot ;
1519
+ private _lastMouseEvent : MouseEvent | null = null ;
1453
1520
private _titlesEditable : boolean = false ;
1454
1521
private _previousTitle : Title < T > | null = null ;
1455
1522
private _dragData : Private . IDragData | null = null ;
@@ -1961,8 +2028,7 @@ namespace Private {
1961
2028
* A struct which holds the scroll data for a tab bar.
1962
2029
*/
1963
2030
export interface IScrollData {
1964
- timerHandle : number ;
1965
- scrollDirection : '+' | '-' ;
2031
+ direction : '+' | '-' ;
1966
2032
rate : number ;
1967
2033
}
1968
2034
@@ -2177,12 +2243,19 @@ namespace Private {
2177
2243
}
2178
2244
2179
2245
/**
2180
- * Test if the event exceeds the drag threshold.
2246
+ * Test if the event or scroll state exceeds the drag threshold.
2181
2247
*/
2182
- export function dragExceeded ( data : IDragData , event : MouseEvent ) : boolean {
2248
+ export function dragExceeded (
2249
+ data : IDragData ,
2250
+ event : MouseEvent ,
2251
+ scrollState : IScrollState | null
2252
+ ) : boolean {
2183
2253
let dx = Math . abs ( event . clientX - data . pressX ) ;
2184
2254
let dy = Math . abs ( event . clientY - data . pressY ) ;
2185
- return dx >= DRAG_THRESHOLD || dy >= DRAG_THRESHOLD ;
2255
+ let ds = scrollState
2256
+ ? Math . abs ( data . initialScrollPosition - scrollState . position )
2257
+ : 0 ;
2258
+ return dx >= DRAG_THRESHOLD || dy >= DRAG_THRESHOLD || ds >= DRAG_THRESHOLD ;
2186
2259
}
2187
2260
2188
2261
/**
0 commit comments