How to add a second Callback ???

Mac OSX specific forum
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

How to add a second Callback ???

Post by Wolfram »

How can I add a second Callback to a ListIconGadget() ?
I need one to be able to edit the items and one to add a ComboBoxGadget() in one row of the ListIconGadget().
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to add a second Callback ???

Post by wilbert »

I'm not sure what you mean by a second callback.
Can't you use BindEvent ?
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: How to add a second Callback ???

Post by Wolfram »

wilbert wrote:I'm not sure what you mean by a second callback.
Can't you use BindEvent ?
In this example only one callback work. If I remove the first one the second will work.

Code: Select all

ImportC ""
  sel_registerName(MethodName.S)
  class_addMethod(Class.I, Selector.I, Implementation.I, Types.S)
EndImport

Procedure.S ConvertToUTF8(String.S)
  Protected UTF8String.S = Space(StringByteLength(String))
  PokeS(@UTF8String, String, -1, #PB_UTF8)
  ProcedureReturn UTF8String
EndProcedure

ProcedureC EditingFinishedCallback(Object.I, Selector.I, Notification.I)
  Protected EditedCell.I
  Protected EditedColumn.I = CocoaMessage(0, GadgetID(0), "editedColumn")
  Protected EditedRow.I = CocoaMessage(0, GadgetID(0), "editedRow")
  Protected EditedText.S

  EditedCell = CocoaMessage(0, Notification, "object")
  EditedText = PeekS(CocoaMessage(0, CocoaMessage(0, EditedCell, "stringValue"),
    "UTF8String"), -1, #PB_UTF8)
  SetGadgetItemText(0, EditedRow, EditedText, EditedColumn)
  Debug "Edit finished"
EndProcedure

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:$",
      @"AAA")
    CocoaMessage(0, ComboBox, "addItemWithObjectValue:$",
      @"BBB")
    CocoaMessage(0, ComboBox, "addItemWithObjectValue:$",
      @"CCC")
    View = ComboBox
  Else
    ; ----- If Column <> 1, read current cell content, create new view with
    ;       NSTextField, write current content into that view and return it
    CellContent = GetGadgetItemText(0, Row, Column)
    TextFrame\size\width = GetGadgetItemAttribute(0,
      #PB_ListIcon_ColumnWidth, Column)
    TextFrame\size\height = 18
    View = CocoaMessage(0, CocoaMessage(0, 0, "NSTextField new"),
      "initWithFrame:@", TextFrame)
    CocoaMessage(0, View, "setStringValue:$", @CellContent)
    
  EndIf

  ProcedureReturn View
EndProcedure


Define CursorLocation.NSPoint
Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define NotificationCenter.I = CocoaMessage(0, 0,
  "NSNotificationCenter defaultCenter")
Define SelectedColumn.I
Define Selector.I = sel_registerName(ConvertToUTF8("textDidEndEditing:"))


Define RowHeight.CGFloat = 25
Define Selector2.I = sel_registerName_("tableView:viewForTableColumn:row:")


OpenWindow(0, 200, 100, 430, 195, "Editable ListIconGadget demo II")
ListIconGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20, "Name", 110)
AddGadgetColumn(0, 1, "Address1", 292)
AddGadgetColumn(0, 2, "Address2", GadgetWidth(0) - GetGadgetItemAttribute(0,
  0, #PB_ListIcon_ColumnWidth) - 8)
AddGadgetItem(0, -1, "Harry Rannit" + #LF$ + "")
AddGadgetItem(0, -1, "Ginger Brokeit" + #LF$ + "")
AddGadgetItem(0, -1, "Didi Foundit" + #LF$ + "")
CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", -1)


; ----- 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, Selector2, @GetViewForCellCallback(), "v@:@@@")
CocoaMessage(0, GadgetID(0), "setDelegate:", AppDelegate)

class_addMethod(DelegateClass, Selector, @EditingFinishedCallback(), "v@:@")
CocoaMessage(0, NotificationCenter,
             "addObserver:", AppDelegate,
             "selector:", Selector,
             "name:$", @"NSControlTextDidEndEditingNotification",
             "object:", GadgetID(0))



Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
        CursorLocation\x = WindowMouseX(0)
        CursorLocation\y = WindowHeight(0) - WindowMouseY(0)
        CocoaMessage(@CursorLocation, GadgetID(0),
          "convertPoint:@", @CursorLocation, "fromView:", 0)
        SelectedColumn = CocoaMessage(0, GadgetID(0),
          "columnAtPoint:@", @CursorLocation)
        CocoaMessage(0, GadgetID(0),
          "editColumn:", SelectedColumn,
          "row:", GetGadgetState(0),
          "withEvent:", 0,
          "select:", #YES)
      EndIf
  EndSelect
ForEver
macOS Catalina 10.15.7
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to add a second Callback ???

Post by Shardik »

Sorry, but your problem can't be solved by a second callback. You have taken my example for adding ComboBoxes to cells in a column of a ListIconGadget and added my example to edit single cells. Unfortunately my first example is a hack. Although the example is working fine, it's messing around with PureBasic internals by replacing PureBasic's NSCells (cell-based) in the ListIconGadget with view-based NSComboBoxes and NSTextFields. As a result the PureBasic event loop doesn't work correctly anymore for the ListIconGadget, so that the method editColumn:row:withEvent:select: in the event loop is never called!

The only solution seems to be to replace PureBasic's NSCell objects (cell-based) by NSComboBoxCell objects (also cell-based). This seems to work so far, but until now I wasn't able to detect the selection of a ComboBox element and display the chosen element in the ComboBox. After selecting an element from the ComboBox, the selected element is always briefly displayed in the ComboBox but cleared after a very short period. I currently don't know whether this technique is possibly interfering with PureBasic internals... :oops:
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to add a second Callback ???

Post by Shardik »

I have finally found a solution which utilizes NSComboBoxCells to display ComboBoxes in a column of a ListIconGadget. It's possible to edit the content of normal cells and ComboBox cells (and thereby add new ComboBox items). You may disable the editing of ComboBox cells by setting #ComboBoxEdtitableState to #False.

I have tested this example successfully with PB 5.44 x86 and x64 in ASCII and Unicode mode with 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

EnableExplicit

#ListIcon = 0
#ComboBoxColumn = 1
#ComboBoxEditableState = #True ; #True enables editing of ComboBox,
                               ; #False disables it

Define AppDelegate.I = CocoaMessage(0,
  CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
Define ColumnObject.I
Define i.I
Define ComboBoxCell.I
Define CursorLocation.NSPoint
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define NotificationCenter.I = CocoaMessage(0, 0,
  "NSNotificationCenter defaultCenter")
Define RowHeight.CGFloat = 21
Define SelectedCell.I
Define SelectedColumn.I
Define SelectedRow.I
Define Selector1.I = sel_registerName_("itemChanged:")
Define Selector2.I = sel_registerName_("textDidEndEditing:")
Define SubclassedComboBoxCell.I

ProcedureC ComboBoxSelectionChangedCallback(Object.I, Selector.I,
  Notification.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)
      EndIf
    EndIf
  EndIf
EndProcedure 

ProcedureC EditingEndedCallback(Object.I, Selector.I, Notification.I)
  Shared ComboBoxCell.I

  Protected Cell.I
  Protected Column.I = CocoaMessage(0, GadgetID(0), "editedColumn")
  Protected Row.I = CocoaMessage(0, GadgetID(0), "editedRow")
  Protected Text.S

  Cell = CocoaMessage(0, Notification, "object")
  Text = PeekS(CocoaMessage(0, CocoaMessage(0, Cell, "stringValue"),
    "UTF8String"), -1, #PB_UTF8)

  If Column = #ComboBoxColumn
    If Trim(Text) <> ""
      If CocoaMessage(0, ComboBoxCell,
        "indexOfItemWithObjectValue:$", @Text) = #NSNotFound
        CocoaMessage(0, ComboBoxCell, "addItemWithObjectValue:$", @Text)
      EndIf
    EndIf
  EndIf
 
  SetGadgetItemText(0, Row, Text, Column)
EndProcedure

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 column 0 vertically
;       Caution: _setVerticallyCentered: is an internal flag; it could simply
;       disappear without warning in a future MacOS 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)
objc_registerClassPair_(SubclassedComboBoxCell)
object_setClass_(ComboBoxCell, SubclassedComboBoxCell)

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

; ----- Set callback to detect end of editing in ListIcon cell
class_addMethod_(DelegateClass, Selector2, @EditingEndedCallback(), "v@:@")
CocoaMessage(0, NotificationCenter,
  "addObserver:", AppDelegate,
  "selector:", Selector2,
  "name:$", @"NSControlTextDidEndEditingNotification",
  "object:", GadgetID(#ListIcon))

SetActiveGadget(#ListIcon)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      CocoaMessage(0, NotificationCenter, "removeObserver:", AppDelegate)
      Break
    Case #PB_Event_Gadget
      If EventGadget() = #ListIcon And EventType() = #PB_EventType_LeftClick
        CursorLocation\x = WindowMouseX(0)
        CursorLocation\y = WindowHeight(0) - WindowMouseY(0)
        CocoaMessage(@CursorLocation, GadgetID(#ListIcon),
          "convertPoint:@", @CursorLocation, "fromView:", 0)
        SelectedRow = CocoaMessage(0, GadgetID(#ListIcon),
          "rowAtPoint:@", @CursorLocation)
        SelectedColumn = CocoaMessage(0, GadgetID(#ListIcon),
          "columnAtPoint:@", @CursorLocation)

        If SelectedColumn = #ComboBoxColumn
          SelectedCell = CocoaMessage(0, ColumnObject,
            "dataCellForRow:", SelectedRow)
          CocoaMessage(0, SelectedCell, "setEditable:", #ComboBoxEditableState)
          CocoaMessage(0, GadgetID(#ListIcon),
            "editColumn:", SelectedColumn,
            "row:", GetGadgetState(#ListIcon),
            "withEvent:", 0,
            "select:", #YES)
        Else
          CocoaMessage(0, GadgetID(#ListIcon),
            "editColumn:", SelectedColumn,
            "row:", GetGadgetState(#ListIcon),
            "withEvent:", 0,
            "select:", #YES)
        EndIf
      EndIf
  EndSelect
ForEver

; ----- 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
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: How to add a second Callback ???

Post by Wolfram »

Thanks a lot!
macOS Catalina 10.15.7
Post Reply