I need to implement a method on a windows delegate. This one specifically, but for this question the specific method doesnt really matter: https://developer.apple.com/documentation/appkit/nswindowdelegate/1419611-window?language=objc
I found some old code that gets me close https://forum.xojo.com/17545-disable-resizing-window-from-top-nswindowdelegate/p1#p157628 but it causes new problems, such as window contents not resizing. Likely because Im replacing the delegate object which the framework is setting.
Without using plugins, does anybody have any ideas that might help?
Here’s a solution, though it’s an unsupported hack that may break in a future Xojo version. Another user gave me some clues privately to avoid the “should you / shouldn’t you” conversation. But since this goes into an open source project anyway, I figure I might as well share what I did.
In the window’s constructor (in this case MainWindow) the goal is to modify the XOJWindowController class to add a method reference to your own shared method. This only needs to happen once. So we’ll use a shared property to track wether or not this has happened.
Herein lies the risk. If Xojo changes the delegate class name or implements the method themselves, this code breaks. The good news is that Xojo has little reason to rename the class, because we could just find it again. And implementing the method themselves would be good for us too, as this would be easier to implement.
Onto some usable code:
Shared Property DelegateClass As Pointer
Function Constructor ()
#if TargetCocoa
Declare Function NSSelectorFromString Lib "Cocoa" (SelectorName As CFStringRef) As Ptr
Declare Function NSClassFromString Lib "Cocoa" (ClassName As CFStringRef) As Ptr
Declare Function class_addMethod Lib CocoaLib (Ref As Ptr, Name As Ptr, Imp As Ptr, Types As CString) As Boolean
If Self.DelegateClass = Nil Then
Self.DelegateClass = NSClassFromString("XOJWindowController")
If Not class_addMethod(Self.DelegateClass, NSSelectorFromString("window:willPositionSheet:usingRect:"), AddressOf Handler_WillPositionSheet, "{NSRect=ffff}@:@@{NSRect=ffff}") Then
Break
Return
End If
End If
Super.Constructor()
#endif
End Function
Private Shared Function Handler_WillPositionSheet(id as Ptr, s as Ptr, WindowHandle As Integer, SheetHandle As Integer, DefaultPosition As NSRect) as NSRect
#Pragma Unused id
#Pragma Unused s
#Pragma Unused WindowHandle
#Pragma Unused SheetHandle
// Open the sheet 60 pixels below the natural position
DefaultPosition.Top = DefaultPosition.Top - 60
Return DefaultPosition
End Function
Now, here’s the funny thing about this code. It will not only affect all instances of this window, but it affects all windows! An improvement would be to implement this on a module. You can use WindowHandle to locate the window the action belongs to.