ListIconGadget with > 1 million items

Mac OSX specific forum
hesitate
User
User
Posts: 11
Joined: Tue Mar 01, 2016 2:00 pm

ListIconGadget with > 1 million items

Post by hesitate »

I am porting an app from Windows to Mac OS. The program loads > 1 million items in a ListIconGadget sometimes. There are filters and the program also supports sorting. It works damn well (fast) on Windows with a virtual ListIconGadget.

Now, I'm porting it to Mac OS. I've started reading the Obj-C docs/AppKit/NSTableView and have already ported some auxiliary functions with CocoaMessage.

However, I can't figure out what's the optimal way to use the NSTableView++ for the task. (Load 1 mil of items, filter should immediately update the view..)

Any pointers on optimal(speed) implementation? Or a snippet demonstrating it with bogus data?

All cells are plain text.

Thank you.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ListIconGadget with > 1 million items

Post by wilbert »

The most optimal approach is to create your own class that conforms to the NSTableViewDataSource protocol and use an instance of that class as data source.
Windows (x64)
Raspberry Pi OS (Arm64)
hesitate
User
User
Posts: 11
Joined: Tue Mar 01, 2016 2:00 pm

Re: ListIconGadget with > 1 million items

Post by hesitate »

Thanks for the pointer.

I subclassed NSObject, added the 2 needed methods, instantiated it, set the datasource of the NSTableView and it started crashing (invalid memory access on EndProcedure or ProcedureReturn). The methods set bogus data, so they're not buggy.

I can call the 2 methods with CocoaMessage but when the NSTableView calls them, they crash.

I fixed it (don't know if it's a fix) by setting the delegate of NSTableView to nil, before setting its data source. The preset delegate (maybe set by Purebasic runtime?) doesn't work with my class. Now, it seems to be working fast.

Anyone had the same issue? I hope I don't introduce other problems by nilling the NSTableView delegate.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ListIconGadget with > 1 million items

Post by wilbert »

hesitate wrote:Anyone had the same issue? I hope I don't introduce other problems by nilling the NSTableView delegate.
I think maybe no one tried it before.

If you want to be sure, you can create the table view yourself with NSTableView alloc / initWithFrame instead of using the PB gadget.
When you do that, you can add it to the contentView of the window or create a PB ContainerGadget and use it with that (setContentView).
The main problem with PB gadgets in this case is that you don't know what methods PB itself subclasses and if it will stay the same for future versions.
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: ListIconGadget with > 1 million items

Post by Danilo »

hesitate wrote:(invalid memory access on EndProcedure or ProcedureReturn).
Did you use Procedure or ProcedureC?
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ListIconGadget with > 1 million items

Post by wilbert »

Danilo wrote:
hesitate wrote:(invalid memory access on EndProcedure or ProcedureReturn).
Did you use Procedure or ProcedureC?
I'm having the same problems when I try.
Also, clicking an item seems to work but walking the list not.
It seems you have to come up with your own delegate as well to make it work

Here's my attempt, a list of two million random numbers.
Clicking the second column will sort the data.

Code tested with PB 5.42

Code: Select all

; create array with two million 32 bit values
Global Dim MyArray.l(1999999)

; fill array with random numbers
RandomData(@MyArray(), 2000000 << 2)



; dataSource number of rows
ProcedureC.i numberOfRows(obj, sel, tableView)
  ProcedureReturn ArraySize(MyArray()) + 1
EndProcedure

; dataSource get value
ProcedureC.i getTableValue(obj, sel, tableView, tableColumn, rowIndex)
  Protected.i NSNumber, Columns
  Protected.i Column0, Column1
  
  Columns = CocoaMessage(0, tableView, "tableColumns")
  Column0 = CocoaMessage(0, Columns, "objectAtIndex:", 0)
  Column1 = CocoaMessage(0, Columns, "objectAtIndex:", 1)
  
  Select tableColumn
    Case Column0
      NSNumber = CocoaMessage(0, 0, "NSNumber numberWithInteger:", rowIndex)
    Case Column1
      NSNumber = CocoaMessage(0, 0, "NSNumber numberWithLong:", MyArray(rowIndex))
  EndSelect
  
  ProcedureReturn CocoaMessage(0, NSNumber, "stringValue")
EndProcedure

; dataSource set value (not implemented because we want read-only)
ProcedureC setTableValue(obj, sel, tableView, object, tableColumn, rowIndex)
EndProcedure

; delegate selection change
ProcedureC selectionChange(obj, sel, notification)
  PostEvent(#PB_Event_Gadget, 0, 0, #PB_EventType_Change)    
EndProcedure

; delegate column click
ProcedureC columnClick(obj, sel, tableView, tableColumn)
  Protected.i Columns
  Protected.i Column0, Column1
  
  Columns = CocoaMessage(0, tableView, "tableColumns")
  Column0 = CocoaMessage(0, Columns, "objectAtIndex:", 0)
  Column1 = CocoaMessage(0, Columns, "objectAtIndex:", 1)
  
  Select tableColumn
    Case Column1
      SortArray(MyArray(), #PB_Sort_Ascending)
      CocoaMessage(0, tableView, "reloadData")
  EndSelect
  
EndProcedure



; create classes
class = objc_allocateClassPair_(objc_getClass_("NSObject"), "MyTableDataSourceClass", 0)
class_addMethod_(class, sel_registerName_("numberOfRowsInTableView:"), @numberOfRows(), "v@:@")
class_addMethod_(class, sel_registerName_("tableView:objectValueForTableColumn:row:"), @getTableValue(), "@@:@@i")
class_addMethod_(class, sel_registerName_("tableView:setObjectValue:forTableColumn:row:"), @setTableValue(), "v@:@@@i")
objc_registerClassPair_(class)

class = objc_allocateClassPair_(objc_getClass_("NSObject"), "MyTableDelegateClass", 0)
class_addMethod_(class, sel_registerName_("tableViewSelectionDidChange:"), @selectionChange(), "v@:@")
class_addMethod_(class, sel_registerName_("tableView:didClickTableColumn:"), @columnClick(), "v@:@@")

objc_registerClassPair_(class)



; ListIconGadget callback
Procedure ListIconGadgetCB()
  If GetGadgetState(0) >= 0
    SetGadgetText(1, Str(MyArray(GetGadgetState(0))))
  EndIf  
EndProcedure



; test
OpenWindow(0, 0, 0, 400, 340, "ListIconGadget demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ListIconGadget(0, 10, 10, 380, 280, "Column 1", 150)
AddGadgetColumn(0, 1, "Column 2", 150)
StringGadget(1, 10, 300, 380, 30, "", #PB_String_ReadOnly)

DataSource = CocoaMessage(0, 0, "MyTableDataSourceClass new")
Delegate = CocoaMessage(0, 0, "MyTableDelegateClass new")
CocoaMessage(0, GadgetID(0), "setDataSource:", DataSource)
CocoaMessage(0, GadgetID(0), "setDelegate:", Delegate)

BindGadgetEvent(0, @ListIconGadgetCB(), #PB_EventType_Change)

Repeat 
  Event = WaitWindowEvent()
Until Event  = #PB_Event_CloseWindow
Windows (x64)
Raspberry Pi OS (Arm64)
hesitate
User
User
Posts: 11
Joined: Tue Mar 01, 2016 2:00 pm

Re: ListIconGadget with > 1 million items

Post by hesitate »

Danilo,
I use ProcedureC.

wilbert,
it works both with a nilled delegate and NSApplication sharedApplication delegate that I needed for columnclicks/sorting.
Post Reply