5
5
6
6
import * as dom from 'vs/base/browser/dom' ;
7
7
import { StandardWheelEvent , IMouseWheelEvent } from 'vs/base/browser/mouseEvent' ;
8
- import { TimeoutTimer } from 'vs/base/common/async' ;
9
8
import { Disposable , IDisposable } from 'vs/base/common/lifecycle' ;
10
9
import * as platform from 'vs/base/common/platform' ;
11
10
import { HitTestContext , MouseTarget , MouseTargetFactory , PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget' ;
12
- import { IMouseTarget , IMouseTargetViewZoneData , MouseTargetType } from 'vs/editor/browser/editorBrowser' ;
13
- import { ClientCoordinates , EditorMouseEvent , EditorMouseEventFactory , GlobalEditorPointerMoveMonitor , createEditorPagePosition , createCoordinatesRelativeToEditor } from 'vs/editor/browser/editorDom' ;
11
+ import { IMouseTarget , IMouseTargetOutsideEditor , IMouseTargetViewZoneData , MouseTargetType } from 'vs/editor/browser/editorBrowser' ;
12
+ import { ClientCoordinates , EditorMouseEvent , EditorMouseEventFactory , GlobalEditorPointerMoveMonitor , createEditorPagePosition , createCoordinatesRelativeToEditor , PageCoordinates } from 'vs/editor/browser/editorDom' ;
14
13
import { ViewController } from 'vs/editor/browser/view/viewController' ;
15
14
import { EditorZoom } from 'vs/editor/common/config/editorZoom' ;
16
15
import { Position } from 'vs/editor/common/core/position' ;
@@ -20,6 +19,7 @@ import { ViewContext } from 'vs/editor/common/viewModel/viewContext';
20
19
import * as viewEvents from 'vs/editor/common/viewEvents' ;
21
20
import { ViewEventHandler } from 'vs/editor/common/viewEventHandler' ;
22
21
import { EditorOption } from 'vs/editor/common/config/editorOptions' ;
22
+ import { NavigationCommandRevealType } from 'vs/editor/browser/coreCommands' ;
23
23
24
24
export interface IPointerHandlerHelper {
25
25
viewDomNode : HTMLElement ;
@@ -34,6 +34,11 @@ export interface IPointerHandlerHelper {
34
34
*/
35
35
getLastRenderData ( ) : PointerHandlerLastRenderData ;
36
36
37
+ /**
38
+ * Render right now
39
+ */
40
+ renderNow ( ) : void ;
41
+
37
42
shouldSuppressMouseDownOnViewZone ( viewZoneId : string ) : boolean ;
38
43
shouldSuppressMouseDownOnWidget ( widgetId : string ) : boolean ;
39
44
@@ -69,6 +74,7 @@ export class MouseHandler extends ViewEventHandler {
69
74
this . _context ,
70
75
this . viewController ,
71
76
this . viewHelper ,
77
+ this . mouseTargetFactory ,
72
78
( e , testEventTarget ) => this . _createMouseTarget ( e , testEventTarget ) ,
73
79
( e ) => this . _getMouseColumn ( e )
74
80
) ) ;
@@ -177,10 +183,6 @@ export class MouseHandler extends ViewEventHandler {
177
183
public override onFocusChanged ( e : viewEvents . ViewFocusChangedEvent ) : boolean {
178
184
return false ;
179
185
}
180
- public override onScrollChanged ( e : viewEvents . ViewScrollChangedEvent ) : boolean {
181
- this . _mouseDownOperation . onScrollChanged ( ) ;
182
- return false ;
183
- }
184
186
// --- end event handlers
185
187
186
188
public getTargetAtClientPoint ( clientX : number , clientY : number ) : IMouseTarget | null {
@@ -313,36 +315,36 @@ export class MouseHandler extends ViewEventHandler {
313
315
314
316
class MouseDownOperation extends Disposable {
315
317
316
- private readonly _context : ViewContext ;
317
- private readonly _viewController : ViewController ;
318
- private readonly _viewHelper : IPointerHandlerHelper ;
319
318
private readonly _createMouseTarget : ( e : EditorMouseEvent , testEventTarget : boolean ) => IMouseTarget ;
320
319
private readonly _getMouseColumn : ( e : EditorMouseEvent ) => number ;
321
320
322
321
private readonly _mouseMoveMonitor : GlobalEditorPointerMoveMonitor ;
323
- private readonly _onScrollTimeout : TimeoutTimer ;
322
+ private readonly _topBottomDragScrolling : TopBottomDragScrolling ;
324
323
private readonly _mouseState : MouseDownState ;
325
324
326
325
private _currentSelection : Selection ;
327
326
private _isActive : boolean ;
328
327
private _lastMouseEvent : EditorMouseEvent | null ;
329
328
330
329
constructor (
331
- context : ViewContext ,
332
- viewController : ViewController ,
333
- viewHelper : IPointerHandlerHelper ,
330
+ private readonly _context : ViewContext ,
331
+ private readonly _viewController : ViewController ,
332
+ private readonly _viewHelper : IPointerHandlerHelper ,
333
+ private readonly _mouseTargetFactory : MouseTargetFactory ,
334
334
createMouseTarget : ( e : EditorMouseEvent , testEventTarget : boolean ) => IMouseTarget ,
335
335
getMouseColumn : ( e : EditorMouseEvent ) => number
336
336
) {
337
337
super ( ) ;
338
- this . _context = context ;
339
- this . _viewController = viewController ;
340
- this . _viewHelper = viewHelper ;
341
338
this . _createMouseTarget = createMouseTarget ;
342
339
this . _getMouseColumn = getMouseColumn ;
343
340
344
341
this . _mouseMoveMonitor = this . _register ( new GlobalEditorPointerMoveMonitor ( this . _viewHelper . viewDomNode ) ) ;
345
- this . _onScrollTimeout = this . _register ( new TimeoutTimer ( ) ) ;
342
+ this . _topBottomDragScrolling = this . _register ( new TopBottomDragScrolling (
343
+ this . _context ,
344
+ this . _viewHelper ,
345
+ this . _mouseTargetFactory ,
346
+ ( position , inSelectionMode , revealType ) => this . _dispatchMouse ( position , inSelectionMode , revealType )
347
+ ) ) ;
346
348
this . _mouseState = new MouseDownState ( ) ;
347
349
348
350
this . _currentSelection = new Selection ( 1 , 1 , 1 , 1 ) ;
@@ -374,7 +376,12 @@ class MouseDownOperation extends Disposable {
374
376
target : position
375
377
} ) ;
376
378
} else {
377
- this . _dispatchMouse ( position , true ) ;
379
+ if ( position . type === MouseTargetType . OUTSIDE_EDITOR ) {
380
+ this . _topBottomDragScrolling . start ( position , e ) ;
381
+ } else {
382
+ this . _topBottomDragScrolling . stop ( ) ;
383
+ this . _dispatchMouse ( position , true , NavigationCommandRevealType . Minimal ) ;
384
+ }
378
385
}
379
386
}
380
387
@@ -436,7 +443,7 @@ class MouseDownOperation extends Disposable {
436
443
}
437
444
438
445
this . _mouseState . isDragAndDrop = false ;
439
- this . _dispatchMouse ( position , e . shiftKey ) ;
446
+ this . _dispatchMouse ( position , e . shiftKey , NavigationCommandRevealType . Minimal ) ;
440
447
441
448
if ( ! this . _isActive ) {
442
449
this . _isActive = true ;
@@ -452,7 +459,7 @@ class MouseDownOperation extends Disposable {
452
459
453
460
private _stop ( ) : void {
454
461
this . _isActive = false ;
455
- this . _onScrollTimeout . cancel ( ) ;
462
+ this . _topBottomDragScrolling . stop ( ) ;
456
463
}
457
464
458
465
public onHeightChanged ( ) : void {
@@ -463,27 +470,6 @@ class MouseDownOperation extends Disposable {
463
470
this . _mouseMoveMonitor . stopMonitoring ( ) ;
464
471
}
465
472
466
- public onScrollChanged ( ) : void {
467
- if ( ! this . _isActive ) {
468
- return ;
469
- }
470
- this . _onScrollTimeout . setIfNotSet ( ( ) => {
471
- if ( ! this . _lastMouseEvent ) {
472
- return ;
473
- }
474
- const position = this . _findMousePosition ( this . _lastMouseEvent , false ) ;
475
- if ( ! position ) {
476
- // Ignoring because position is unknown
477
- return ;
478
- }
479
- if ( this . _mouseState . isDragAndDrop ) {
480
- // Ignoring because users are dragging the text
481
- return ;
482
- }
483
- this . _dispatchMouse ( position , true ) ;
484
- } , 10 ) ;
485
- }
486
-
487
473
public onCursorStateChanged ( e : viewEvents . ViewCursorStateChangedEvent ) : void {
488
474
this . _currentSelection = e . selections [ 0 ] ;
489
475
}
@@ -496,41 +482,45 @@ class MouseDownOperation extends Disposable {
496
482
const mouseColumn = this . _getMouseColumn ( e ) ;
497
483
498
484
if ( e . posy < editorContent . y ) {
499
- const verticalOffset = Math . max ( viewLayout . getCurrentScrollTop ( ) - ( editorContent . y - e . posy ) , 0 ) ;
485
+ const outsideDistance = editorContent . y - e . posy ;
486
+ const verticalOffset = Math . max ( viewLayout . getCurrentScrollTop ( ) - outsideDistance , 0 ) ;
500
487
const viewZoneData = HitTestContext . getZoneAtCoord ( this . _context , verticalOffset ) ;
501
488
if ( viewZoneData ) {
502
489
const newPosition = this . _helpPositionJumpOverViewZone ( viewZoneData ) ;
503
490
if ( newPosition ) {
504
- return MouseTarget . createOutsideEditor ( mouseColumn , newPosition ) ;
491
+ return MouseTarget . createOutsideEditor ( mouseColumn , newPosition , 'above' , outsideDistance ) ;
505
492
}
506
493
}
507
494
508
495
const aboveLineNumber = viewLayout . getLineNumberAtVerticalOffset ( verticalOffset ) ;
509
- return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( aboveLineNumber , 1 ) ) ;
496
+ return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( aboveLineNumber , 1 ) , 'above' , outsideDistance ) ;
510
497
}
511
498
512
499
if ( e . posy > editorContent . y + editorContent . height ) {
500
+ const outsideDistance = e . posy - editorContent . y - editorContent . height ;
513
501
const verticalOffset = viewLayout . getCurrentScrollTop ( ) + e . relativePos . y ;
514
502
const viewZoneData = HitTestContext . getZoneAtCoord ( this . _context , verticalOffset ) ;
515
503
if ( viewZoneData ) {
516
504
const newPosition = this . _helpPositionJumpOverViewZone ( viewZoneData ) ;
517
505
if ( newPosition ) {
518
- return MouseTarget . createOutsideEditor ( mouseColumn , newPosition ) ;
506
+ return MouseTarget . createOutsideEditor ( mouseColumn , newPosition , 'below' , outsideDistance ) ;
519
507
}
520
508
}
521
509
522
510
const belowLineNumber = viewLayout . getLineNumberAtVerticalOffset ( verticalOffset ) ;
523
- return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( belowLineNumber , model . getLineMaxColumn ( belowLineNumber ) ) ) ;
511
+ return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( belowLineNumber , model . getLineMaxColumn ( belowLineNumber ) ) , 'below' , outsideDistance ) ;
524
512
}
525
513
526
514
const possibleLineNumber = viewLayout . getLineNumberAtVerticalOffset ( viewLayout . getCurrentScrollTop ( ) + e . relativePos . y ) ;
527
515
528
516
if ( e . posx < editorContent . x ) {
529
- return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( possibleLineNumber , 1 ) ) ;
517
+ const outsideDistance = editorContent . x - e . posx ;
518
+ return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( possibleLineNumber , 1 ) , 'left' , outsideDistance ) ;
530
519
}
531
520
532
521
if ( e . posx > editorContent . x + editorContent . width ) {
533
- return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( possibleLineNumber , model . getLineMaxColumn ( possibleLineNumber ) ) ) ;
522
+ const outsideDistance = e . posx - editorContent . x - editorContent . width ;
523
+ return MouseTarget . createOutsideEditor ( mouseColumn , new Position ( possibleLineNumber , model . getLineMaxColumn ( possibleLineNumber ) ) , 'right' , outsideDistance ) ;
534
524
}
535
525
536
526
return null ;
@@ -574,14 +564,15 @@ class MouseDownOperation extends Disposable {
574
564
return null ;
575
565
}
576
566
577
- private _dispatchMouse ( position : IMouseTarget , inSelectionMode : boolean ) : void {
567
+ private _dispatchMouse ( position : IMouseTarget , inSelectionMode : boolean , revealType : NavigationCommandRevealType ) : void {
578
568
if ( ! position . position ) {
579
569
return ;
580
570
}
581
571
this . _viewController . dispatchMouse ( {
582
572
position : position . position ,
583
573
mouseColumn : position . mouseColumn ,
584
574
startedOnLineNumbers : this . _mouseState . startedOnLineNumbers ,
575
+ revealType,
585
576
586
577
inSelectionMode : inSelectionMode ,
587
578
mouseDownCount : this . _mouseState . count ,
@@ -598,6 +589,134 @@ class MouseDownOperation extends Disposable {
598
589
}
599
590
}
600
591
592
+ class TopBottomDragScrolling extends Disposable {
593
+
594
+ private _operation : TopBottomDragScrollingOperation | null ;
595
+
596
+ constructor (
597
+ private readonly _context : ViewContext ,
598
+ private readonly _viewHelper : IPointerHandlerHelper ,
599
+ private readonly _mouseTargetFactory : MouseTargetFactory ,
600
+ private readonly _dispatchMouse : ( position : IMouseTarget , inSelectionMode : boolean , revealType : NavigationCommandRevealType ) => void ,
601
+ ) {
602
+ super ( ) ;
603
+ this . _operation = null ;
604
+ }
605
+
606
+ public override dispose ( ) : void {
607
+ super . dispose ( ) ;
608
+ this . stop ( ) ;
609
+ }
610
+
611
+ public start ( position : IMouseTargetOutsideEditor , mouseEvent : EditorMouseEvent ) : void {
612
+ if ( this . _operation ) {
613
+ this . _operation . setPosition ( position , mouseEvent ) ;
614
+ } else {
615
+ this . _operation = new TopBottomDragScrollingOperation ( this . _context , this . _viewHelper , this . _mouseTargetFactory , this . _dispatchMouse , position , mouseEvent ) ;
616
+ }
617
+ }
618
+
619
+ public stop ( ) : void {
620
+ if ( this . _operation ) {
621
+ this . _operation . dispose ( ) ;
622
+ this . _operation = null ;
623
+ }
624
+ }
625
+ }
626
+
627
+ class TopBottomDragScrollingOperation extends Disposable {
628
+
629
+ private _position : IMouseTargetOutsideEditor ;
630
+ private _mouseEvent : EditorMouseEvent ;
631
+ private _lastTime : number ;
632
+ private _animationFrameDisposable : IDisposable ;
633
+
634
+ constructor (
635
+ private readonly _context : ViewContext ,
636
+ private readonly _viewHelper : IPointerHandlerHelper ,
637
+ private readonly _mouseTargetFactory : MouseTargetFactory ,
638
+ private readonly _dispatchMouse : ( position : IMouseTarget , inSelectionMode : boolean , revealType : NavigationCommandRevealType ) => void ,
639
+ position : IMouseTargetOutsideEditor ,
640
+ mouseEvent : EditorMouseEvent
641
+ ) {
642
+ super ( ) ;
643
+ this . _position = position ;
644
+ this . _mouseEvent = mouseEvent ;
645
+ this . _lastTime = Date . now ( ) ;
646
+ this . _animationFrameDisposable = dom . scheduleAtNextAnimationFrame ( ( ) => this . _execute ( ) ) ;
647
+ }
648
+
649
+ public override dispose ( ) : void {
650
+ this . _animationFrameDisposable . dispose ( ) ;
651
+ }
652
+
653
+ public setPosition ( position : IMouseTargetOutsideEditor , mouseEvent : EditorMouseEvent ) : void {
654
+ this . _position = position ;
655
+ this . _mouseEvent = mouseEvent ;
656
+ }
657
+
658
+ /**
659
+ * update internal state and return elapsed ms since last time
660
+ */
661
+ private _tick ( ) : number {
662
+ const now = Date . now ( ) ;
663
+ const elapsed = now - this . _lastTime ;
664
+ this . _lastTime = now ;
665
+ return elapsed ;
666
+ }
667
+
668
+ /**
669
+ * get the number of lines per second to auto-scroll
670
+ */
671
+ private _getScrollSpeed ( ) : number {
672
+ const lineHeight = this . _context . configuration . options . get ( EditorOption . lineHeight ) ;
673
+ const viewportInLines = this . _context . configuration . options . get ( EditorOption . layoutInfo ) . height / lineHeight ;
674
+ const outsideDistanceInLines = this . _position . outsideDistance / lineHeight ;
675
+
676
+ if ( outsideDistanceInLines <= 1.5 ) {
677
+ return Math . max ( 30 , viewportInLines * ( 1 + outsideDistanceInLines ) ) ;
678
+ }
679
+ if ( outsideDistanceInLines <= 3 ) {
680
+ return Math . max ( 60 , viewportInLines * ( 2 + outsideDistanceInLines ) ) ;
681
+ }
682
+ return Math . max ( 200 , viewportInLines * ( 7 + outsideDistanceInLines ) ) ;
683
+ }
684
+
685
+ private _execute ( ) : void {
686
+ const lineHeight = this . _context . configuration . options . get ( EditorOption . lineHeight ) ;
687
+ const scrollSpeedInLines = this . _getScrollSpeed ( ) ;
688
+ const elapsed = this . _tick ( ) ;
689
+ const scrollInPixels = scrollSpeedInLines * ( elapsed / 1000 ) * lineHeight ;
690
+ const scrollValue = ( this . _position . outsidePosition === 'above' ? - scrollInPixels : scrollInPixels ) ;
691
+
692
+ this . _context . viewModel . viewLayout . deltaScrollNow ( 0 , scrollValue ) ;
693
+ this . _viewHelper . renderNow ( ) ;
694
+
695
+ const viewportData = this . _context . viewLayout . getLinesViewportData ( ) ;
696
+ const edgeLineNumber = ( this . _position . outsidePosition === 'above' ? viewportData . startLineNumber : viewportData . endLineNumber ) ;
697
+
698
+ // First, try to find a position that matches the horizontal position of the mouse
699
+ let mouseTarget : IMouseTarget ;
700
+ {
701
+ const editorPos = createEditorPagePosition ( this . _viewHelper . viewDomNode ) ;
702
+ const horizontalScrollbarHeight = this . _context . configuration . options . get ( EditorOption . layoutInfo ) . horizontalScrollbarHeight ;
703
+ const pos = new PageCoordinates ( this . _mouseEvent . pos . x , editorPos . y + editorPos . height - horizontalScrollbarHeight - 0.1 ) ;
704
+ const relativePos = createCoordinatesRelativeToEditor ( this . _viewHelper . viewDomNode , editorPos , pos ) ;
705
+ mouseTarget = this . _mouseTargetFactory . createMouseTarget ( this . _viewHelper . getLastRenderData ( ) , editorPos , pos , relativePos , null ) ;
706
+ }
707
+ if ( ! mouseTarget . position || mouseTarget . position . lineNumber !== edgeLineNumber ) {
708
+ if ( this . _position . outsidePosition === 'above' ) {
709
+ mouseTarget = MouseTarget . createOutsideEditor ( this . _position . mouseColumn , new Position ( edgeLineNumber , 1 ) , 'above' , this . _position . outsideDistance ) ;
710
+ } else {
711
+ mouseTarget = MouseTarget . createOutsideEditor ( this . _position . mouseColumn , new Position ( edgeLineNumber , this . _context . viewModel . getLineMaxColumn ( edgeLineNumber ) ) , 'below' , this . _position . outsideDistance ) ;
712
+ }
713
+ }
714
+
715
+ this . _dispatchMouse ( mouseTarget , true , NavigationCommandRevealType . None ) ;
716
+ this . _animationFrameDisposable = dom . scheduleAtNextAnimationFrame ( ( ) => this . _execute ( ) ) ;
717
+ }
718
+ }
719
+
601
720
class MouseDownState {
602
721
603
722
private static readonly CLEAR_MOUSE_DOWN_COUNT_TIME = 400 ; // ms
0 commit comments