Plain Cocoa coding

Mac OSX specific forum
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Plain Cocoa coding

Post 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;
}
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Plain Cocoa coding

Post 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 .
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Plain Cocoa coding

Post 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! :)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Plain Cocoa coding

Post by Wolfram »

Hi,

what is the advantage by doing it in this way instead of PB code?
macOS Catalina 10.15.7
User avatar
Danilo
Addict
Addict
Posts: 3037
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: Plain Cocoa coding

Post by Danilo »

Wolfram wrote:what is the advantage by doing it in this way instead of PB code?
It is more fun. ;)
Justin
Addict
Addict
Posts: 829
Joined: Sat Apr 26, 2003 2:49 pm

Re: Plain Cocoa coding

Post 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)
Justin
Addict
Addict
Posts: 829
Joined: Sat Apr 26, 2003 2:49 pm

Re: Plain Cocoa coding

Post 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)
Last edited by Justin on Sun Jun 16, 2019 8:00 pm, edited 1 time in total.
Justin
Addict
Addict
Posts: 829
Joined: Sat Apr 26, 2003 2:49 pm

Re: Plain Cocoa coding

Post 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?
Justin
Addict
Addict
Posts: 829
Joined: Sat Apr 26, 2003 2:49 pm

Re: Plain Cocoa coding

Post 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.
Rinzwind
Enthusiast
Enthusiast
Posts: 636
Joined: Wed Mar 11, 2009 4:06 pm
Location: NL

Re: Plain Cocoa coding

Post by Rinzwind »

For curiosity: applicationWillTerminate is never called?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Plain Cocoa coding

Post by wilbert »

Rinzwind wrote:For curiosity: applicationWillTerminate is never called?
It's because the source code contains "End" within the appShouldTerminate procedure.
Windows (x64)
Raspberry Pi OS (Arm64)
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Plain Cocoa coding

Post 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)
User avatar
mk-soft
Always Here
Always Here
Posts: 5313
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Plain Cocoa coding

Post 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
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Plain Cocoa coding

Post 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@:@")
User avatar
mk-soft
Always Here
Always Here
Posts: 5313
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Plain Cocoa coding

Post 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.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply