How to add a menu to a listView

Mac OSX specific forum
User avatar
Posts: 1839
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to add a menu to a listView

Post by Shardik »

This new example code needs at least MacOS 10.7 (Lion). I tested it successfully on MacOS 10.8.6 (Mountain Lion) and 10.12.1 (Sierra) with PB 5.43 x86 and x64 in both ASCII and Unicode mode.

Since PureBasic is still using NSCells (deprecated cell-based approach, NSCells are not based on NSView, alot less flexible and therefore much harder to modify) to display the cell contents and according to Apple the cell-based and view-based approach may not be mixed, I had to use the delegate method tableView:viewForTableColumn:row: (introduced in Lion) and return a newly allocated view (NSComboBox for a column with a ComboBox or NSTextField for a normal column) for each cell that has to be displayed:

Code: Select all


; ----- Only necessary when compiling with PB x64 on MacOS 10.12 "Sierra"
; Import "-stdlib=libc++ -mmacosx-version-min=10.7" : EndImport

If OSVersion() < #PB_OS_MacOSX_10_7
  MessageRequester("Compilation terminated",
    "Sorry, but you need at least MacOS 10.7 (Lion) to compile this code!")

#ListIcon = 0

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define RowHeight.CGFloat = 25
Define Selector.I = sel_registerName_("tableView:viewForTableColumn:row:")

ProcedureC GetViewForCellCallback(Object.I, Selector.I, TableView.I,
  ColumnObject.I, Row.I)
  Protected CellContent.S
  Protected Column.I
  Protected ColumnID.S = PeekS(CocoaMessage(0, CocoaMessage(0,
    ColumnObject, "identifier"), "UTF8String"), -1, #PB_UTF8)
  Protected ComboBox.I
  Protected TextFrame.NSRect
  Protected View.I

  Column = Val(ColumnID)

  If Column = 1
    ; ----- If column = 1, create ComboBox and return it
    ComboBox = CocoaMessage(0, 0, "NSComboBox new")
    CocoaMessage(0, ComboBox, "addItemWithObjectValue:$",
      @"12 Parliament Way, Battle Street, By the Bay")
    CocoaMessage(0, ComboBox, "addItemWithObjectValue:$",
      @"130 PureBasic Road, BigTown, CodeCity")
    CocoaMessage(0, ComboBox, "addItemWithObjectValue:$",
      @"321 Logo Drive, Mouse House, Downtown")
    View = ComboBox
    ; ----- If Column <> 1, read current cell content, create new view with
    ;       NSTextField, write current content into that view and return it
    CellContent = GetGadgetItemText(#ListIcon, Row, Column)
    TextFrame\size\width = GetGadgetItemAttribute(#ListIcon,
      #PB_ListIcon_ColumnWidth, Column)
    TextFrame\size\height = 18
    View = CocoaMessage(0, CocoaMessage(0, 0, "NSTextField new"),
      "initWithFrame:@", TextFrame)
    CocoaMessage(0, View, "setStringValue:$", @CellContent)

  ProcedureReturn View

OpenWindow(0, 200, 100, 430, 126, "ListIconGadget with ComboBox")
ListIconGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20,
  "Name", 110)
AddGadgetColumn(0, 1, "Address", GadgetWidth(0) - GetGadgetItemAttribute(0,
  0, #PB_ListIcon_ColumnWidth) - 8)
AddGadgetItem(0, -1, "Harry Rannit")
AddGadgetItem(0, -1, "Ginger Brokeit")
AddGadgetItem(0, -1, "Didi Foundit")

; ----- Increase row height to be able to display larger ComboBox
CocoaMessage(0, GadgetID(0), "setRowHeight:@", @RowHeight)

; ----- Initialize callback that returns a new NSView (view-based) instead
;       of the old NSCell (cell-based) for each cell drawn
class_addMethod_(DelegateClass, Selector, @GetViewForCellCallback(), "v@:@@@")
CocoaMessage(0, GadgetID(0), "setDelegate:", AppDelegate)

Until WaitWindowEvent() = #PB_Event_CloseWindow
User avatar
Posts: 1489
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Re: How to add a menu to a listView

Post by fsw »

Shardik wrote:Since PureBasic is still using NSCells (deprecated cell-based approach...
This is my experience as well: some OS X/macOS stuff is really outdated (examples: webgadget doesn't use WKWebview but the old webview, movie library still uses QuickTime instead of AVFoundation etc.)
Lets hope that Fred extends the next PureBasic version to be more up-to-date on macOS.

It's a bummer because there is no other easy to use programming language (that I know of) on macOS besides Swift 3.

On the flip-side I can see why it takes so long for the PureBasic team to update the PB-Libs:
Apple's macOS replacement libraries are not a 1 to 1 replacement. Lot's of stuff doesn't work the same.
In my experience it's a lot of trial and error because some lib-commands have been killed in the new library. And Apple's documentation is written in a way that it's assumed the reader is a well versed professional OS X programmer...
On top of that: XCode really sux big time, and the strange programming approach of half coding and the other half is disguised in some ui-event sub-menus where a "graphical connection" needs to be made in the interface builder...

I am to provide the public with beneficial shocks.
Alfred Hitshock
User avatar
Posts: 1839
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to add a menu to a listView

Post by Shardik »

Although my code example posted above is working quite fine, it is a hack which messes around with PureBasic internals by returning view-based objects instead of cell-based objects. This prevents PureBasic events and PureBasic functions like GetGadgetItemText() to work correctly for the ListIconGadget! Therefore I programmed a more compatible cell-based approach using NSComboBoxCells. But now the ComboBoxes are not editable anymore. If you need editable ComboBoxes you should take a look into this more advanced code example.

This code example was tested successfully with PB 5.44 x86 and x64 in ASCII and Unicode mode on these MacOS versions:
- 10.6.8 (Snow Leopard) (it's only compilable with PB 5.44 x86; to compile it with x64 it's necessary to use PB 5.43 x64!)
- 10.8.6 (Mountain Lion)
- 10.9.5 (Mavericks)
- 10.12.1 (Sierra)

Code: Select all


#ListIcon = 0
#ComboBoxColumn = 1

Define AppDelegate.I = CocoaMessage(0,
  CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
Define ColumnObject.I
Define i.I
Define ComboBoxCell.I
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define NotificationCenter.I = CocoaMessage(0, 0,
  "NSNotificationCenter defaultCenter")
Define RowHeight.CGFloat = 21
Define Selector.I = sel_registerName_("itemChanged:")
Define SubclassedComboBoxCell.I

ProcedureC ComboBoxSelectionChangedCallback(Object.I, Selector.I,
  Protected Cell.I
  Protected ItemIndex.I
  Protected ItemText.S
  Protected Row.I

  Cell = CocoaMessage(0, GadgetID(#ListIcon), "selectedCell")

  If Cell
    Row = CocoaMessage(0, GadgetID(#ListIcon), "selectedRow")
    ItemIndex = CocoaMessage(0, Cell, "indexOfSelectedItem")

    If ItemIndex >= 0
      ItemText = PeekS(CocoaMessage(0, CocoaMessage(0,
        Cell, "itemObjectValueAtIndex:", ItemIndex),
        "UTF8String"), -1, #PB_UTF8)

      If ItemText <> GetGadgetItemText(#ListIcon, Row, #ComboBoxColumn)
        SetGadgetItemText(#ListIcon, Row, ItemText, #ComboBoxColumn)

OpenWindow(0, 200, 100, 250, 113, "ListIcon with ComboBoxes")
ListIconGadget(#ListIcon, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20,
  "Friend", 70, #PB_ListIcon_GridLines)
AddGadgetColumn(#ListIcon, #ComboBoxColumn, "Country",
  GadgetWidth(#ListIcon) - GetGadgetItemAttribute(#ListIcon, 0,
  #PB_ListIcon_ColumnWidth) - 8)
AddGadgetItem(#ListIcon, -1, "Alice")
AddGadgetItem(#ListIcon, -1, "Bob")
AddGadgetItem(#ListIcon, -1, "Eve")

; ----- Increase row height to be able to display the larger ComboBox
CocoaMessage(0, GadgetID(#ListIcon), "setRowHeight:@", @RowHeight)

; ----- Disable highlighting of selected row
CocoaMessage(0, GadgetID(#ListIcon), "setSelectionHighlightStyle:", -1)

; ----- Center text in 1st column vertically
;       Caution: _setVerticallyCentered: is an internal flag; it could simply
;       disappear without warning in a future release and its use may lead
;       to a rejection in Apple's AppStore!
ColumnObject = CocoaMessage(0, CocoaMessage(0,
  GadgetID(#ListIcon), "tableColumns"), "objectAtIndex:", 0)
CocoaMessage(0, CocoaMessage(0, ColumnObject, "dataCell"),
  "_setVerticallyCentered:", #YES)

; ----- Create a NSComboBoxCell (used internally as an editable ComboBox by
;       PureBasic) and use it in all cells of column #ComboBoxColumn
ColumnObject = CocoaMessage(0, CocoaMessage(0,
   GadgetID(#ListIcon), "tableColumns"), "objectAtIndex:", #ComboBoxColumn)
ComboBoxCell = CocoaMessage(0, 0, "NSComboBoxCell new")
CocoaMessage(0, ColumnObject, "setDataCell:", ComboBoxCell)

; ----- Remove border around button (special style for NSComboBoxCell in
;       NSTableView)
CocoaMessage(0, ComboBoxCell, "setButtonBordered:", #NO)

; ----- Fill ComboBox
CocoaMessage(0, ComboBoxCell, "addItemWithObjectValue:$", @"France")
CocoaMessage(0, ComboBoxCell, "addItemWithObjectValue:$", @"Germany")
CocoaMessage(0, ComboBoxCell, "addItemWithObjectValue:$", @"USA")

; ----- Subclass ComboBoxCell
SubclassedComboBoxCell = objc_allocateClassPair_(CocoaMessage(0,
  ComboBoxCell, "class"), "SubclassedComboBoxCell", 0)
object_setClass_(ComboBoxCell, SubclassedComboBoxCell)

; ----- Set callback to detect selection change in ComboBox
class_addMethod_(DelegateClass, Selector, @ComboBoxSelectionChangedCallback(),
CocoaMessage(0, NotificationCenter,
  "addObserver:", AppDelegate,
  "selector:", Selector,
  "name:$", @"NSTableViewSelectionDidChangeNotification",
  "object:", CocoaMessage(0, GadgetID(#ListIcon), "menu"))


Until WaitWindowEvent() = #PB_Event_CloseWindow

; ----- Display countries currently selected in ComboBoxes
For i = 0 To 2
  Debug Str(i + 1) + ". " + GetGadgetItemText(#ListIcon, i, 0) + " - " +
    GetGadgetItemText(#ListIcon, i, #ComboBoxColumn)
Next i

CocoaMessage(0, NotificationCenter, "removeObserver:", AppDelegate)
Post Reply