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.
ListIconGadget with > 1 million items
Re: ListIconGadget with > 1 million items
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)
Raspberry Pi OS (Arm64)
Re: ListIconGadget with > 1 million items
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.
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.
Re: ListIconGadget with > 1 million items
I think maybe no one tried it before.hesitate wrote:Anyone had the same issue? I hope I don't introduce other problems by nilling the NSTableView delegate.
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)
Raspberry Pi OS (Arm64)
Re: ListIconGadget with > 1 million items
Did you use Procedure or ProcedureC?hesitate wrote:(invalid memory access on EndProcedure or ProcedureReturn).
Re: ListIconGadget with > 1 million items
I'm having the same problems when I try.Danilo wrote:Did you use Procedure or ProcedureC?hesitate wrote:(invalid memory access on EndProcedure or ProcedureReturn).
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)
Raspberry Pi OS (Arm64)
Re: ListIconGadget with > 1 million items
Danilo,
I use ProcedureC.
wilbert,
it works both with a nilled delegate and NSApplication sharedApplication delegate that I needed for columnclicks/sorting.
I use ProcedureC.
wilbert,
it works both with a nilled delegate and NSApplication sharedApplication delegate that I needed for columnclicks/sorting.