@@ -17,6 +17,7 @@ import {ProfileSearchResults} from '../lib/profile-search'
1717import { BatchCanvasTextRenderer , BatchCanvasRectRenderer } from '../lib/canvas-2d-batch-renderers'
1818import { Color } from '../lib/color'
1919import { Theme } from './themes/theme'
20+ import { minimapMousePositionAtom } from '../app-state'
2021
2122interface FlamechartFrameLabel {
2223 configSpaceBounds : Rect
@@ -558,6 +559,7 @@ export class FlamechartPanZoomView extends Component<FlamechartPanZoomViewProps,
558559
559560 private lastDragPos : Vec2 | null = null
560561 private mouseDownPos : Vec2 | null = null
562+ private currentMousePos : Vec2 | null = null
561563 private onMouseDown = ( ev : MouseEvent ) => {
562564 this . mouseDownPos = this . lastDragPos = new Vec2 ( ev . offsetX , ev . offsetY )
563565 this . updateCursor ( )
@@ -623,6 +625,7 @@ export class FlamechartPanZoomView extends Component<FlamechartPanZoomViewProps,
623625 }
624626
625627 private onMouseMove = ( ev : MouseEvent ) => {
628+ this . currentMousePos = new Vec2 ( ev . offsetX , ev . offsetY )
626629 this . updateCursor ( )
627630 if ( this . lastDragPos ) {
628631 ev . preventDefault ( )
@@ -686,6 +689,7 @@ export class FlamechartPanZoomView extends Component<FlamechartPanZoomViewProps,
686689 }
687690
688691 private onMouseLeave = ( ev : MouseEvent ) => {
692+ this . currentMousePos = null
689693 this . hoveredLabel = null
690694 this . props . onNodeHover ( null )
691695 this . renderCanvas ( )
@@ -728,12 +732,67 @@ export class FlamechartPanZoomView extends Component<FlamechartPanZoomViewProps,
728732 if ( ! this . container ) return
729733 const { width, height} = this . container . getBoundingClientRect ( )
730734
731- if ( ev . key === '=' || ev . key === '+' ) {
732- this . zoom ( new Vec2 ( width / 2 , height / 2 ) , 0.5 )
733- ev . preventDefault ( )
734- } else if ( ev . key === '-' || ev . key === '_' ) {
735- this . zoom ( new Vec2 ( width / 2 , height / 2 ) , 2 )
736- ev . preventDefault ( )
735+ // Check if we have a minimap mouse position (user is hovering over minimap)
736+ // if we do, then pan the mouse to the minimap mouse position and then
737+ // perform zoom from the center of the main view
738+ //
739+ // If we aren't hovering over the minimap, then use the main view and
740+ // zoom around the mouse position, falling back to the center of
741+ // the main view if it's not available.
742+ const minimapMousePos = minimapMousePositionAtom . get ( )
743+ let zoomCenter : Vec2
744+ let shouldZoom = true
745+
746+ if ( minimapMousePos ) {
747+ const currentViewport = this . props . configSpaceViewportRect
748+
749+ // Check if the minimap mouse position is within the current viewport bounds
750+ const isWithinViewport =
751+ minimapMousePos . x >= currentViewport . left ( ) &&
752+ minimapMousePos . x <= currentViewport . right ( ) &&
753+ minimapMousePos . y >= currentViewport . top ( ) &&
754+ minimapMousePos . y <= currentViewport . bottom ( )
755+
756+ // Pan to the minimap mouse position
757+ const newOrigin = new Vec2 (
758+ minimapMousePos . x - currentViewport . width ( ) / 2 ,
759+ minimapMousePos . y - currentViewport . height ( ) / 2 ,
760+ )
761+ this . props . setConfigSpaceViewportRect ( currentViewport . withOrigin ( newOrigin ) )
762+
763+ // If the position was outside the viewport, just pan without zooming
764+ // Next +/- press will do the zoom
765+ if ( ! isWithinViewport ) {
766+ shouldZoom = false
767+ }
768+
769+ zoomCenter = new Vec2 ( width / 2 , height / 2 )
770+ } else {
771+ zoomCenter = this . currentMousePos || new Vec2 ( width / 2 , height / 2 )
772+ }
773+
774+ // By default the zoom multiplier is 1 (no transformation), if
775+ // we are zooming in, scale the transform down by 0.5, else
776+ // scale up by 2 (this moves us 1 full grid line chunk at a time)
777+ let zoomMultiplier = 1
778+
779+ switch ( ev . key ) {
780+ case '=' :
781+ case '+' :
782+ zoomMultiplier = 0.5
783+ break
784+ case '-' :
785+ case '_' :
786+ zoomMultiplier = 2
787+ break
788+ }
789+
790+ if ( shouldZoom ) {
791+ // Slight delay to ensure we pan before zooming
792+ requestAnimationFrame ( ( ) => {
793+ this . zoom ( zoomCenter , zoomMultiplier )
794+ ev . preventDefault ( )
795+ } )
737796 }
738797
739798 if ( ev . ctrlKey || ev . shiftKey || ev . metaKey ) return
@@ -744,7 +803,7 @@ export class FlamechartPanZoomView extends Component<FlamechartPanZoomViewProps,
744803 //
745804 // See: https://github.com/jlfwong/speedscope/pull/184
746805 if ( ev . key === '0' ) {
747- this . zoom ( new Vec2 ( width / 2 , height / 2 ) , 1e9 )
806+ this . zoom ( zoomCenter , 1e9 )
748807 } else if ( ev . key === 'ArrowRight' || ev . code === 'KeyD' ) {
749808 this . pan ( new Vec2 ( 100 , 0 ) )
750809 } else if ( ev . key === 'ArrowLeft' || ev . code === 'KeyA' ) {
0 commit comments