Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions example/gui-linux/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gui-linux
virtualization
2 changes: 2 additions & 0 deletions example/linux/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
linux
virtualization
2 changes: 2 additions & 0 deletions example/macOS/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
macOS
virtualization
67 changes: 64 additions & 3 deletions virtualization_view.m
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ @implementation AppDelegate {
NSTimer *_scrollTimer;
NSPoint _scrollDelta;
id _mouseMovedMonitor;
id _scrollWheelMonitor;
}

- (instancetype)initWithVirtualMachine:(VZVirtualMachine *)virtualMachine
Expand Down Expand Up @@ -225,6 +226,10 @@ - (void)dealloc
[NSEvent removeMonitor:_mouseMovedMonitor];
_mouseMovedMonitor = nil;
}
if (_scrollWheelMonitor) {
[NSEvent removeMonitor:_scrollWheelMonitor];
_scrollWheelMonitor = nil;
}
[self stopScrollTimer];
if (_virtualMachine) {
[_virtualMachine removeObserver:self forKeyPath:@"state"];
Expand Down Expand Up @@ -416,6 +421,13 @@ - (void)setupGraphicWindow
return event;
}];

// Add scroll wheel event monitor for zoom functionality
_scrollWheelMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
handler:^NSEvent *(NSEvent *event) {
[self handleScrollWheel:event];
return event;
}];

// Create scroll view for the virtual machine view
NSScrollView *scrollView = [self createScrollViewForVirtualMachineView:_virtualMachineView];
[_window setContentView:scrollView];
Expand Down Expand Up @@ -638,6 +650,7 @@ - (void)showErrorAlertWithMessage:(NSString *)message error:(NSError *)error
- (void)toggleZoomMode:(id)sender
{
_isZoomEnabled = !_isZoomEnabled;
NSScrollView *scrollView = (NSScrollView *)_window.contentView;

// Reset zoom when zoom mode is disabled.
if (!_isZoomEnabled) {
Expand All @@ -646,15 +659,29 @@ - (void)toggleZoomMode:(id)sender
[context setDuration:0.3];
[[_window.contentView animator] setMagnification:1.0];
}
completionHandler:nil];
completionHandler:^{
// Hide scrollers when zoom is disabled
if ([scrollView isKindOfClass:[NSScrollView class]]) {
scrollView.hasVerticalScroller = NO;
scrollView.hasHorizontalScroller = NO;
}
}];
} else {
// Show scrollers when zoom is enabled (they'll auto-hide when not needed)
if ([scrollView isKindOfClass:[NSScrollView class]]) {
scrollView.hasVerticalScroller = YES;
scrollView.hasHorizontalScroller = YES;
scrollView.autohidesScrollers = YES;
}
}
}

- (NSScrollView *)createScrollViewForVirtualMachineView:(VZVirtualMachineView *)view
{
NSScrollView *scrollView = [[[NSScrollView alloc] initWithFrame:_window.contentView.bounds] autorelease];
scrollView.hasVerticalScroller = YES;
scrollView.hasHorizontalScroller = YES;
scrollView.hasVerticalScroller = NO;
scrollView.hasHorizontalScroller = NO;
scrollView.autohidesScrollers = YES;
scrollView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
scrollView.documentView = view;

Expand Down Expand Up @@ -697,6 +724,40 @@ - (void)handleMagnification:(NSMagnificationGestureRecognizer *)recognizer
[scrollView setMagnification:newMagnification centeredAtPoint:centeredPoint];
}

// Handles scroll wheel events for zooming when Command or Option key is held.
// This provides zoom functionality for mice and non-trackpad devices.
- (void)handleScrollWheel:(NSEvent *)event
{
if (!_isZoomEnabled) {
return;
}

// Only zoom if Command or Option key is held
if (!(event.modifierFlags & NSEventModifierFlagCommand) &&
!(event.modifierFlags & NSEventModifierFlagOption)) {
return;
}

NSScrollView *scrollView = (NSScrollView *)_window.contentView;
if (![scrollView isKindOfClass:[NSScrollView class]]) {
return;
}

// Calculate zoom delta (scrolling up = zoom in, down = zoom out)
CGFloat zoomDelta = event.scrollingDeltaY * 0.01; // Scale factor for smooth zooming
CGFloat currentMagnification = scrollView.magnification;
CGFloat newMagnification = currentMagnification + zoomDelta;

// Clamp to min/max magnification
newMagnification = MIN(scrollView.maxMagnification, MAX(scrollView.minMagnification, newMagnification));

// Get mouse location for centered zooming
NSPoint mouseLocation = [_window.contentView convertPoint:event.locationInWindow fromView:nil];
NSPoint centeredPoint = [scrollView.contentView convertPoint:mouseLocation fromView:_window.contentView];

[scrollView setMagnification:newMagnification centeredAtPoint:centeredPoint];
}

// When the mouse approaches the window's edges, this handler adjusts the scroll position
// to provide a smooth panning experience without requiring manual scroll input.
- (void)handleMouseMovement:(NSEvent *)event
Expand Down