Page 1 of 1

Plain Cocoa coding

Posted: Sat Jun 07, 2014 9:17 pm
by Danilo
How to add a button or listbox to a cocoa window using plain API?

Code: Select all

CocoaMessage(@app,0,"NSApplication sharedApplication")
CocoaMessage(@win,0,"NSWindow alloc")
Debug app
Debug win

#NSBorderlessWindowMask = 0
#NSTitledWindowMask = 1 << 0
#NSClosableWindowMask = 1 << 1
#NSMiniaturizableWindowMask = 1 << 2
#NSResizableWindowMask = 1 << 3
#NSTexturedBackgroundWindowMask = 1 << 8

size.NSSize
size\width = 800
size\height = 600
rect.NSRect
rect\origin\x = 100
rect\origin\y = 100
rect\size\width = 800
rect\size\height = 600

#MASK = #NSTitledWindowMask | #NSClosableWindowMask | #NSMiniaturizableWindowMask | #NSResizableWindowMask

CocoaMessage(0,win,"initWithContentRect:@",@rect,"styleMask:",#MASK,"backing:",2,"defer:",#NO)
CocoaMessage(0,win,"makeKeyWindow")
CocoaMessage(0,Win,"makeKeyAndOrderFront:",app)

CocoaMessage(0,win,"setTitle:$",@"My Window")

CocoaMessage(0,win,"setMinSize:",size)

CocoaMessage(0,win,"center")
CocoaMessage(0,win,"update")
CocoaMessage(0,win,"display")

CocoaMessage(0,win,"setPreventsApplicationTerminationWhenModal:",#NO)
CocoaMessage(0,win,"setReleasedWhenClosed:",#YES)

; how to add a button or listview here?

CocoaMessage(0,app,"run")

;CocoaMessage(0,app,"runModalForWindow:",win)
;CocoaMessage(0,app,"terminate:",app)
The window is also missing the delegate. In Objective-C I did it like this:

Code: Select all

#include <cocoa/cocoa.h>

@interface MyWindowDelegate : NSObject<NSWindowDelegate>
@end

@implementation MyWindowDelegate
    - (BOOL)windowShouldClose:(id)window {
        [window release];
        NSApplication *app = [NSApplication sharedApplication];
        [app terminate:app];
        return YES;
    }

    - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
        return NSTerminateNow;
    }

@end

int CreateWindow(int x, int y, int width, int height, String title) {
    NSApplication *app = [NSApplication sharedApplication];
    unsigned int styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask;

    NSWindow *w1 = [ [NSWindow alloc]
                     initWithContentRect:NSMakeRect( x, y, width, height )
                     styleMask:styleMask
                     backing:NSBackingStoreBuffered
                     defer:NO
                   ];

    MyWindowDelegate *delegate = [[MyWindowDelegate alloc] init];
    [w1 setDelegate:delegate];
    [w1 makeKeyWindow];
    [w1 makeKeyAndOrderFront:app];
    [w1 setTitle:title.ToNSString()];
    [w1 setAcceptsMouseMovedEvents:YES];
    [w1 setPreventsApplicationTerminationWhenModal:NO];
    [w1 setReleasedWhenClosed:YES];

    [w1 center];
    [w1 update];
    [w1 display];

    //[app runModalForWindow:w1];
    [app run];
    //[w1 release];

    return 0;
}

Re: Plain Cocoa coding

Posted: Sun Jun 08, 2014 7:34 am
by wilbert
When you create a window, you also have to create a content view and set it using the NSWindow method setContentView:
To add things like buttons, you use the NSView method addSubview:

Creating a delegate is difficult.
Most examples on the forum use the delegate class PureBasic itself uses and add a method to that class using the C function class_addMethod .
To create a class yourself, you need objc_allocateClassPair, add the methods you need and register it using objc_registerClassPair .
information about these low level C functions can be found in the Objective-C Runtime Reference .

Re: Plain Cocoa coding

Posted: Sun Jun 08, 2014 5:26 pm
by Danilo
wilbert wrote:When you create a window, you also have to create a content view and set it using the NSWindow method setContentView:
To add things like buttons, you use the NSView method addSubview:
Thanks, that helped. Just made a small test:

Code: Select all

EnableExplicit

Procedure InitRect(*rect.NSRect,x,y,width,height)
    If *rect
        *rect\origin\x = x
        *rect\origin\y = y
        *rect\size\width = width
        *rect\size\height = height
    EndIf
EndProcedure

Procedure InitSize(*size.NSSize,width,height)
    If *size
        *size\width = width
        *size\height = height
    EndIf
EndProcedure

Procedure PushButton(x,y,width,height,title.s)
    Protected btn, rect.NSRect
    CocoaMessage(@btn,0,"NSButton alloc")
    If btn
        ;CocoaMessage(0,btn,"init")
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@btn,btn,"initWithFrame:@",@rect)
        CocoaMessage(0,btn,"setTitle:$",@title)
        CocoaMessage(0,btn,"setButtonType:",0)
        CocoaMessage(0,btn,"setBezelStyle:",1)
    EndIf
    ProcedureReturn btn
EndProcedure

Procedure StickyButton(x,y,width,height,title.s)
    Protected btn, rect.NSRect
    CocoaMessage(@btn,0,"NSButton alloc")
    If btn
        ;CocoaMessage(0,btn,"init")
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@btn,btn,"initWithFrame:@",@rect)
        CocoaMessage(0,btn,"setTitle:$",@title)
        CocoaMessage(0,btn,"setButtonType:",1)
        CocoaMessage(0,btn,"setBezelStyle:",1)
    EndIf
    ProcedureReturn btn
EndProcedure

Procedure Checkbox(x,y,width,height,title.s)
    Protected btn, rect.NSRect
    CocoaMessage(@btn,0,"NSButton alloc")
    If btn
        ;CocoaMessage(0,btn,"init")
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@btn,btn,"initWithFrame:@",@rect)
        CocoaMessage(0,btn,"setTitle:$",@title)
        CocoaMessage(0,btn,"setButtonType:",3)
        CocoaMessage(0,btn,"setBezelStyle:",1)
    EndIf
    ProcedureReturn btn
EndProcedure

Procedure RadioButton(x,y,width,height,title.s)
    Protected btn, rect.NSRect
    CocoaMessage(@btn,0,"NSButton alloc")
    If btn
        ;CocoaMessage(0,btn,"init")
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@btn,btn,"initWithFrame:@",@rect)
        CocoaMessage(0,btn,"setTitle:$",@title)
        CocoaMessage(0,btn,"setButtonType:",4)
        CocoaMessage(0,btn,"setBezelStyle:",1)
    EndIf
    ProcedureReturn btn
EndProcedure

Procedure Frame(x,y,width,height,title.s)
    Protected box, rect.NSRect
    CocoaMessage(@box,0,"NSBox alloc")
    If box
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@box,box,"initWithFrame:@",@rect)
        CocoaMessage(0,box,"setTitle:$",@title)
    EndIf
    ProcedureReturn box
EndProcedure

Procedure RadioButtonGroup(x,y,width,height,names.s)
    Protected matrix, rect.NSRect, size.NSSize, index, cell, name.s
    
    CocoaMessage(@matrix,0,"NSMatrix alloc")
    If matrix
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@matrix,matrix,"initWithFrame:@",@rect)
        CocoaMessage(@cell,0,"NSButtonCell alloc")
        CocoaMessage(0,cell,"init")
        CocoaMessage(0,cell,"setButtonType:",4)
        CocoaMessage(0,cell,"setBezelStyle:",1)
        CocoaMessage(0,cell,"setTitle:$",@"----------------------------------")
        CocoaMessage(@matrix,matrix,"initWithFrame:@",@rect,"mode:",0,"prototype:",cell,"numberOfRows:",0,"numberOfColumns:",0)
        CocoaMessage(0,matrix,"setAutoscroll:",#YES)
        CocoaMessage(0,matrix,"setAutorecalculatesCellSize:",#YES)
        ;CocoaMessage(0,cell,"release")

        index = 1
        Repeat
            name = StringField(names,index,"|")
            If name
                CocoaMessage(0,matrix,"addRow")
                CocoaMessage(@cell,matrix,"cellAtRow:",index-1,"column:",0)
                If cell
                    CocoaMessage(0,cell,"setTitle:$",@name)
                EndIf
            EndIf
            index + 1
        Until name = ""
        CocoaMessage(0,matrix,"setAutorecalculatesCellSize:",#YES)
    EndIf
    ProcedureReturn matrix
EndProcedure

Procedure CheckboxGroup(x,y,width,height,names.s)
    Protected matrix, rect.NSRect, size.NSSize, index, cell, name.s
    Protected space$ = Space(500)
    CocoaMessage(@matrix,0,"NSMatrix alloc")
    If matrix
        InitRect(@rect,x,y,width,height)
        CocoaMessage(@matrix,matrix,"initWithFrame:@",@rect)
        CocoaMessage(@cell,0,"NSButtonCell alloc")
        CocoaMessage(0,cell,"init")
        CocoaMessage(0,cell,"setButtonType:",3)
        CocoaMessage(0,cell,"setBezelStyle:",1)
        CocoaMessage(0,cell,"setTitle:$",@space$)
        CocoaMessage(@matrix,matrix,"initWithFrame:@",@rect,"mode:",0,"prototype:",cell,"numberOfRows:",0,"numberOfColumns:",0)
        CocoaMessage(0,matrix,"setAutoscroll:",#YES)
        CocoaMessage(0,matrix,"setAutorecalculatesCellSize:",#YES)
        ;CocoaMessage(0,cell,"release")
        index = 1
        Repeat
            name = StringField(names,index,"|")
            If name
                CocoaMessage(0,matrix,"addRow")
                CocoaMessage(@cell,matrix,"cellAtRow:",index-1,"column:",0)
                If cell
                   CocoaMessage(0,cell,"setTitle:$",@name)
                EndIf
            EndIf
            index + 1
        Until name = ""
        CocoaMessage(0,matrix,"setAutorecalculatesCellSize:",#YES)
    EndIf
    ProcedureReturn matrix
EndProcedure



Procedure App()
    Protected app
    CocoaMessage(@app,0,"NSApplication sharedApplication")
    ProcedureReturn app
EndProcedure

Procedure Window(x,y,width,height,title.s,mask,center=0)
    Protected size.NSSize, rect.NSRect, win, view
    CocoaMessage(@win,0,"NSWindow alloc")
    If win
        InitRect(@rect,x,y,width,height)
        CocoaMessage(0,win,"initWithContentRect:@",@rect,"styleMask:",mask,"backing:",2,"defer:",#NO)
        CocoaMessage(0,win,"makeKeyWindow")
        CocoaMessage(0,Win,"makeKeyAndOrderFront:",App())
        CocoaMessage(0,win,"setTitle:$",@title)
        ;InitSize(@size,width,height)
        ;CocoaMessage(0,win,"setMinSize:",size)
        
        ;CocoaMessage(0,win,"setPreventsApplicationTerminationWhenModal:",#NO)
        ;CocoaMessage(0,win,"setReleasedWhenClosed:",#YES)
        
        CocoaMessage(@view,0,"NSView alloc")
        If view
            InitRect(@rect,0,0,width,height)
            CocoaMessage(@view,view,"initWithFrame:@",@rect)
            CocoaMessage(0,win,"setContentView:",view)
        EndIf
        
        If center
            CocoaMessage(0,win,"center")
        EndIf
        CocoaMessage(0,win,"update")
        CocoaMessage(0,win,"display")
    EndIf
    ProcedureReturn win
EndProcedure

Procedure AddGadgetToWindow(win,gadget)
    Protected view
    If win And gadget
        CocoaMessage(@view,win,"contentView")
        If view
            CocoaMessage(0,view,"addSubview:",gadget)
        EndIf
    EndIf
EndProcedure

Procedure AddGadgetToFrame(frame,gadget)
    If frame And gadget
        CocoaMessage(0,frame,"addSubview:",gadget)
    EndIf
EndProcedure

Procedure AddGadgetToMatrix(matrix,gadget,row,column)
    If matrix And gadget
        CocoaMessage(0,matrix,"putCell:",gadget,"atRow:",row,"column:",column)
    EndIf
EndProcedure

;--------------------------------------------------------------------

#NSBorderlessWindowMask = 0
#NSTitledWindowMask = 1 << 0
#NSClosableWindowMask = 1 << 1
#NSMiniaturizableWindowMask = 1 << 2
#NSResizableWindowMask = 1 << 3
#NSTexturedBackgroundWindowMask = 1 << 8


#MASK = #NSTitledWindowMask | #NSClosableWindowMask | #NSMiniaturizableWindowMask |
        #NSResizableWindowMask ;| #NSTexturedBackgroundWindowMask

Define win, frame

win = Window(100,100,800,600,"My Window",#MASK,1)
If win
    AddGadgetToWindow(win,PushButton(10,10,150,25,"Push Button"))
    AddGadgetToWindow(win,StickyButton(10,45,150,25,"Sticky Button"))
    AddGadgetToWindow(win,Checkbox(10,80,150,25,"Checkbox"))
    AddGadgetToWindow(win,RadioButton(10,115,150,25,"Radio Button"))
    frame = Frame(10,150,450,200,"Frame")
    If frame
        AddGadgetToFrame(frame,PushButton(10,10,120,25,"Button 1"))
        AddGadgetToFrame(frame,PushButton(10,45,120,25,"Button 2"))
        AddGadgetToFrame(frame,RadioButtonGroup(140,10,120,160,"RadioBtn 1|RadioBtn 2|RadioBtn 3|RadioBtn 4|RadioBtn 5"))
        AddGadgetToFrame(frame,CheckboxGroup(270,10,120,160,"CheckBox 1|CheckBox 2|CheckBox 3|CheckBox 4|CheckBox 5"))
        AddGadgetToWindow(win,frame)
    EndIf
EndIf

CocoaMessage(0,App(),"run")
wilbert wrote:Creating a delegate is difficult.
Most examples on the forum use the delegate class PureBasic itself uses and add a method to that class using the C function class_addMethod .
To create a class yourself, you need objc_allocateClassPair, add the methods you need and register it using objc_registerClassPair .
information about these low level C functions can be found in the Objective-C Runtime Reference .
That comes next, thanks again! :)

Re: Plain Cocoa coding

Posted: Sun Jun 08, 2014 6:06 pm
by Wolfram
Hi,

what is the advantage by doing it in this way instead of PB code?

Re: Plain Cocoa coding

Posted: Sun Jun 08, 2014 9:09 pm
by Danilo
Wolfram wrote:what is the advantage by doing it in this way instead of PB code?
It is more fun. ;)

Re: Plain Cocoa coding

Posted: Sat Jun 15, 2019 11:43 pm
by Justin
Hi everyone after 5 years :D
Any progress on this?
I am just trying to quit the application when clicking the close button, in Xcode I simply put in the AppDelegate implementation:

Code: Select all

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
    return YES;
}
In PB I setup a callback for that delegate and it is called, but returning #YES has no effect, any ideas?

Code: Select all

EnableExplicit

ImportC ""
  sel_registerName(MethodName.p-UTF8)
  class_addMethod(Class.I, Selector.I, Implementation.I, Types.p-UTF8)
EndImport

Define.i app, win, appDelegate, appDelegateClass
Define.NSSize size
Define.NSRect rect
Define.i selAppShouldTerminateALWC

ProcedureC.b appShouldTerminateALWC(obj.i, sel.i, app.i)
	Debug "terminate ALWC " + Str(app)
	
	ProcedureReturn #YES
EndProcedure

CocoaMessage(@app, 0, "NSApplication sharedApplication")
Debug app
CocoaMessage(@appDelegate, app, "delegate")
CocoaMessage(@appDelegateClass, appDelegate, "class")

selAppShouldTerminateALWC = sel_registerName_("applicationShouldTerminateAfterLastWindowClosed:")

class_addMethod_(appDelegateClass, selAppShouldTerminateALWC, @appShouldTerminateALWC(), "c@:@")

CocoaMessage(@win,0,"NSWindow alloc")

#NSBorderlessWindowMask = 0
#NSTitledWindowMask = 1 << 0
#NSClosableWindowMask = 1 << 1
#NSMiniaturizableWindowMask = 1 << 2
#NSResizableWindowMask = 1 << 3
#NSTexturedBackgroundWindowMask = 1 << 8

size\width = 100
size\height = 100

rect\origin\x = 100
rect\origin\y = 100
rect\size\width = 800
rect\size\height = 600

#MASK = #NSTitledWindowMask | #NSClosableWindowMask | #NSMiniaturizableWindowMask | #NSResizableWindowMask

CocoaMessage(0, win, "initWithContentRect:@", @rect, "styleMask:", #MASK, "backing:", 2, "defer:", #NO)
CocoaMessage(0, win,"makeKeyWindow")
CocoaMessage(0, win,"makeKeyAndOrderFront:",app)

CocoaMessage(0, win, "setTitle:$", @"My Window")

CocoaMessage(0, win, "setMinSize:@",@size)

CocoaMessage(0, win, "center")
CocoaMessage(0, win, "update")
CocoaMessage(0, win, "display")

CocoaMessage(0, win, "setPreventsApplicationTerminationWhenModal:",#NO)
CocoaMessage(0, win, "setReleasedWhenClosed:",#YES)
 
CocoaMessage(0, app, "run")

; CocoaMessage(0,app,"runModalForWindow:",win)
; CocoaMessage(0,app,"terminate:",app)

Re: Plain Cocoa coding

Posted: Sun Jun 16, 2019 3:44 pm
by Justin
I created a window and app delegates like Wilbert said and the functions are called.
Now the window closes when I click the close button, but the PB debugger quits unexpectedly afterwards.
Closing with the top menu entry does not work. Any help?

Update: Ending the program in appShouldTerminate() fixes the error.

Code: Select all

EnableExplicit

Define.i app, win, appDelegate, appDelegateClass
Define.NSSize size
Define.NSRect rect
Define.i myAppDelegateClass, myAppDelegate
Define.i myWindowDelegateClass, myWindowDelegate
Define.i pool

ProcedureC appShouldTerminate(obj.i, sel.i, app.i)
	Debug "appShouldTerminate"
	End
	ProcedureReturn #YES
EndProcedure

ProcedureC appShouldTerminateALWC(obj.i, sel.i, app.i)
	Debug "appShouldTerminateALWC"
	
	ProcedureReturn #YES
EndProcedure

ProcedureC appWillTerminate(obj.i, sel.i, ntn.i)
	Debug "appWillTerminate"
EndProcedure

ProcedureC winShouldClose(obj.i, sel.i, win.i)
	Define.i app
	
	Debug "winShouldClose"
	
	CocoaMessage(0, win, "release")
	CocoaMessage(@app, 0, "NSApplication sharedApplication")
	CocoaMessage(0, app, "terminate:", app)
	
	ProcedureReturn #YES
EndProcedure

CocoaMessage(@pool, 0, "NSAutoreleasePool alloc")
CocoaMessage(0, pool, "init")

CocoaMessage(@app, 0, "NSApplication sharedApplication")

; CocoaMessage(@appDelegate, app, "delegate")
; CocoaMessage(@appDelegateClass, appDelegate, "class")

myAppDelegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myAppDelegate", 0)
class_addProtocol_(myAppDelegateClass, objc_getProtocol_("NSApplicationDelegate"))

class_addMethod_(myAppDelegateClass, sel_registerName_("applicationShouldTerminate:"), @appShouldTerminate(), "L@:@")
class_addMethod_(myAppDelegateClass, sel_registerName_("applicationShouldTerminateAfterLastWindowClosed:"), @appShouldTerminateALWC(), "c@:@")
class_addMethod_(myAppDelegateClass, sel_registerName_("applicationWillTerminate:"), @appWillTerminate(), "v@:@")

objc_registerClassPair_(myAppDelegateClass)
myAppDelegate = class_createInstance_(myAppDelegateClass, 0)
CocoaMessage(0, app, "setDelegate:", myAppDelegate)

CocoaMessage(@win, 0, "NSWindow alloc")
myWindowDelegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myWindowDelegate", 0)
class_addProtocol_(myWindowDelegateClass, objc_getProtocol_("NSWindowDelegate"))
class_addMethod_(myWindowDelegateClass, sel_registerName_("windowShouldClose:"), @winShouldClose(), "c@:@")
; class_addMethod_(myWindowDelegateClass, sel_registerName_("applicationShouldTerminate:"), @appShouldTerminate(), "c@:@")
myWindowDelegate = class_createInstance_(myWindowDelegateClass, 0)

#NSBorderlessWindowMask = 0
#NSTitledWindowMask = 1 << 0
#NSClosableWindowMask = 1 << 1
#NSMiniaturizableWindowMask = 1 << 2
#NSResizableWindowMask = 1 << 3
#NSTexturedBackgroundWindowMask = 1 << 8

size\width = 100
size\height = 100

rect\origin\x = 100
rect\origin\y = 100
rect\size\width = 800
rect\size\height = 600

#MASK = #NSTitledWindowMask | #NSClosableWindowMask | #NSMiniaturizableWindowMask | #NSResizableWindowMask

CocoaMessage(0, win, "initWithContentRect:@", @rect, "styleMask:", #MASK, "backing:", 2, "defer:", #NO)
CocoaMessage(0, win, "makeKeyWindow")
CocoaMessage(0, win, "makeKeyAndOrderFront:", app)
CocoaMessage(0, win, "setTitle:$", @"My Window")
CocoaMessage(0, win, "setMinSize:@", @size)
CocoaMessage(0, win, "center")
CocoaMessage(0, win, "update")
CocoaMessage(0, win, "display")

CocoaMessage(0, win, "setPreventsApplicationTerminationWhenModal:", #NO)
CocoaMessage(0, win, "setReleasedWhenClosed:", #YES)

CocoaMessage(0, win, "setDelegate:", myWindowDelegate)

CocoaMessage(0, app, "run")
CocoaMessage(0, pool, "drain")

; Debug "END"
; CocoaMessage(0, app, "runModalForWindow:",win)
; CocoaMessage(0, app, "terminate:", app)

Re: Plain Cocoa coding

Posted: Sun Jun 16, 2019 7:58 pm
by Justin
Just found that ending the program in appShouldTerminate() gets rid of the debugger error.
Now I only need to handle the quit command in the main menu, has anyone tried before?

Re: Plain Cocoa coding

Posted: Wed Jun 19, 2019 8:57 pm
by Justin
Well, finally I did it. The solution was to set the quit menu item "action" with a pb callback that terminates the app.

Re: Plain Cocoa coding

Posted: Tue Sep 10, 2019 6:03 am
by Rinzwind
For curiosity: applicationWillTerminate is never called?

Re: Plain Cocoa coding

Posted: Tue Sep 10, 2019 6:35 am
by wilbert
Rinzwind wrote:For curiosity: applicationWillTerminate is never called?
It's because the source code contains "End" within the appShouldTerminate procedure.

Re: Plain Cocoa coding

Posted: Sun Jun 13, 2021 2:14 pm
by mestnyi
Can anyone comment on what's going on here?

Code: Select all

EnableExplicit

Define.i app, win, appDelegate, appDelegateClass
Define.NSSize size
Define.NSRect rect
Define.i myAppDelegateClass, myAppDelegate
Define.i myWindowDelegateClass, myWindowDelegate
Global.i pool

ProcedureC winShouldClose(obj.i, sel.i, win.i) ; call 1
	Define.i app
	
	Debug "winShouldClose - " + obj +" "+sel +" - "+win
	
	CocoaMessage(0, win, "release")
	;CocoaMessage(@app, 0, "NSApplication sharedApplication")
	;CocoaMessage(0, app, "terminate:", app)
	
	ProcedureReturn #YES
EndProcedure

ProcedureC appShouldTerminateALWC(obj.i, sel.i, app.i) ; call 2
	Debug "appShouldTerminateALWC - " + obj +" - "+sel +" - "+app
	
	ProcedureReturn #YES
EndProcedure

ProcedureC appShouldTerminate(obj.i, sel.i, app.i) ; call 3
	Debug "appShouldTerminate - " + obj +" - "+sel +" - "+app
	
	ProcedureReturn #YES
EndProcedure

ProcedureC appWillTerminate(obj.i, sel.i, ntn.i) ; call 4
	Debug "appWillTerminate - " + obj +" - "+sel +" - "+ntn
	
	End
	ProcedureReturn #YES
EndProcedure


CocoaMessage(@pool, 0, "NSAutoreleasePool alloc")
CocoaMessage(0, pool, "init")

CocoaMessage(@app, 0, "NSApplication sharedApplication")

; CocoaMessage(@appDelegate, app, "delegate")
; CocoaMessage(@appDelegateClass, appDelegate, "class")

myAppDelegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myAppDelegate", 0)
class_addProtocol_(myAppDelegateClass, objc_getProtocol_("NSApplicationDelegate"))

class_addMethod_(myAppDelegateClass, sel_registerName_("applicationShouldTerminate:"), @appShouldTerminate(), "L@:@")
class_addMethod_(myAppDelegateClass, sel_registerName_("applicationShouldTerminateAfterLastWindowClosed:"), @appShouldTerminateALWC(), "c@:@")
class_addMethod_(myAppDelegateClass, sel_registerName_("applicationWillTerminate:"), @appWillTerminate(), "v@:@")

objc_registerClassPair_(myAppDelegateClass)
myAppDelegate = class_createInstance_(myAppDelegateClass, 0)
CocoaMessage(0, app, "setDelegate:", myAppDelegate)

CocoaMessage(@win, 0, "NSWindow alloc")
myWindowDelegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "myWindowDelegate", 0)
class_addProtocol_(myWindowDelegateClass, objc_getProtocol_("NSWindowDelegate"))
class_addMethod_(myWindowDelegateClass, sel_registerName_("windowShouldClose:"), @winShouldClose(), "c@:@")
;class_addMethod_(myWindowDelegateClass, sel_registerName_("applicationShouldTerminate:"), @appShouldTerminate(), "c@:@")
myWindowDelegate = class_createInstance_(myWindowDelegateClass, 0)

#NSBorderlessWindowMask = 0
#NSTitledWindowMask = 1 << 0
#NSClosableWindowMask = 1 << 1
#NSMiniaturizableWindowMask = 1 << 2
#NSResizableWindowMask = 1 << 3
#NSTexturedBackgroundWindowMask = 1 << 8

size\width = 100
size\height = 100

rect\origin\x = 100
rect\origin\y = 100
rect\size\width = 800
rect\size\height = 600

#MASK = #NSTitledWindowMask | #NSClosableWindowMask | #NSMiniaturizableWindowMask | #NSResizableWindowMask

CocoaMessage(0, win, "initWithContentRect:@", @rect, "styleMask:", #MASK, "backing:", 2, "defer:", #NO)
CocoaMessage(0, win, "makeKeyWindow")
CocoaMessage(0, win, "makeKeyAndOrderFront:", app)
CocoaMessage(0, win, "setTitle:$", @"My Window")
CocoaMessage(0, win, "setMinSize:@", @size)
CocoaMessage(0, win, "center")
CocoaMessage(0, win, "update")
CocoaMessage(0, win, "display")

CocoaMessage(0, win, "setPreventsApplicationTerminationWhenModal:", #NO)
CocoaMessage(0, win, "setReleasedWhenClosed:", #YES)

CocoaMessage(0, win, "setDelegate:", myWindowDelegate)

CocoaMessage(0, app, "run")
CocoaMessage(0, pool, "drain")

; Debug "END"
; CocoaMessage(0, app, "runModalForWindow:",win)
; CocoaMessage(0, app, "terminate:", app)

Re: Plain Cocoa coding

Posted: Sun Jun 13, 2021 3:25 pm
by mk-soft
It's all Objective-C. So everything is object-oriented.

To create or extend objects and classes, there is the API. To call the objects, there is the PB function CocoMessage.

Result_as_Integer_or_Object = CocoaMessage(@result_as_other_types, object, method, param1_value, param2_name, param2_value, ...)

Furthermore, read every single step in the Apple Developer documentation.

To read out the methods of an object, I wrote the following dump function, because sometimes the names of the methods do not always run the same as in the Apple documentation.

Code: Select all

;-TOP Dump Object Methods

; by mk-soft, 29.12.2019, v1.06

Structure udtArray
  i.i[0]
EndStructure

ImportC ""
  class_copyMethodList(*Class, *p_methodCount) ; -> An array of pointers of type Method describing
                                            ;    the instance methods implemented by the class
                                            ;    Any instance methods implemented by superclasses are Not included
                                            ;    You must free the array with free()
  
  class_getName(*Class) ; -> UnsafePointer<Int8> -> *string
  sel_getName(*Selector) ; -> const char *
  method_getName(*Method) ; -> Selector
  method_getTypeEncoding(*Method) ; -> const char *
  method_getReturnType(*Method, *dst, dst_len) ; -> void
  method_getNumberOfArguments(*Method) ; -> unsigned int
  method_getArgumentType(*Method, index, *dst, dst_len) ; -> void
  
  NSGetSizeAndAlignment(*StringPtr, *p_size, *p_align) ; -> const char *
                                                       ;    Obtains the actual size and the aligned size of an encoded type.
EndImport

; ----

Procedure.s GetArgumentType(*String)
  Protected r1.s, arg.s, size.i
  arg = PeekS(*String, -1, #PB_UTF8)
  r1 + arg + " - "
  
  If Left(arg, 1) = "^"
    r1 + "A pointer to type of "
    arg = Mid(arg, 2)
  EndIf
  
  Select arg
    Case "c" : r1 + "A char "
    Case "i" : r1 + "An int "
    Case "s" : r1 + "A short "
    Case "l" : r1 + "A long "
    Case "q" : r1 + "A long long"
    Case "C" : r1 + "An unsigned char "
    Case "I" : r1 + "An unsigned int "
    Case "S" : r1 + "An unsigned short "
    Case "L" : r1 + "An unsigned long "
    Case "Q" : r1 + "An unsigned long long "
    Case "f" : r1 + "A float "
    Case "d" : r1 + "A double "
    Case "B" : r1 + "A C++ bool Or a C99 _Bool "
    Case "v" : r1 + "A void"
    Case "*" : r1 + "A character string (char *) "
    Case "@" : r1 + "An object (whether statically typed Or typed id) "
    Case "#" : r1 + "A class object (Class) "
    Case ":" : r1 + "A method selector (SEL) "
    Default:
      NSGetSizeAndAlignment(*String, @size, #Null)
      r1 + "[" + Str(size) + " bytes]"
  EndSelect
  
  If Right(arg, 1) = "?"
    r1 + "An unknown type (e.g. function pointer)"
  EndIf
  
  ProcedureReturn r1
  
EndProcedure

; ----

Procedure.s DumpObjectMethods(*Object, Super = #False, HidePrivate = #True, ShowEncoding = #False, FirstArgument = 2)
  Protected r1.s, i, c, n, methodCount, Method.s
  Protected *Class, *Method, *Methods.udtArray
  Protected *String = AllocateMemory(1024)
  
  *Class = object_getclass_(*Object)
  If Super
    *Class = class_getsuperclass_(*Class) 
    r1 = "SuperClass "
  Else
    r1 = "Class "
  EndIf
  
  *Methods = class_copyMethodList(*Class, @methodCount)
  
  r1 + PeekS(class_getName(*Class), -1, #PB_UTF8) + " - Count of Methods " + methodCount + #LF$
  For i = 0 To methodCount - 1
    *Method = *Methods\i[i];
    Method = PeekS(sel_getName(method_getName(*Method)), -1, #PB_UTF8)
    If HidePrivate And Left(Method, 1) = "_"
      Continue
    EndIf
    r1 + "Method " + Method + #LF$
    If ShowEncoding
      r1 + " * Encoding " + PeekS(method_getTypeEncoding(*Method), -1, #PB_UTF8) + #LF$
    EndIf
    method_getReturnType(*Method, *String, 1024)
    r1 + " -- ReturnType = " + GetArgumentType(*String) + #LF$
    c = method_getNumberOfArguments(*Method)
    For n = FirstArgument To c - 1
      method_getArgumentType(*Method, n, *String, 1024)
      r1 + " -- Argument " + Str(n - FirstArgument + 1) + " = " + GetArgumentType(*String) + #LF$
    Next
  Next
  r1 + "End Class" + #LF$
  
  free_(*methods)
  FreeMemory(*String)
  
  ProcedureReturn r1
EndProcedure

; ****

;-Example
CompilerIf #PB_Compiler_IsMainFile
  NSApp = CocoaMessage(0, 0, "NSApplication sharedApplication")
  Debug DumpObjectMethods(NSApp, 1)
CompilerEndIf

Re: Plain Cocoa coding

Posted: Sun Jun 13, 2021 8:03 pm
by mestnyi
mk-soft thank you
that's not what I asked.
I meant the description, that is, the order in which the functions are called.
https://developer.apple.com/documentati ... houldclose
Can anyone explain to me how you got this?

Code: Select all

ProcedureC winShouldClose(obj.i, sel.i, win.i)
EndProcedure
class_addMethod_(myWindowDelegateClass, sel_registerName_("windowShouldClose:"), @winShouldClose(), "c@:@")

Re: Plain Cocoa coding

Posted: Sun Jun 13, 2021 8:19 pm
by mk-soft
The only thing that helps is to look at the examples in Objective-C.

macOS runs a little differently than Windows. There are events here too. But you have to add the methods that belong to the event (notifications) to your own DelegateClass in order to catch them.