Looking for a Cocoa method to replace MouseMove in a Canvas

Mac OSX specific forum
Niffo
Enthusiast
Enthusiast
Posts: 500
Joined: Tue Jan 31, 2006 9:43 am
Location: France

Looking for a Cocoa method to replace MouseMove in a Canvas

Post by Niffo »

Hello there, (Wilbert ? :-D)

I am looking for a Cocoa method to workaround (subclass) the MouseMove event of the PB CanvasGadget who does not always works on Mac OS Sierra (http://www.purebasic.fr/english/viewtop ... 24&t=66703).

I took a look on the "[PB Cocoa] Methods, Tips & Tricks" thread (http://www.purebasic.fr/english/viewtop ... 19&t=50795) but i only found a method to handle a mousemove event over a window, not over a gadget.

Any idea ?
Last edited by Niffo on Fri Jun 16, 2017 3:58 pm, edited 2 times in total.
Niffo
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by wilbert »

There's more users which can help out with Cocoa. :)
You might want to look at NSTrackingArea .
It allows you to track mouse actions inside a specific area.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by wilbert »

Here's some example of using a tracking area.
Coordinates for the tracking area start at bottom left (0, 0) which is a bit inconvenient.
Of course you can use #NSTrackingInVisibleRect to track the entire gadget.

Code: Select all

;Import "-stdlib=libc++ -mmacosx-version-min=10.7" : EndImport

EnumerationBinary
  #NSTrackingMouseEnteredAndExited    = $001
  #NSTrackingMouseMoved
  #NSTrackingCursorUpdate
  #NSTrackingActiveWhenFirstResponder = $010
  #NSTrackingActiveInKeyWindow
  #NSTrackingActiveInActiveApp
  #NSTrackingActiveAlways
  #NSTrackingAssumeInside             = $100
  #NSTrackingInVisibleRect
  #NSTrackingEnabledDuringMouseDrag
EndEnumeration

ProcedureC _mouseEntered_(obj, sel, event)
  Debug "mouseEntered"
EndProcedure

ProcedureC _mouseExited_(obj, sel, event)
  Debug "mouseExited"
EndProcedure

ProcedureC _mouseMoved_(obj, sel, event)
  Debug "mouseMoved"
EndProcedure

ProcedureC _cursorUpdate_(obj, sel, event)
  Debug "cursorUpdate"  
EndProcedure

Global Tracker.i

Procedure InitTracker()
  Protected trackerClass.i
  If Not Tracker
    trackerClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myAreaTracker", 0)
    class_addMethod_(trackerClass, sel_registerName_("mouseEntered:"), @_mouseEntered_(), "v@:@")
    class_addMethod_(trackerClass, sel_registerName_("mouseExited:"), @_mouseExited_(), "v@:@")
    class_addMethod_(trackerClass, sel_registerName_("mouseMoved:"), @_mouseMoved_(), "v@:@")
    class_addMethod_(trackerClass, sel_registerName_("cursorUpdate:"), @_cursorUpdate_(), "v@:@")
    objc_registerClassPair_(trackerClass)
    Tracker = class_createInstance_(trackerClass, 0)
  EndIf
EndProcedure

InitTracker()

Procedure CreateTrackingArea(x, y, w, h, options, userInfo = #nil)
  Protected Rect.NSRect
  Rect\origin\x = x : Rect\origin\y = y
  Rect\size\width = w : Rect\size\height = h
  ProcedureReturn CocoaMessage(0,CocoaMessage(0,0, "NSTrackingArea alloc"),
                               "initWithRect:@", @Rect, "options:", options,
                               "owner:", Tracker, "userInfo:", userInfo)
EndProcedure




If OpenWindow(0, 0, 0, 220, 220, "CanvasGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 10, 10, 200, 200)
  TA = CreateTrackingArea(0, 0, 50, 50, #NSTrackingMouseEnteredAndExited | #NSTrackingMouseMoved | #NSTrackingActiveInActiveApp)
  CocoaMessage(0, GadgetID(0), "addTrackingArea:", TA)
  
  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget And EventGadget() = 0 
      If EventType() = #PB_EventType_LeftButtonDown Or (EventType() = #PB_EventType_MouseMove And GetGadgetAttribute(0, #PB_Canvas_Buttons) & #PB_Canvas_LeftButton)
        If StartDrawing(CanvasOutput(0))
          x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
          y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
          Circle(x, y, 10, RGB(Random(255), Random(255), Random(255)))
          StopDrawing()
        EndIf
      EndIf
    EndIf    
    
  Until Event = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
Niffo
Enthusiast
Enthusiast
Posts: 500
Joined: Tue Jan 31, 2006 9:43 am
Location: France

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by Niffo »

Thank you VERY MUCH Wilbert ! Seems to be exactly what i need. If i remember correctly, i tried to implement Cocoa NSTrackingArea sometime ago but with no success. On top of that, i used the concept of Tracking Area before in Carbon but this one did not allow the MouseMove events, only MouseEnter and MouseExit.
I am going to see if the "Control Bounds Changed" is automatic in case of resize of the gadget or if i have to call a "HIViewChangeTrackingArea()" like method.
Niffo
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by wilbert »

Niffo wrote:I am going to see if the "Control Bounds Changed" is automatic in case of resize of the gadget or if i have to call a "HIViewChangeTrackingArea()" like method.
If you use #NSTrackingInVisibleRect the area you specify doesn't matter, it will always track the entire view and if it resizes, it still works.
If you want a specific area and you have to change it, you can use removeTrackingArea: to remove the old one and add a new one.
Windows (x64)
Raspberry Pi OS (Arm64)
Niffo
Enthusiast
Enthusiast
Posts: 500
Joined: Tue Jan 31, 2006 9:43 am
Location: France

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by Niffo »

wilbert wrote:If you use #NSTrackingInVisibleRect the area you specify doesn't matter, it will always track the entire view and if it resizes, it still works.
So nice, works like a charm !

I have multiple canvas gadgets and i need to have only one _mouseMoved_() callback. Do you know how i can get (or pass) the calling GadgetID from the callback function ?
With the "userInfo" parameter ? I read here that this parameter does not work with the "mouseMoved" event : https://developer.apple.com/library/con ... jects.html :(
Niffo
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by wilbert »

Well, that was a simple question which took quite some thinking to figure out a solution.
I think this will do the trick ...

Code: Select all

;Import "-stdlib=libc++ -mmacosx-version-min=10.7" : EndImport


;- Callbacks

ProcedureC _mouseEntered_(obj, sel, event)
  Protected GadgetNumber.i
  object_getInstanceVariable_(obj, "_gadget", @GadgetNumber)
  Debug "entered [" + Str(GadgetNumber) + "]"
EndProcedure

ProcedureC _mouseExited_(obj, sel, event)
  Protected GadgetNumber.i
  object_getInstanceVariable_(obj, "_gadget", @GadgetNumber)
  Debug "exited [" + Str(GadgetNumber) + "]"
EndProcedure

ProcedureC _mouseMoved_(obj, sel, event)
  Protected GadgetNumber.i
  object_getInstanceVariable_(obj, "_gadget", @GadgetNumber)
  Debug "moved [" + Str(GadgetNumber) + "]"
EndProcedure

;- Create myGadgetTracker class

Procedure InitTracker()
  Protected trackerClass.i = objc_allocateClassPair_(objc_getClass_("NSTrackingArea"), "myGadgetTracker", 20)
  class_addMethod_(trackerClass, sel_registerName_("mouseEntered:"), @_mouseEntered_(), "v@:@")
  class_addMethod_(trackerClass, sel_registerName_("mouseExited:"), @_mouseExited_(), "v@:@")
  class_addMethod_(trackerClass, sel_registerName_("mouseMoved:"), @_mouseMoved_(), "v@:@")
  CompilerIf SizeOf(Integer) = 8
    class_addIvar_(trackerClass, "_gadget", 8, 3, "q"); add .q variable named gadget 
  CompilerElse
    class_addIvar_(trackerClass, "_gadget", 4, 2, "l"); add .l variable named gadget 
  CompilerEndIf
  objc_registerClassPair_(trackerClass)
EndProcedure

InitTracker()

;- TrackGadget procedure

Procedure TrackGadget(GadgetNumber.i)
  Protected ZeroRect.NSRect, TA.i = CocoaMessage(0, 0, "myGadgetTracker alloc")
  CocoaMessage(0, TA, "initWithRect:@", @ZeroRect, "options:", $643, "owner:", TA, "userInfo:", 0)
  object_setInstanceVariable_(TA, "_gadget", GadgetNumber)
  CocoaMessage(0, GadgetID(GadgetNumber), "addTrackingArea:", TA)
  ProcedureReturn TA
EndProcedure




; *** test ***

OpenWindow(0, 0, 0, 222, 200, "ButtonGadgets", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(0, 10, 10, 200, 20, "Standard Button")
ButtonGadget(1, 10, 40, 200, 20, "Left Button", #PB_Button_Left)
ButtonGadget(2, 10, 70, 200, 20, "Right Button", #PB_Button_Right)
ButtonGadget(3, 10,100, 200, 60, "Multiline Button  (longer text gets automatically wrapped)", #PB_Button_MultiLine)
ButtonGadget(4, 10,170, 200, 20, "Toggle Button", #PB_Button_Toggle)
TrackGadget(0)
TrackGadget(1)
TrackGadget(2)
TrackGadget(3)
TrackGadget(4)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Windows (x64)
Raspberry Pi OS (Arm64)
Niffo
Enthusiast
Enthusiast
Posts: 500
Joined: Tue Jan 31, 2006 9:43 am
Location: France

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by Niffo »

Incredible : works so nice, you really are THE god of Cocoa here !
Again, thank you very much for this very valuable code.
Now it's up to me to incorporate it in my subclassing multi-platform library.
Niffo
WilliamL
Addict
Addict
Posts: 1214
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by WilliamL »

Wow, works very well and I would think it would be useful! An interesting example of callbacks.

I note that #PB_Button_MultiLine doesn't work on OS X.
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Looking for a Cocoa method to replace MoveMove in a Canv

Post by wilbert »

WilliamL wrote:An interesting example of callbacks.
Thanks :)

For me the most interesting part was to figure out how to add an instance variable to a custom class. :wink:
Can be quite useful at times.
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply