Page 1 of 1

Edit TreeGadget items?

Posted: Tue Sep 04, 2018 8:05 pm
by wombats
Hi,

I'm trying to set it up so users can edit TreeGadget items, but it's safe to say I have absolutely no idea what I'm doing. I can't get it to start the editing. I'd like to be able to validate what they enter. Does anyone have any tips?

I started with this code by Shardik.

Code: Select all

EnableExplicit

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
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 View.I
    View = CocoaMessage(0, CocoaMessage(0, 0, "NSTextField new"), "setEditable:", #YES)
  ProcedureReturn View
EndProcedure

OpenWindow(0, 200, 100, 430, 126, "")
TreeGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)

AddGadgetItem(0, -1, "Harry Rannit")
AddGadgetItem(0, -1, "Ginger Brokeit")
AddGadgetItem(0, -1, "Didi Foundit")

class_addMethod_(DelegateClass, Selector, @GetViewForCellCallback(), "v@:@@@")
CocoaMessage(0, GadgetID(0), "setDelegate:", AppDelegate)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: Edit TreeGadget items?

Posted: Wed Sep 05, 2018 9:21 pm
by Shardik
I had already posted this example of a ListIconGadget with editable cells. Since internally a ListIconGadget in PureBasic is a NSTableView and a TreeGadget is a NSOutlineView which is based on a NSTableView, it would have been as easy as changing the ListIconGadget into a TreeGadget in my example. For your conveniance I have done that for you. (And I have removed the procedure ConvertToUTF8() because it isn't necessary anymore since Fred has removed a bug requiring this produre as a workaround.)

Code: Select all

EnableExplicit

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

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)
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("textDidEndEditing:")

OpenWindow(0, 200, 100, 220, 130, "Editable TreeGadget")
TreeGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
AddGadgetItem(0, -1, "Fruits")
AddGadgetItem(0, -1, "Apples", 0, 1)
AddGadgetItem(0, -1, "Bananas", 0, 1)
AddGadgetItem(0, -1, "Oranges", 0, 1)
SetGadgetItemState(0, 0, #PB_Tree_Expanded)
CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", -1)
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

Re: Edit TreeGadget items?

Posted: Thu Sep 06, 2018 8:40 am
by wombats
Thanks, Shardik. I didn't see your ListIconGadget code.

It doesn't work in my project...it won't start the edit when I call:

Code: Select all

CocoaMessage(0, GadgetID(0),
          "editColumn:", SelectedColumn,
          "row:", GetGadgetState(0),
          "withEvent:", 0,
          "select:", #YES)
EndIf
I copied your code exactly. I commented out any other CocoaGadget code I have in there. Can you think of anything that could interfere with it?

Re: Edit TreeGadget items?

Posted: Thu Sep 06, 2018 9:57 am
by Shardik
Does my code example from above work unchanged?

If yes, try to post a stripped down version of your project that demonstrates the non-working edit operation.

And please state your MacOS version and your PureBasic version, 32-bit or 64-bit, ASCII or Unicode mode...

Re: Edit TreeGadget items?

Posted: Thu Sep 06, 2018 10:16 am
by wombats
Yes, your code sample works fine unchanged.

It seems the problem is that my TreeGadget is a pointer within a Structure. Is that the wrong thing to do?

Code: Select all

EnableExplicit

Structure MyStructure
  *gadget
EndStructure

Global *test.MyStructure = AllocateStructure(MyStructure)

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

ProcedureC EditingFinishedCallback(Object.I, Selector.I, Notification.I)
  Protected EditedCell.I
  Protected EditedColumn.I = CocoaMessage(0, GadgetID(*test\gadget), "editedColumn")
  Protected EditedRow.I = CocoaMessage(0, GadgetID(*test\gadget), "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)
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("textDidEndEditing:")

OpenWindow(0, 200, 100, 220, 130, "Editable TreeGadget")

*test\gadget = TreeGadget(#PB_Any, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
AddGadgetItem(*test\gadget, -1, "Fruits")
AddGadgetItem(*test\gadget, -1, "Apples", 0, 1)
AddGadgetItem(*test\gadget, -1, "Bananas", 0, 1)
AddGadgetItem(*test\gadget, -1, "Oranges", 0, 1)
SetGadgetItemState(*test\gadget, 0, #PB_Tree_Expanded)
CocoaMessage(0, GadgetID(*test\gadget), "setSelectionHighlightStyle:", -1)
class_addMethod(DelegateClass, Selector, @EditingFinishedCallback(), "v@:@")
CocoaMessage(0, NotificationCenter,
  "addObserver:", AppDelegate,
  "selector:", Selector,
  "name:$", @"NSControlTextDidEndEditingNotification",
  "object:", GadgetID(*test\gadget))

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(*test\gadget),
          "convertPoint:@", @CursorLocation, "fromView:", 0)
        SelectedColumn = CocoaMessage(0, GadgetID(*test\gadget),
          "columnAtPoint:@", @CursorLocation)
        CocoaMessage(0, GadgetID(*test\gadget),
          "editColumn:", SelectedColumn,
          "row:", GetGadgetState(*test\gadget),
          "withEvent:", 0,
          "select:", #YES)
      EndIf
  EndSelect
ForEver
I am using PB v5.70b1 Unicode on macOS High Sierra.

Re: Edit TreeGadget items?

Posted: Thu Sep 06, 2018 8:57 pm
by Shardik
In your code you have forgotten to modify 2 gadget references. Please change in procedure EditingFinishedCallback()

Code: Select all

SetGadgetItemText(0, EditedRow, EditedText, EditedColumn)
to

Code: Select all

  SetGadgetItemText(*test\gadget, EditedRow, EditedText, EditedColumn)
and in your event loop please change

Code: Select all

      If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
to

Code: Select all

      If EventGadget() = *test\gadget And EventType() = #PB_EventType_LeftClick
and your example will run like a charm... :wink:

Re: Edit TreeGadget items?

Posted: Fri Sep 07, 2018 10:32 am
by wombats
You're right about that. That was a stupid mistake.

However...it's still not working in my project. I can't see what the problem is.

Re: Edit TreeGadget items?

Posted: Sat Mar 16, 2019 6:05 pm
by wombats
I am at my wits' end trying to get this to work in my project. It works if I create a second window and put the TreeGadget in that, but it doesn't work in my main window. I don't understand it.

I have commented out all the unrelated CocoaMessage commands, but that makes no difference.

I have tried recreating the setup of my project in an example (with the TreeGadget in containers, etc.) and it works fine there.

It works if I do this:

Code: Select all

CocoaMessage(0, GadgetID(tree), "setDelegate:", delegateClass)
However, the TreeGadget's items' text disappears.

I am so confused.

Re: Edit TreeGadget items?

Posted: Sat Mar 16, 2019 7:35 pm
by Shardik
Sorry, but we are only able to help you if you post a stripped down example demonstrating your problems...

Re: Edit TreeGadget items?

Posted: Sun Mar 17, 2019 6:40 am
by wombats
I know. I'm sorry I couldn't provide one earlier. I spent a long time picking apart my project to see what could be the problem and then I suddenly realised the difference between the TreeGadget in my project and the one in the example...the items in mine have images.

So when an item has an image, it cannot be edited. I am afraid this might be a showstopper for this functionality, though I hope not.

Code: Select all

EnableExplicit

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

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)
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("textDidEndEditing:")

CreateImage(0, 16, 16, 24, #Red)
CreateImage(1, 16, 16, 24, #Blue)

OpenWindow(0, 200, 100, 220, 130, "Editable TreeGadget")
TreeGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
AddGadgetItem(0, -1, "Fruits", ImageID(0))
AddGadgetItem(0, -1, "Apples", 0, 1)
AddGadgetItem(0, -1, "Bananas", ImageID(1), 1)
AddGadgetItem(0, -1, "Oranges", 0, 1)
SetGadgetItemState(0, 0, #PB_Tree_Expanded)
CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", -1)
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

Re: Edit TreeGadget items?

Posted: Mon Mar 25, 2019 2:54 pm
by Shardik
wombats wrote:I know. I'm sorry I couldn't provide one earlier. I spent a long time picking apart my project to see what could be the problem and then I suddenly realised the difference between the TreeGadget in my project and the one in the example...the items in mine have images.
Thank you for taking the time to strip down your code until you were able to demonstrate the error.
wombats wrote:So when an item has an image, it cannot be edited. I am afraid this might be a showstopper for this functionality, though I hope not.
Unfortunately I haven't found a solution yet. Fred seems to use a custom object called "TreeItem". Only Fred or freak possibly know how to make it possible to edit a "TreeItem" containing both an image and a text. The only but complicated alternative I currently can think of is to program a pure API solution using the NSOutlineView on which the TreeGadget is based on. As a further complication the TreeGadget is based on an old cell-based approach which doesn't offer a combination of image and text with native Cocoa framework methods. From the Apple OpenSource site you may download the class ImageAndTextCell.m and header file ImageAndTextCell.h which you would have to implement in your PureBasic code.

Re: Edit TreeGadget items?

Posted: Tue Apr 02, 2019 2:58 pm
by wombats
Shardik wrote:
wombats wrote:I know. I'm sorry I couldn't provide one earlier. I spent a long time picking apart my project to see what could be the problem and then I suddenly realised the difference between the TreeGadget in my project and the one in the example...the items in mine have images.
Thank you for taking the time to strip down your code until you were able to demonstrate the error.
wombats wrote:So when an item has an image, it cannot be edited. I am afraid this might be a showstopper for this functionality, though I hope not.
Unfortunately I haven't found a solution yet. Fred seems to use a custom object called "TreeItem". Only Fred or freak possibly know how to make it possible to edit a "TreeItem" containing both an image and a text. The only but complicated alternative I currently can think of is to program a pure API solution using the NSOutlineView on which the TreeGadget is based on. As a further complication the TreeGadget is based on an old cell-based approach which doesn't offer a combination of image and text with native Cocoa framework methods. From the Apple OpenSource site you may download the class ImageAndTextCell.m and header file ImageAndTextCell.h which you would have to implement in your PureBasic code.
That's a shame. Thank you for looking into it.

Re: Edit TreeGadget items?

Posted: Wed Jul 17, 2019 12:25 pm
by Shardik
I have found a workaround to edit tree items with an image. My example code does the following:
If a tree item selected for edit contains an image:
- Grab that image
- Delete that image from the item
- When text editing starts, no icon is displayed and text editing is possible
- After text editing ends, redisplay the image again

The first step was to find a way how to grab the image from a tree item. For this purpose I already posted this cross-platform example.

This is the complete example which I tested successfully on MacOS 10.6.8 'Snow Leopard' with PB 5.46 x86 in ASCII and Unicode mode and on MacOS 10.13.6 'High Sierra' with PB 5.46 x64 in ASCII and Unicode mode:

Code: Select all

EnableExplicit

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define Cell.I
Define CellImage.I
Define ClassName.S
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define NotificationCenter.I = CocoaMessage(0, 0,
  "NSNotificationCenter defaultCenter")
Define SelectedRow.I
Define Selector.I = sel_registerName_("textDidEndEditing:")

ProcedureC EditingFinishedCallback(Object.I, Selector.I, Notification.I)
  Shared CellImage.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)

  If CellImage
    SetGadgetItemImage(0, EditedRow, CellImage)
    CellImage = 0
  EndIf
EndProcedure

Procedure GetGadgetItemImage(TreeGadgetID.I, Row.I)
  Protected Item.I
  Protected CellImage.I

  Item = CocoaMessage(0, GadgetID(TreeGadgetID), "itemAtRow:", Row)
  CellImage = PeekI(PeekI(PeekI(Item + SizeOf(Integer))) + SizeOf(Integer) * 3)

  ProcedureReturn CellImage
EndProcedure

CreateImage(0, 16, 16, 24, $FF)
CreateImage(1, 16, 16, 24, $FF0000)

OpenWindow(0, 200, 100, 220, 130, "Editable TreeGadget")
TreeGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
AddGadgetItem(0, -1, "Fruit", ImageID(0))
AddGadgetItem(0, -1, "Apples", 0, 1)
AddGadgetItem(0, -1, "Bananas", ImageID(1), 1)
AddGadgetItem(0, -1, "Oranges", 0, 1)
SetGadgetItemState(0, 0, #PB_Tree_Expanded)
CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", -1)
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
        SelectedRow = GetGadgetState(0)
        Cell = CocoaMessage(0, GadgetID(0),
          "preparedCellAtColumn:", 0,
          "row:", SelectedRow)
        ClassName = PeekS(CocoaMessage(0, CocoaMessage(0,
          Cell, "className"), "UTF8String"), -1, #PB_UTF8)

        If ClassName = "PBIconTextCell"
          CellImage = GetGadgetItemImage(0, SelectedRow)
          SetGadgetItemImage(0, SelectedRow, 0)
        EndIf

        CocoaMessage(0, GadgetID(0), "setFocusedColumn:", 0)
        CocoaMessage(0, GadgetID(0),
          "editColumn:", 0,
          "row:", SelectedRow,
          "withEvent:", 0,
          "select:", #YES)
      EndIf
  EndSelect
ForEver

Re: Edit TreeGadget items?

Posted: Sun Mar 10, 2024 2:04 pm
by wombats
My apologies for not thanking you for that. This doesn't seem to work anymore in the latest PureBasic, unfortunately. I had created my own tree gadget using the CanvasGadget since drag and drop wasn't working on macOS, but now that's fixed, I'd like to use the proper TreeGadget. I couldn't get my own tree to work as well.

Am I remembering correctly that there was a relatively recent change and PureBasic now handles the item in a custom manner? Would that mean this isn't possible now?

Re: Edit TreeGadget items?

Posted: Sun Mar 10, 2024 2:33 pm
by mk-soft
That is correct.
Purebasic uses a new owner draw for the item cells.
This also applies to the ListViewGadget and ListIconGadget.
This means that all old examples no longer work.