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
|
||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||
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,
|
||||
"SysLink",WS_TABSTOP,42,26,150,9
|
||||
ICON "APPICON",IDC_STATIC,12,9,20,20
|
||||
|
||||
@@ -3906,6 +3906,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
OPENFILENAME openFileName;
|
||||
static TCHAR filePath[MAX_PATH] = {L"zoomit"};
|
||||
NOTIFYICONDATA tNotifyIconData;
|
||||
static DWORD64 g_TelescopingZoomLastTick = 0ull;
|
||||
|
||||
const auto drawAllRightJustifiedLines = [&rc]( long lineHeight, bool doPop = false ) {
|
||||
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) {
|
||||
case WM_CREATE:
|
||||
|
||||
@@ -4776,7 +4868,10 @@ LRESULT APIENTRY MainWndProc(
|
||||
zoomTelescopeStep = ZOOM_LEVEL_STEP_IN;
|
||||
zoomTelescopeTarget = g_ZoomLevels[g_SliderZoomLevel];
|
||||
if( g_AnimateZoom )
|
||||
{
|
||||
zoomLevel = static_cast<float>(1.0) * zoomTelescopeStep;
|
||||
g_TelescopingZoomLastTick = GetTickCount64();
|
||||
}
|
||||
else
|
||||
zoomLevel = zoomTelescopeTarget;
|
||||
SetTimer( hWnd, 1, ZOOM_LEVEL_STEP_TIME, NULL );
|
||||
@@ -4794,6 +4889,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
// Start telescoping zoom.
|
||||
zoomTelescopeStep = ZOOM_LEVEL_STEP_OUT;
|
||||
zoomTelescopeTarget = 1.0;
|
||||
g_TelescopingZoomLastTick = GetTickCount64();
|
||||
SetTimer( hWnd, 2, ZOOM_LEVEL_STEP_TIME, NULL );
|
||||
|
||||
} else {
|
||||
@@ -5470,6 +5566,14 @@ LRESULT APIENTRY MainWndProc(
|
||||
g_Zoomed, g_Drawing, g_Tracing);
|
||||
|
||||
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 ) {
|
||||
|
||||
@@ -6735,88 +6839,7 @@ LRESULT APIENTRY MainWndProc(
|
||||
|
||||
case 2:
|
||||
case 1:
|
||||
//
|
||||
// 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 );
|
||||
doTelescopingZoomTimer();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
|
||||
Reference in New Issue
Block a user