mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 11:17:53 +01:00
Make zooming animation smooth for ZoomIt on mouse move (#42339)
This change coalesces the `WM_MOUSEMOVE` events with the `WM_TIMER` events in the cases 1, 2 (telescope animation for zoom in, respectively, out). I factored into a new lambda the `WM_TIMER` code and I made it save the "now" amount of ticks into the new `g_TelescopingZoomLastTick`. During `WM_MOUSEMOVE` processing, if the current tick count (rather imprecise, but good enough) is larger than the animation step, `ZOOM_LEVEL_STEP_TIME`, then I call that lambda. It's convenient that the code in there already handles the "end of telescope zooming animation" case. ## Validation Steps Performed The choppy zooming animation was evident while moving the mouse during a telescope zoom. It was also [reported on the Sysinternals blog](https://techcommunity.microsoft.com/blog/sysinternals-blog/zoomit-v9-10-procdump-3-5-for-linux-and-jcd-1-0-1/4461244/replies/4461281).
This commit is contained in:
@@ -122,7 +122,7 @@ BEGIN
|
|||||||
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
||||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||||
LTEXT "ZoomIt v9.20",IDC_VERSION,42,7,73,10
|
LTEXT "ZoomIt v9.20",IDC_VERSION,42,7,73,10
|
||||||
LTEXT "Copyright <EFBFBD> 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
|
||||||
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
||||||
"SysLink",WS_TABSTOP,42,26,150,9
|
"SysLink",WS_TABSTOP,42,26,150,9
|
||||||
ICON "APPICON",IDC_STATIC,12,9,20,20
|
ICON "APPICON",IDC_STATIC,12,9,20,20
|
||||||
|
|||||||
@@ -3906,6 +3906,7 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
OPENFILENAME openFileName;
|
OPENFILENAME openFileName;
|
||||||
static TCHAR filePath[MAX_PATH] = {L"zoomit"};
|
static TCHAR filePath[MAX_PATH] = {L"zoomit"};
|
||||||
NOTIFYICONDATA tNotifyIconData;
|
NOTIFYICONDATA tNotifyIconData;
|
||||||
|
static DWORD64 g_TelescopingZoomLastTick = 0ull;
|
||||||
|
|
||||||
const auto drawAllRightJustifiedLines = [&rc]( long lineHeight, bool doPop = false ) {
|
const auto drawAllRightJustifiedLines = [&rc]( long lineHeight, bool doPop = false ) {
|
||||||
rc.top = textPt.y - static_cast<LONG>(g_TextBufferPreviousLines.size()) * lineHeight;
|
rc.top = textPt.y - static_cast<LONG>(g_TextBufferPreviousLines.size()) * lineHeight;
|
||||||
@@ -3932,6 +3933,97 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto doTelescopingZoomTimer = [hWnd, wParam, lParam, &x, &y]( bool invalidate = true ) {
|
||||||
|
if( zoomTelescopeStep != 0.0f )
|
||||||
|
{
|
||||||
|
zoomLevel *= zoomTelescopeStep;
|
||||||
|
g_TelescopingZoomLastTick = GetTickCount64();
|
||||||
|
if( (zoomTelescopeStep > 1 && zoomLevel >= zoomTelescopeTarget) ||
|
||||||
|
(zoomTelescopeStep < 1 && zoomLevel <= zoomTelescopeTarget) )
|
||||||
|
{
|
||||||
|
zoomLevel = zoomTelescopeTarget;
|
||||||
|
|
||||||
|
g_TelescopingZoomLastTick = 0ull;
|
||||||
|
KillTimer( hWnd, wParam );
|
||||||
|
OutputDebug( L"SETCURSOR mon_left: %x mon_top: %x x: %d y: %d\n",
|
||||||
|
monInfo.rcMonitor.left,
|
||||||
|
monInfo.rcMonitor.top,
|
||||||
|
cursorPos.x,
|
||||||
|
cursorPos.y );
|
||||||
|
SetCursorPos( monInfo.rcMonitor.left + cursorPos.x,
|
||||||
|
monInfo.rcMonitor.top + cursorPos.y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Case where we didn't zoom at all
|
||||||
|
g_TelescopingZoomLastTick = 0ull;
|
||||||
|
KillTimer( hWnd, wParam );
|
||||||
|
}
|
||||||
|
if( wParam == 2 && zoomLevel == 1 )
|
||||||
|
{
|
||||||
|
g_Zoomed = FALSE;
|
||||||
|
if( g_ZoomOnLiveZoom )
|
||||||
|
{
|
||||||
|
GetCursorPos( &cursorPos );
|
||||||
|
cursorPos = ScalePointInRects( cursorPos, monInfo.rcMonitor, g_LiveZoomSourceRect );
|
||||||
|
SetCursorPos( cursorPos.x, cursorPos.y );
|
||||||
|
SendMessage( hWnd, WM_HOTKEY, LIVE_HOTKEY, 0 );
|
||||||
|
}
|
||||||
|
else if( lParam != SHALLOW_ZOOM )
|
||||||
|
{
|
||||||
|
// Figure out where final unzoomed cursor should be
|
||||||
|
if( g_Drawing )
|
||||||
|
{
|
||||||
|
cursorPos = prevPt;
|
||||||
|
}
|
||||||
|
OutputDebug( L"FINAL MOUSE: x: %d y: %d\n", cursorPos.x, cursorPos.y );
|
||||||
|
GetZoomedTopLeftCoordinates( zoomLevel, &cursorPos, &x, width, &y, height );
|
||||||
|
cursorPos.x = monInfo.rcMonitor.left + x + static_cast<int>((cursorPos.x - x) * zoomLevel);
|
||||||
|
cursorPos.y = monInfo.rcMonitor.top + y + static_cast<int>((cursorPos.y - y) * zoomLevel);
|
||||||
|
SetCursorPos( cursorPos.x, cursorPos.y );
|
||||||
|
}
|
||||||
|
if( hTargetWindow )
|
||||||
|
{
|
||||||
|
SetWindowPos( hTargetWindow, HWND_BOTTOM, rcTargetWindow.left, rcTargetWindow.top, rcTargetWindow.right - rcTargetWindow.left, rcTargetWindow.bottom - rcTargetWindow.top, 0 );
|
||||||
|
hTargetWindow = NULL;
|
||||||
|
}
|
||||||
|
DeleteDrawUndoList( &drawUndoList );
|
||||||
|
|
||||||
|
// Restore live zoom if we came from that mode
|
||||||
|
if( g_ZoomOnLiveZoom )
|
||||||
|
{
|
||||||
|
SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast<WPARAM>(g_LiveZoomLevel), reinterpret_cast<LPARAM>(&g_LiveZoomSourceRect) );
|
||||||
|
g_ZoomOnLiveZoom = FALSE;
|
||||||
|
forcePenResize = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetForegroundWindow( g_ActiveWindow );
|
||||||
|
ClipCursor( NULL );
|
||||||
|
g_HaveDrawn = FALSE;
|
||||||
|
g_TypeMode = TypeModeOff;
|
||||||
|
g_HaveTyped = FALSE;
|
||||||
|
g_Drawing = FALSE;
|
||||||
|
EnableDisableStickyKeys( TRUE );
|
||||||
|
DeleteObject( hTypingFont );
|
||||||
|
DeleteDC( hdcScreen );
|
||||||
|
DeleteDC( hdcScreenCompat );
|
||||||
|
DeleteDC( hdcScreenCursorCompat );
|
||||||
|
DeleteDC( hdcScreenSaveCompat );
|
||||||
|
DeleteObject( hbmpCompat );
|
||||||
|
DeleteObject (hbmpCursorCompat );
|
||||||
|
DeleteObject( hbmpDrawingCompat );
|
||||||
|
DeleteObject( hDrawingPen );
|
||||||
|
|
||||||
|
SetFocus( g_ActiveWindow );
|
||||||
|
ShowWindow( hWnd, SW_HIDE );
|
||||||
|
}
|
||||||
|
if( invalidate )
|
||||||
|
{
|
||||||
|
InvalidateRect( hWnd, NULL, FALSE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
|
|
||||||
@@ -4776,7 +4868,10 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
|
zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
|
||||||
zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel];
|
zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel];
|
||||||
if( g_AnimateZoom )
|
if( g_AnimateZoom )
|
||||||
|
{
|
||||||
zoomLevel = static_cast<float>(1.0) * zoomTelescopeStep;
|
zoomLevel = static_cast<float>(1.0) * zoomTelescopeStep;
|
||||||
|
g_TelescopingZoomLastTick = GetTickCount64();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
zoomLevel = zoomTelescopeTarget;
|
zoomLevel = zoomTelescopeTarget;
|
||||||
SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
|
SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
|
||||||
@@ -4794,6 +4889,7 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
// Start telescoping zoom.
|
// Start telescoping zoom.
|
||||||
zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
|
zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
|
||||||
zoomTelescopeTarget = 1.0;
|
zoomTelescopeTarget = 1.0;
|
||||||
|
g_TelescopingZoomLastTick = GetTickCount64();
|
||||||
SetTimer( hWnd, 2, ZOOM_LEVEL_STEP_TIME, NULL );
|
SetTimer( hWnd, 2, ZOOM_LEVEL_STEP_TIME, NULL );
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -5470,6 +5566,14 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
g_Zoomed, g_Drawing, g_Tracing);
|
g_Zoomed, g_Drawing, g_Tracing);
|
||||||
|
|
||||||
OutputDebug(L"Window visible: %d Topmost: %d\n", IsWindowVisible(hWnd), GetWindowLong(hWnd, GWL_EXSTYLE)& WS_EX_TOPMOST);
|
OutputDebug(L"Window visible: %d Topmost: %d\n", IsWindowVisible(hWnd), GetWindowLong(hWnd, GWL_EXSTYLE)& WS_EX_TOPMOST);
|
||||||
|
if( g_Zoomed && g_TelescopingZoomLastTick != 0ull && !g_Drawing && !g_Tracing )
|
||||||
|
{
|
||||||
|
ULONG64 now = GetTickCount64();
|
||||||
|
if( now - g_TelescopingZoomLastTick >= ZOOM_LEVEL_STEP_TIME )
|
||||||
|
{
|
||||||
|
doTelescopingZoomTimer( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( g_Zoomed && (g_TypeMode == TypeModeOff) && !g_bSaveInProgress ) {
|
if( g_Zoomed && (g_TypeMode == TypeModeOff) && !g_bSaveInProgress ) {
|
||||||
|
|
||||||
@@ -6735,88 +6839,7 @@ LRESULT APIENTRY MainWndProc(
|
|||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
case 1:
|
case 1:
|
||||||
//
|
doTelescopingZoomTimer();
|
||||||
// Telescoping zoom timer
|
|
||||||
//
|
|
||||||
if( zoomTelescopeStep ) {
|
|
||||||
|
|
||||||
zoomLevel *= zoomTelescopeStep;
|
|
||||||
if( (zoomTelescopeStep > 1 && zoomLevel >= zoomTelescopeTarget ) ||
|
|
||||||
(zoomTelescopeStep < 1 && zoomLevel <= zoomTelescopeTarget )) {
|
|
||||||
|
|
||||||
zoomLevel = zoomTelescopeTarget;
|
|
||||||
KillTimer( hWnd, wParam );
|
|
||||||
OutputDebug( L"SETCURSOR mon_left: %x mon_top: %x x: %d y: %d\n",
|
|
||||||
monInfo.rcMonitor.left, monInfo.rcMonitor.top, cursorPos.x, cursorPos.y );
|
|
||||||
SetCursorPos( monInfo.rcMonitor.left + cursorPos.x,
|
|
||||||
monInfo.rcMonitor.top + cursorPos.y );
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Case where we didn't zoom at all
|
|
||||||
KillTimer( hWnd, wParam );
|
|
||||||
}
|
|
||||||
if( wParam == 2 && zoomLevel == 1 ) {
|
|
||||||
|
|
||||||
g_Zoomed = FALSE;
|
|
||||||
if( g_ZoomOnLiveZoom )
|
|
||||||
{
|
|
||||||
GetCursorPos( &cursorPos );
|
|
||||||
cursorPos = ScalePointInRects( cursorPos, monInfo.rcMonitor, g_LiveZoomSourceRect );
|
|
||||||
SetCursorPos( cursorPos.x, cursorPos.y );
|
|
||||||
SendMessage(hWnd, WM_HOTKEY, LIVE_HOTKEY, 0);
|
|
||||||
}
|
|
||||||
else if( lParam != SHALLOW_ZOOM )
|
|
||||||
{
|
|
||||||
// Figure out where final unzoomed cursor should be
|
|
||||||
if (g_Drawing) {
|
|
||||||
cursorPos = prevPt;
|
|
||||||
}
|
|
||||||
OutputDebug(L"FINAL MOUSE: x: %d y: %d\n", cursorPos.x, cursorPos.y );
|
|
||||||
GetZoomedTopLeftCoordinates(zoomLevel, &cursorPos, &x, width, &y, height);
|
|
||||||
cursorPos.x = monInfo.rcMonitor.left + x + static_cast<int>((cursorPos.x - x) * zoomLevel);
|
|
||||||
cursorPos.y = monInfo.rcMonitor.top + y + static_cast<int>((cursorPos.y - y) * zoomLevel);
|
|
||||||
SetCursorPos(cursorPos.x, cursorPos.y);
|
|
||||||
}
|
|
||||||
if( hTargetWindow ) {
|
|
||||||
|
|
||||||
SetWindowPos( hTargetWindow, HWND_BOTTOM, rcTargetWindow.left, rcTargetWindow.top,
|
|
||||||
rcTargetWindow.right - rcTargetWindow.left,
|
|
||||||
rcTargetWindow.bottom - rcTargetWindow.top, 0 );
|
|
||||||
hTargetWindow = NULL;
|
|
||||||
}
|
|
||||||
DeleteDrawUndoList( &drawUndoList );
|
|
||||||
|
|
||||||
// Restore live zoom if we came from that mode
|
|
||||||
if( g_ZoomOnLiveZoom ) {
|
|
||||||
|
|
||||||
SendMessage( g_hWndLiveZoom, WM_USER_SET_ZOOM, static_cast<WPARAM>(g_LiveZoomLevel), reinterpret_cast<LPARAM>(&g_LiveZoomSourceRect) );
|
|
||||||
g_ZoomOnLiveZoom = FALSE;
|
|
||||||
forcePenResize = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetForegroundWindow( g_ActiveWindow );
|
|
||||||
ClipCursor( NULL );
|
|
||||||
g_HaveDrawn = FALSE;
|
|
||||||
g_TypeMode = TypeModeOff;
|
|
||||||
g_HaveTyped = FALSE;
|
|
||||||
g_Drawing = FALSE;
|
|
||||||
EnableDisableStickyKeys( TRUE );
|
|
||||||
DeleteObject( hTypingFont );
|
|
||||||
DeleteDC( hdcScreen );
|
|
||||||
DeleteDC( hdcScreenCompat );
|
|
||||||
DeleteDC( hdcScreenCursorCompat );
|
|
||||||
DeleteDC( hdcScreenSaveCompat );
|
|
||||||
DeleteObject( hbmpCompat );
|
|
||||||
DeleteObject( hbmpCursorCompat );
|
|
||||||
DeleteObject( hbmpDrawingCompat );
|
|
||||||
DeleteObject( hDrawingPen );
|
|
||||||
|
|
||||||
SetFocus( g_ActiveWindow );
|
|
||||||
ShowWindow( hWnd, SW_HIDE );
|
|
||||||
}
|
|
||||||
InvalidateRect( hWnd, NULL, FALSE );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
|||||||
Reference in New Issue
Block a user