PureBasic Forum
https://www.purebasic.fr/english/

Plain Cocoa coding
https://www.purebasic.fr/english/viewtopic.php?f=19&t=59589
Page 1 of 1

Author:  Danilo [ Sat Jun 07, 2014 9:17 pm ]
Post subject:  Plain Cocoa coding

How to add a button or listbox to a cocoa window using plain API?
Code:
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:
#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;
}

Author:  wilbert [ Sun Jun 08, 2014 7:34 am ]
Post subject:  Re: Plain Cocoa coding

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 .

Author:  Danilo [ Sun Jun 08, 2014 5:26 pm ]
Post subject:  Re: Plain Cocoa coding

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:
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! :)

Author:  Wolfram [ Sun Jun 08, 2014 6:06 pm ]
Post subject:  Re: Plain Cocoa coding

Hi,

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

Author:  Danilo [ Sun Jun 08, 2014 9:09 pm ]
Post subject:  Re: Plain Cocoa coding

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

It is more fun. ;)

Author:  Justin [ Sat Jun 15, 2019 11:43 pm ]
Post subject:  Re: Plain Cocoa coding

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:
- (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:
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)

Author:  Justin [ Sun Jun 16, 2019 3:44 pm ]
Post subject:  Re: Plain Cocoa coding

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:
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)

Author:  Justin [ Sun Jun 16, 2019 7:58 pm ]
Post subject:  Re: Plain Cocoa coding

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?

Author:  Justin [ Wed Jun 19, 2019 8:57 pm ]
Post subject:  Re: Plain Cocoa coding

Well, finally I did it. The solution was to set the quit menu item "action" with a pb callback that terminates the app.

Author:  Rinzwind [ Tue Sep 10, 2019 6:03 am ]
Post subject:  Re: Plain Cocoa coding

For curiosity: applicationWillTerminate is never called?

Author:  wilbert [ Tue Sep 10, 2019 6:35 am ]
Post subject:  Re: Plain Cocoa coding

Rinzwind wrote:
For curiosity: applicationWillTerminate is never called?

It's because the source code contains "End" within the appShouldTerminate procedure.

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/