Jump to content
Typer2

Zoom gesture on macOS not working

Recommended Posts

I am looking for a way to support the zoom gesture on a MacBook by using the trackpad, but even the basic ImageZoom demo doesn't seem to work.

 

C:\Users\Public\Documents\Embarcadero\Studio\22.0\Samples\Object Pascal\Mobile Snippets\InteractiveGestures\ImageZoom

 

The documentation says:

"The interactive gestures are multi-touch gestures (Zoom, Rotate, and so on) that are equivalent to System Gestures on Windows, and to Gestures on macOS, iOS, and Android. Every time the fingers move on the touch surface, an OnGesture event is fired."

 

Is this not supported, or am I missing something?

Share this post


Link to post
1 hour ago, Alexander Halser said:

Everything else - simply broken

Zoom is "broken" because the code assumes there are touch events available when the magnifyWithEvent method (in FMX.Platform.Mac) is called. In theory, the same result could be implemented using individual touch events, but it would be more complex. 

 

magnifyWithEvent is supposed to be used in conjunction with the magnification property on the event parameter (an NSEvent).


I didn't look into the other gestures.

Share this post


Link to post
37 minutes ago, Alexander Halser said:

So, it's officially broken, despite documentation says the opposite?

For Zoom, definitely. You mentioned that Rotate works?

Share this post


Link to post

Yes, rotate works as described in the docs. Zoom and pan do not. There's no event at all for these.

The pan gesture (swiping up or down with 2 fingers) obviously gets translated into a mouse wheel message.

Edited by Alexander Halser

Share this post


Link to post

The problem is in FMX.Platform.Mac:

 

procedure TFMXViewBase.magnifyWithEvent(event: NSEvent);
var
  ...
begin
  ...

  if FGestureControl <> nil then
  begin
    LTouches := event.touchesMatchingPhase(NSTouchPhaseTouching, NSView(Super));
{ECS/ALEX
 Here comes our problem: LTouches.count is zero, that's why the gesture event is not fired.
 I assume that we have to deal with "event.magnification" to determine "FEventInfo.Distance".
}
    if LTouches.count >= 2 then
    begin
      LTouchesArray := LTouches.allObjects;
      LTouch := TNSTouch.Wrap(LTouchesArray.objectAtIndex(0));
      LDeviceSize := LTouch.deviceSize;
      ...

I am currently experimenting with the event.magnification value (which is a float value), to match FEventInfo.Distance in a way that the example from the official docs keeps working the way it promises, but doesn't.

 

However, I have a hard time to believe that this has never worked. If Emba has dedicated examples, there must have been a time when this did work properly. It perhaps depends on the MacOS version. My own version of MacOS that I'm using for testing is relatively old (Big Sur), with a second Mac used by one of my colleagues running Sonoma. The gesture is not fired on either machine, but may have been working on OS versions < Big Sur. 

 

If it has never worked with touchpad gestures, it might have worked and still work with a real touch screen. Is anyone competent to comment on that - does the igiZoom gesture work as advertised with touch screen Mac (we don't have them here, so I cannot test this)?

Edited by Alexander Halser

Share this post


Link to post
10 minutes ago, Alexander Halser said:

I am currently experimenting with the event.magnification value (which is a float value), to match FEventInfo.Distance in a way that the example from the official docs keeps working the way it promises, but doesn't.

Have you investigated whether macOS sends regular touch events in addition to magnifyWithEvent when zooming? As I mentioned earlier, this may be a more reliable way, however it might be a bit complex.

Share this post


Link to post
Quote

Have you investigated whether macOS sends regular touch events in addition to magnifyWithEvent when zooming?

No, I haven't. 

But the magnifyWithEvent comes reliably and this is what Delphi actually evaluates. I am not that deep into native MacOS development, so I rather stay with the methods that Delphi already implements.

Share this post


Link to post

I have just upgraded to Delphi 12 and the issue is indeed still not solved. Zoom is a vital part of my software as it is aimed at designers, so I will report this bug.

Share this post


Link to post

IMO the gestures are designed for real touchscreens and not for touchpads. I believe this has never worked with touchpads.

 

Anyway, I have implemented a solution for myself by patching FMX.Platform.Mac, which was already patched anyway to work around the Sonoma scaling bug on MacOS. This is certainly not for everyone, but works like a charm. It uses the Angle parameter for an alternative zoom gesture (Angle is used in rotation gestures, for zoom it's always zero). Your app needs to respond to this accordingly. Most importantly, it doesn't break regular zoom gestures coming from real touch screens.

 

procedure TFMXViewBase.magnifyWithEvent(event: NSEvent);
var
  ...
begin
  ...

  if FGestureControl <> nil then
  begin
    LTouches := event.touchesMatchingPhase(NSTouchPhaseTouching, NSView(Super));
    if LTouches.count >= 2 then
    begin
      LTouchesArray := LTouches.allObjects;
      LTouch := TNSTouch.Wrap(LTouchesArray.objectAtIndex(0));
      LDeviceSize := LTouch.deviceSize;
      FEventInfo.Distance := 0; //reset the distance
      // Find the greatest distance between the touches.
      for I := 0 to LTouches.count - 2 do
      begin
        LTouch := TNSTouch.Wrap(LTouchesArray.objectAtIndex(I));
        LPoint := LTouch.normalizedPosition;
        for J := 1 to LTouches.count - 1 do
        begin
          LTouch := TNSTouch.Wrap(LTouchesArray.objectAtIndex(J));
          LPoint2 := LTouch.normalizedPosition;

          Distance := Round(Sqrt(Sqr(LPoint.x * LDeviceSize.width - LPoint2.x * LDeviceSize.width) +
            Sqr(LPoint.y * LDeviceSize.height - LPoint2.y * LDeviceSize.height)));
          if Distance > FEventInfo.Distance then
            FEventInfo.Distance := Distance;
        end;

        FEventInfo.GestureID := igiZoom;
        if Supports(FGestureControl, IGestureControl, GestureObj) then
          GestureObj.CMGesture(FEventInfo);
        FEventInfo.Flags := [];
      end
    end
{ECS/ALEX}
    else if LTouches.count = 0 then
    begin
      FEventInfo.Distance := 0;
      FEventInfo.Angle := event.magnification;

      FEventInfo.GestureID := igiZoom;
      if Supports(FGestureControl, IGestureControl, GestureObj) then
        GestureObj.CMGesture(FEventInfo);
      FEventInfo.Flags := [];
    end;
  end
{ECS/ALEX}
  else
    //send the message up the responder chain
    NSView(Super).magnifyWithEvent(event);
end;

 

Edited by Alexander Halser
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×