I wanted to simplify that a bit.
I don't know which methods can be added to a gadget. Removing a single method is not possible without further ado.
Code: Select all
;-TOP
; Comment : osx Helper
; Author : mk-soft
; Version : v1.01
; Create : 13.04.2019
; Update :
; OS : macOS
;- Begin Module
DeclareModule osxClassHelper
; Create a new class with NSObject and Method
Declare CreateClass(ClassName.s, RegisterName.s, *Callback, Args.s) ; Result Object (Instance)
Declare DisposeClass(ClassName.s)
; Add Method to Gadget
Declare AddGadgetMethod(Gadget, RegisterName.s, *Callback, Args.s) ; True or False
Declare DisposeGadgetMethods(Gadget)
EndDeclareModule
; ---
Module osxClassHelper
EnableExplicit
ImportC ""
objc_disposeClassPair(*Class)
EndImport
;-- Structure
Structure udtClassObject
*Class
*Object
EndStructure
Structure udtMethodList
Name.s
*Method
EndStructure
Structure udtGadgetObject
*GadgetID
*OldGadgetClass
*NewGadgetClass
Map MethodList.udtMethodList()
EndStructure
;-- Globals
Global NewMap ClassObject.udtClassObject()
Global NewMap GadgetObject.udtGadgetObject()
;- New Object with instance
Procedure CreateClass(ClassName.s, RegisterName.s, *Callback, Args.s)
Protected *Class, *Method, *Object
*Class = objc_allocateClassPair_(objc_getClass_("NSObject"), ClassName, 0)
If Not *Class
ProcedureReturn 0
EndIf
*Method = class_addMethod_(*Class, sel_registerName_(RegisterName), *Callback, Args)
If Not *Method
objc_disposeClassPair(*Class)
ProcedureReturn 0
EndIf
If Not objc_registerClassPair_(*Class)
objc_disposeClassPair(*Class)
ProcedureReturn 0
EndIf
*Object = CocoaMessage(0, 0, ClassName + " new")
If Not *Object
objc_disposeClassPair(*Class)
ProcedureReturn 0
EndIf
ClassObject(ClassName)\Class = *Class
ClassObject()\Object = *Object
ProcedureReturn *Object
EndProcedure
Procedure DisposeClass(ClassName.s)
If FindMapElement(ClassObject(), ClassName)
CocoaMessage(0, ClassObject()\Object, "release")
objc_disposeClassPair(ClassObject()\Class)
DeleteMapElement(ClassObject())
EndIf
EndProcedure
;- Add Method to Gadget
Procedure AddGadgetMethod(Gadget, RegisterName.s, *Callback, Args.s)
With GadgetObject()
If Not FindMapElement(GadgetObject(), Hex(Gadget))
AddMapElement(GadgetObject(), Hex(Gadget))
\GadgetID = GadgetID(Gadget)
\OldGadgetClass = CocoaMessage(0, GadgetID(Gadget), "class")
\NewGadgetClass = objc_allocateClassPair_(\OldGadgetClass, "classGadget_" + \GadgetID, 0)
If Not \NewGadgetClass
ProcedureReturn 0
EndIf
objc_registerClassPair_(\NewGadgetClass)
object_setClass_(\GadgetID, \NewGadgetClass)
EndIf
If Not FindMapElement(\MethodList(), RegisterName)
AddMapElement(\MethodList(), RegisterName)
\MethodList()\Name = RegisterName
\MethodList()\Method = class_addMethod_(\NewGadgetClass, sel_registerName_(RegisterName), *Callback, Args)
If Not \MethodList()\Method
DeleteMapElement(\MethodList())
If MapSize(\MethodList()) = 0
object_setClass_(\GadgetID, \OldGadgetClass)
objc_disposeClassPair(\NewGadgetClass)
DeleteMapElement(GadgetObject())
EndIf
ProcedureReturn 0
EndIf
EndIf
ProcedureReturn \MethodList()\Method
EndWith
EndProcedure
Procedure DisposeGadgetMethods(Gadget)
With GadgetObject()
If FindMapElement(GadgetObject(), Hex(Gadget))
object_setClass_(\GadgetID, \OldGadgetClass)
objc_disposeClassPair(\NewGadgetClass)
DeleteMapElement(GadgetObject())
EndIf
EndWith
EndProcedure
EndModule
;- End Module
CompilerIf #PB_Compiler_IsMainFile
UseModule osxClassHelper
EnumerationBinary
#NSKeyValueObservingOptionNew
#NSKeyValueObservingOptionOld
EndEnumeration
Global *NSKeyValueChangeNewKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeNewKey")
Global *NSKeyValueChangeOldKey.Integer = dlsym_(#RTLD_DEFAULT, "NSKeyValueChangeOldKey")
Enumeration #PB_Event_FirstCustomValue
#EventChangeAppearance
EndEnumeration
ProcedureC KVO(obj, sel, keyPath, object, change, context)
Select PeekS(CocoaMessage(0, keyPath, "UTF8String"), -1, #PB_UTF8)
Case "effectiveAppearance":
CocoaMessage(0, 0, "NSAppearance setCurrentAppearance:", CocoaMessage(0, change, "objectForKey:", *NSKeyValueChangeNewKey\i))
PostEvent(#EventChangeAppearance)
EndSelect
EndProcedure
; Create Key-Value Observer class (PB_KVO)
KVO = CreateClass("PB_KVO", "observeValueForKeyPath:ofObject:change:context:", @KVO(), "v@:@@@^v")
; add observer
Global NSApp.i = CocoaMessage(0, 0, "NSApplication sharedApplication")
CocoaMessage(0, NSApp, "addObserver:", KVO, "forKeyPath:$", @"effectiveAppearance", "options:", #NSKeyValueObservingOptionNew, "context:", #nil)
; ---
Procedure NSColorToRGB(NSColor)
Protected.cgfloat red, green, blue
Protected r, g, b, a
Protected nscolorspace, rgb
nscolorspace = CocoaMessage(0, nscolor, "colorUsingColorSpaceName:$", @"NSCalibratedRGBColorSpace")
If nscolorspace
CocoaMessage(@red, nscolorspace, "redComponent")
CocoaMessage(@green, nscolorspace, "greenComponent")
CocoaMessage(@blue, nscolorspace, "blueComponent")
rgb = RGB(red * 255.0, green * 255.0, blue * 255.0)
ProcedureReturn rgb
EndIf
EndProcedure
Procedure NSColorByNameToRGB(NSColorName.s)
Protected.cgfloat red, green, blue
Protected r, g, b, a
Protected nscolorspace, rgb
nscolorspace = CocoaMessage(0, CocoaMessage(0, 0, "NSColor " + NSColorName), "colorUsingColorSpaceName:$", @"NSCalibratedRGBColorSpace")
If nscolorspace
CocoaMessage(@red, nscolorspace, "redComponent")
CocoaMessage(@green, nscolorspace, "greenComponent")
CocoaMessage(@blue, nscolorspace, "blueComponent")
rgb = RGB(red * 255.0, green * 255.0, blue * 255.0)
ProcedureReturn rgb
EndIf
EndProcedure
; ---
Global Event, text_color, control_background_color , buttonimage, buttonimage_pressed
Procedure UpdateColors()
text_color = NSColorByNameToRGB("textColor")
control_background_color = NSColorByNameToRGB("windowBackgroundColor")
If StartDrawing(CanvasOutput(0))
Box(0, 0, OutputWidth(), OutputHeight(), control_background_color)
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(10, 10, "Update colors", text_color)
StopDrawing()
EndIf
EndProcedure
; ---
ProcedureC UpdateLayer(SubclassedButton.I, Selector.I)
Protected cell.i, layer.i, image.i
Protected Rect.CGRect
cell = CocoaMessage(0, SubclassedButton, "cell")
layer = CocoaMessage(0, SubclassedButton, "layer", 0 )
If CocoaMessage(0, cell, "isHighlighted")
;image =CocoaMessage(0, 0, "NSImage imageNamed:$", @"button_pressed.png")
CocoaMessage(0, layer, "setContents:", buttonimage_pressed)
Else
;image =CocoaMessage(0, 0, "NSImage imageNamed:$", @"button.png")
CocoaMessage(0, layer, "setContents:", buttonimage)
CocoaMessage(@Rect, layer, "contentsCenter")
Rect\origin\x =0.5
Rect\origin\y =0.5
Rect\size\width =0
Rect\size\height =0
CocoaMessage(0, layer, "setContentsCenter:@", @Rect)
EndIf
EndProcedure
; ---
buttonimage = CreateImage(0, 140, 30, 32, $FF901E)
buttonimage_pressed = CreateImage(1, 140, 30, 32, $CD7418)
OpenWindow(0, #PB_Ignore, #PB_Ignore, 300, 150, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20, #PB_Canvas_Container)
ButtonGadget(1, 70, 40, 140, 30, "Click me!")
SetGadgetFont(1, LoadFont(0, "Apple Chancery", 18))
CloseGadgetList()
CocoaMessage(0, GadgetID(1), "setWantsLayer:", #YES)
AddGadgetMethod(1, "updateLayer", @UpdateLayer(), "v@")
UpdateColors()
Repeat
Event = WaitWindowEvent()
If Event = #EventChangeAppearance
UpdateColors()
EndIf
Until Event = #PB_Event_CloseWindow
; remove observer
CocoaMessage(0, NSApp, "removeObserver:", KVO, "forKeyPath:$", @"effectiveAppearance")
DisposeClass("PB_KVO")
DisposeGadgetMethods(1)
CompilerEndIf