Page 1 of 1

Cocoa: How to feed an OutlineView Programatically

Posted: Mon Aug 21, 2017 7:13 pm
by fsw
Tried to create an OutlineView with ObjC which worked, but filling the NSColumn with rows of data is a disaster.
All examples I found use either a NIB file or a Storyboard and with this, some stuff seems to be achieved automatically.
(like setting a datasource or a delegate is allowed but doing the same programmatically raises an error)

The Cocoa API gets more convoluted with every iteration and programmers not using Xcode for the UI are screwed...
Any hint are appreciated.
Thanks.

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Mon Aug 21, 2017 7:22 pm
by wilbert
As far as I know you have to create your own class which implements the NSOutlineViewDataSource protocol and set an instance of that class as the dataSource .
Maybe this post will give you some idea. It's a table view so not an outline view but in both cases you need to create your own data source. Only the methods you need to implement are different.
http://www.purebasic.fr/english/viewtop ... 75#p484975

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Mon Aug 21, 2017 8:03 pm
by fsw
Wow Wilbert, this was fast.
Not sure why I didn't find this example when I did a search...

Will walk through the code line by line.
Thank you.

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Tue Aug 22, 2017 9:20 am
by wilbert
I tried a bit for myself, got it at least to do something.

Code: Select all

Global RootNode = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Root node")
CocoaMessage(0, RootNode, "retain")

ChildNode1 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1")
ChildNode2 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 2")
ChildNode1_1 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.1")
ChildNode1_2 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.2")
ChildNode1_3 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.3")
ChildNodes = CocoaMessage(0, RootNode, "mutableChildNodes")
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode2)
ChildNodes = CocoaMessage(0, ChildNode1, "mutableChildNodes")
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_1)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_2)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_3)



; dataSource => child of item
ProcedureC.i child_ofItem(obj, sel, outlineView, index, item)
  If item = #nil : item = RootNode : EndIf
  ProcedureReturn CocoaMessage(0, CocoaMessage(0, item, "mutableChildNodes"), "objectAtIndex:", index)
EndProcedure

; dataSource => is item expandable ?
ProcedureC.i isItemExpandable(obj, sel, outlineView, item)
  If CocoaMessage(0, item, "isLeaf")
    ProcedureReturn #NO
  Else
    ProcedureReturn #YES
  EndIf
EndProcedure

; dataSource => number of children of item
ProcedureC numberOfChildrenOfItem(obj, sel, outlineView, item)
  If item = #nil : item = RootNode : EndIf
  ProcedureReturn CocoaMessage(0, CocoaMessage(0, item, "mutableChildNodes"), "count")
EndProcedure

; dataSource => object value
ProcedureC objectValueForTableColumn_byItem(obj, sel, outlineView, tableColumn, item)
  ProcedureReturn CocoaMessage(0, item, "representedObject")
EndProcedure



; create class(es)
class = objc_allocateClassPair_(objc_getClass_("NSObject"), "MyDataSourceClass", 0)
class_addMethod_(class, sel_registerName_("outlineView:child:ofItem:"), @child_ofItem(), "@@:i@")
class_addMethod_(class, sel_registerName_("outlineView:isItemExpandable:"), @isItemExpandable(), "c@:@")
class_addMethod_(class, sel_registerName_("outlineView:numberOfChildrenOfItem:"), @numberOfChildrenOfItem(), "i@:@")
class_addMethod_(class, sel_registerName_("outlineView:objectValueForTableColumn:byItem:"), @objectValueForTableColumn_byItem(), "@@:@@")
objc_registerClassPair_(class)


; test
OpenWindow(0, 0, 0, 400, 340, "NSOutlineView test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TreeGadget(0, 10, 10, 380, 280)

DataSource = CocoaMessage(0, 0, "MyDataSourceClass new")
CocoaMessage(0, GadgetID(0), "setDataSource:", DataSource)

Repeat 
  Event = WaitWindowEvent()
Until Event  = #PB_Event_CloseWindow
A better approach might be using a NSTreeController but I don't understand how to bind it to a NSOutlineView programmatically.

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Wed Aug 23, 2017 11:14 pm
by fsw
This is awesome stuff Wilbert.
When I see your code... it all looks so easy.

Thank you
8)

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Thu Jun 19, 2025 8:34 am
by Wolfram
Unfortunately I get an error "[ERROR] Illegal instruction. (executing binary Data?)" on line 43.

Code: Select all

ProcedureReturn CocoaMessage(0, item, "representedObject") ;<-- hier it crashes
Can someone explain me why?

Re: Cocoa: How to feed an OutlineView Programatically

Posted: Thu Jun 19, 2025 11:45 am
by Wolfram
I guess that's the right...

Code: Select all

Global RootNode = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Root node")
CocoaMessage(0, RootNode, "retain")

ChildNode1 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1")
ChildNode2 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 2")
ChildNode1_1 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.1")
ChildNode1_2 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.2")
ChildNode1_3 = CocoaMessage(0, 0, "NSTreeNode treeNodeWithRepresentedObject:$", @"Child node 1.3")
ChildNodes = CocoaMessage(0, RootNode, "mutableChildNodes")
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode2)
ChildNodes = CocoaMessage(0, ChildNode1, "mutableChildNodes")
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_1)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_2)
CocoaMessage(0, ChildNodes, "addObject:", ChildNode1_3)



; dataSource => child of item
ProcedureC.i child_ofItem(obj, sel, outlineView, index, item)
  If item = #nil : item = RootNode : EndIf
  ProcedureReturn CocoaMessage(0, CocoaMessage(0, item, "mutableChildNodes"), "objectAtIndex:", index)
EndProcedure

; dataSource => is item expandable ?
ProcedureC.i isItemExpandable(obj, sel, outlineView, item)
  
  If CocoaMessage(0, item, "isLeaf")
    ProcedureReturn #NO
  Else
    ProcedureReturn #YES
  EndIf
EndProcedure

; dataSource => number of children of item
ProcedureC numberOfChildrenOfItem(obj, sel, outlineView, item)
  If item = #nil : item = RootNode : EndIf
  ProcedureReturn CocoaMessage(0, CocoaMessage(0, item, "mutableChildNodes"), "count")
EndProcedure

; dataSource => object value
ProcedureC objectValueForTableColumn_byItem(obj, sel, outlineView, tableColumn, item)

  ProcedureReturn CocoaMessage(0, item, "representedObject")
EndProcedure



; create class(es)
class = objc_allocateClassPair_(objc_getClass_("NSObject"), "MyDataSourceClass", 0)
class_addMethod_(class, sel_registerName_("outlineView:child:ofItem:"), @child_ofItem(), "@@:i@")
class_addMethod_(class, sel_registerName_("outlineView:isItemExpandable:"), @isItemExpandable(), "c@:@")
class_addMethod_(class, sel_registerName_("outlineView:numberOfChildrenOfItem:"), @numberOfChildrenOfItem(), "i@:@")
class_addMethod_(class, sel_registerName_("outlineView:objectValueForTableColumn:byItem:"), @objectValueForTableColumn_byItem(), "@@:@@")
objc_registerClassPair_(class)


; test
OpenWindow(0, 0, 0, 400, 340, "NSOutlineView test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TreeGadget(0, 10, 10, 380, 280)

DataSource = CocoaMessage(0, 0, "MyDataSourceClass new")
CocoaMessage(0, GadgetID(0), "setDataSource:", DataSource)
CocoaMessage(0, GadgetID(0), "setDelegate:", DataSource)

Repeat 
  Event = WaitWindowEvent()
Until Event  = #PB_Event_CloseWindow