Cocoa.ListBox.ContextualMenuAction Selected Row

Xojo 2014r2
Mavericks 10.9.4

I fall in something strange.

Contextual Menu in a ListBox:
If I do not choose a Row (select it) and I Right-Click (ctrl-click) on a Row, ListIndex = -1 because no Row is selected.

If you take a different point of view:

You have a selected Row n the ListBox and Right-Click (ctrl-click) in a different entry, the action will be done in the already selected Row, not in the one you clicked into !

Is this new ?
Yes, this is recent. I searched and found a compile done for Carbon in early june 2013 and the behavior is different: the Right-Click (ctrl-click) select the Row behind it !

So, the real question is: how do I port to Cocoa that feature ?

BTW: MouseDown is fired after the Right-Click, so storing there the X,Y values in Properties is useless.

PS: this looks like what the Finder does. You are allowed to activate the ContextualMenu for an item located where you want in a visible window, but if you have an item selected in the front window, the action is done on that object, not on the clicked one.

I too think that this is irritating to have two different behaviors for the ContextualMenu in a Listbox:

RightMouseClick => changes the row prior to the call of the menu
Ctrl-Key + LeftMouseClick => does not change the row at all

My solution is to create the popup menu in the MouseDown event and to not use ConstructContextualMenu and ContextualMenuAction at all (unless one subclasses Listbox, then one can raise these two events in MouseDown instead of handling everything in MouseDown):

If IsContextualClick Then Dim base As New MenuItem() base.Append(New MenuItem("A")) base.Append(...) Dim hitItem As MenuItem = base.PopUp(MouseX, MouseY) If hitItem <> Nil Then MsgBox(hitItem.Text) End Return True Else Return False End

I just set the ListIndex in the ConstructContextualMenu event

DIM row As Integer = me.RowFromXY(x, y) if (row <> -1) then me.ListIndex = row end if

Thanks for your answers.

Eli: I will check this afternoon.

From where do you get x, y ?

But yes, when you have the clicked row #, you can select it.

BTW: I do not found in the docs if the Row # returned by RowFromXY(x, y) is 0-based or 1-based.

Due to theXojo licensed shema, I avoid to run Xojo’s IDE while connected to internet.

If you are right then the behavior is a change … but I am not sure how long standing…

What it would avoid is the deselection others rows when you have a multiple selection.

The old behavior was always to select the current row but deselect any others even if the current row is already selected
The new behavior you say is to just show the menu and not do any selection

If so both are wrong IMO.

What should happen for a contextual click is this:

If the clicked row is already selected just show the contextual menu as it does now with deselecting anything.
If the row is NOT selected then select it, deselecting any other rows that were selected

I have always make it work that way by checking the selection state of the row clicked on in Mousedown and showing a contextual menu using MenuItem.popup, so I would not notice if the framework behavior changed.

This could be argued. Finder on OS X for example doesn’t change the selection, and I like that. It shows a temporary focus ring like line until the menu has been closed by the user. I find that the better solution.

Karen, Eli:

I cannot make my mind on what to do because both behaviors have good reasons to do like they do.

Back to the main question; here’s the message I wrote earlier:

Because I forgot to save this Conversation, I had to write this one and here’s what I ended with:

[code]Sub ShowFolder(LB As ListBox)
Dim ShowFolderFI As FolderItem
Dim LocX As Integer
Dim LocY As Integer
Dim Row As Integer
Dim Column As Integer

// Do nothing if the ListBox have no Row
If LB.ListCount = 0 Then Return

// Get the LocX, LocY values
LocX = LB.MouseX - LB.Left
LocY = LB.MouseY - LB.HeaderHeight

// Get the clicked Row, Column values
Row = LB.RowFromXY(LocX, LocY)
Column = LB.ColumnFromXY(LocX, LocY)

// Test only Row
If Row = -1 Then
End If

// Set the Row as Selected (for Ctrl-Click)
LB.Selected(Row) = True

// Show the selected folder
ShowFolderFI = LB.RowTag(Row)
If ShowFolderFI <> Nil And ShowFolderFI.Exists Then
// Open that folder
End If

// That’s All Folks !
End Sub[/code]

The Method is called from the ContextualMenuAction Event (where due to get the folder open).

It works fine, but I do not understand why I have to compute X: LB.MouseX - LB.Left.

Extensively tested on OS X, a bit of testing done on Windows XP, not tested in Linux (no Linux flavor installed in VirtualBox).

It is now too late to add the following idea in the shared code, but here I am.

Since Eli said that, I was not aware there is a difference beween ctrl-click and Right-Click !
Earlier today, when I awoke, I was thinking that if the user Right-Click, the line below is useless:

LB.Selected(Row) = True

So, adding an If block to detect if the ctrl key is depressed may be a good idea. Virtual code:

If Keyboard.AsyncCtrl Then
  LB.Selected(Row) = True
End If

One last word: the target ListBox have only one column and the code fits my needs. Your situation / use of ListBox may be different and you may have to modify my code. That is the reason I do not removed the line:

Column = LB.ColumnFromXY(LocX, LocY)

If you look closely the code, you will notice that I do not use Column.

At last, the code is XPlatform: I do not used AppleScript nor AppleEvent to achieve the goal. Thanks goes to the community (where I get the idea).


On Windows XP, Right-Click or Ctrl-Click share the same behaviour.

[quote=116593:@Emile Schwarz]shao:
From where do you get x, y ?[/quote]

Passed as parameters to the event