Dealing with multiple popups sharing one menu?

Hi all,

I have a ContainerControl with a canvas and a popup.

The popup contains a list of possible modifications.

On the window I have ten of these ContainerControls:

The popup selects which item (“Modification”) is associated with which colour. I have a dictionary dictModificationToColour for this.

So far so good.

Now I want to disable an item in the menu when it has been selected so as to avoid double-selections.

How would I best go about this? Everything I come up with seems quite cumbersome and not OOP elegant …

[quote=122213:@Markus Winter]Now I want to disable an item in the menu when it has been selected so as to avoid double-selections.

How would I best go about this? Everything I come up with seems quite cumbersome and not OOP elegant …[/quote]

In the popupMenu ? :

Sub Change() me.Enabled = false End Sub

I’m not sure there is an elegant way to do this. My first approach would be to reload the other popup’s based on the new selection.

[quote=122215:@Michel Bujardet]In the popupMenu ? :

Sub Change() me.Enabled = false End Sub [/quote]
Not quite :wink:

Sorry. I just read again your post and realized my mistake.

I do not know how you process the value of the popupmenus, but just an idea :

Sub Change() static oldindex as integer = 0 if instr(me.text,"?") = 1 then me.listindex = oldindex return end if dim oldtext as string = me.text dim zindex as integer = me.listindex me.RemoveRow(zindex) me.InsertRow(zindex,"? "+oldtext) oldindex = me.listindex End Sub

This way when a row has been selected it starts with ?, so when one tries to select it a second time you can revert to the previous choice and ignore it. Just an idea to keep the code in the PopupMenu.

I think what he’s asking for is when a menu option is selected for the gray color, for example, it either disables or removes it from the lists of all the OTHER popups.

Then it becomes a horse of a different color :wink:

Since you can initialize the PopupMenu in MouseDown when the user clicks on it, you can maintain a central multi-dimensions array containing all the options for all the PopMenus, so you can manage selected colors for all.

Since is not possible to disable a single item in a popupmenu control, you have to build the popup in the mouseDown event.
So the open event of your colorPopup control set :

if v.Ubound=-1 then v.Append "":"Unassigned" v.Append "U":"Unmodified" v.Append "A":"Acetil" v.Append "O":"Oxidation" v.Append "P":"Propionyl" end if me.AddRow v(0).Right.StringValue me.RowTag(0)=v(0).Left.StringValue me.ListIndex=0
where v is v() as pair a window property

then in the mouseDown event:

#Pragma Unused x #Pragma Unused y dim t() as String dim c as String=me.RowTag(me.ListIndex) for i as integer=0 to kLastIndexOfColorPopup if i<>index and colorPopup(i).RowTag(colorPopup(i).ListIndex).StringValue<>"" then t.Append colorPopup(i).RowTag(colorPopup(i).ListIndex) end if next me.DeleteAllRows dim r as integer for i as integer=0 to v.Ubound if t.IndexOf(v(i).Left.StringValue)=-1 then me.AddRow v(i).Right.StringValue me.RowTag(r)=v(i).Left.StringValue if c=v(i).Left.StringValue then me.ListIndex=r r=r+1 end if next

This code will not work on linux since the mouseDown event is never called. In that case you have to use a flag to “block” the change event and rebuild all the popupmenu in the change event.

[quote=122439:@Antonio Rinaldi]This code will not work on linux since the mouseDown event is never called. In that case you have to use a flag to “block” the change event and rebuild all the popupmenu in the change event.
[/quote]

It s possible to implement a workaround MouseDown for the PopMenu in Linux through a 100 ms multiple timer with this code in the Action event :

Sub Action() if System.MouseDown then if System.MouseX > PopupMenu1.left+self.left and system.MouseX < PopupMenu1.left+self.left+PopupMenu1.width _ and System.MouseY > PopupMenu1.Top+self.top and System.MouseY < PopupMenu1.Top+self.top+PopupMenu1.Height then msgbox "mousedown !" end if end if End Sub

Thanks for all the suggestions - I think I have a very nice solution now involving two methods in a custom PopupMenu and two arrays on the window (to keep track of the controls).

Example project at https://dl.dropboxusercontent.com/u/992591/Xojo/Forum/PopupMenu%20example.rbp.zip

Try opening a new window and changing the menu items (via the TextArea)

your solution works
But is not really reusable (think about all the window1 cast the you make)

A better solution is:

  • create the container control with the canvas and the popup (as you did)

  • create 2 computed property theText and theColor

  • in theColor set add the canvas invalidate command

  • in theText get return if(popupMenu1.listIndex>0,popupMenu1.text, kNotAssigned)

  • in theText set verify set the popup listIndex to the proper value (0 if the same value or based on the list)
    This “rule” is the core function since if the passed value is the same as the current then it will change the list index to the non assigned index otherwise it will change to the listIndex value (if exist)

  • create a method to reassign the values list with a string array

  • create a method to initialize the values (color, list and current text) (if you add the component via code otherwise call the reassign list in the window open event)

  • define an event with the newText as parameter to be called from the popupmenu change event if the current text is not kNotAssigned

Use this event in the window to tell to all the other containers that the new text has been selected so who has that value must reset to kNotAssigned. To do this simply set the other container property theText to the newText value.

This way your container is self contained.

Thanks, Antonio. Not sure yet how that second bit works though …

[quote=123161:@Antonio Rinaldi]

  • create a method to reassign the values list with a string array

  • create a method to initialize the values (color, list and current text) (if you add the component via code otherwise call the reassign list in the window open event)

  • define an event with the newText as parameter to be called from the popupmenu change event if the current text is not kNotAssigned

Use this event in the window to tell to all the other containers that the new text has been selected so who has that value must reset to kNotAssigned. To do this simply set the other container property theText to the newText value.

This way your container is self contained.[/quote]

define an event in the container control (for example TextChanged(newText as string)
in the popupmenu change event write if me.text<> kNotAssigned then raiseEvent TextChanged me.text

How to use this event in your window is based on how you put the container control (via IDE o by code)
In general the strategy is:
tell to all this containerControl except the one that raised this event to set theText to the newText parameter.
For example (if you used the IDE to put the CCs and they are named cc1,cc2,cc3 etc)
in cc2 TextChanged event you have to write:
cc1.theText=newText
cc3.theText=newText

instead if you use addHandler to set the EventAction and you have a cc() array of cc you have to define a window method like:
doTextChanged(c as myCC, newText as string)
for i as integer=0 to cc.ubound
if cc(i)<>c then cc(i).theText=newText
next