Visible property of CActions

Hello,

Fortunately, cButtons, CMenus, and all CDecorateableActions provide a very nice API (pretty much like a Swing JComponent) to:

-setAccelerator
-setDisabledIcon
-setEnabled
-setIcon
-setText
-setTooltip

so thanks for that already!! :smiley: but my hapiness would be full with an additional -setVisible :frowning:

We could get the same effect of toggling the visibility of the action by adding/removing it from its corresponding Dockable, I think. However, my code is organized such that at some point I manipulate Actions without having access to the Dockable. And the API above allows me to do that perfectly. It’s just the setVisible that I’m missing to show/hide a certain Action.

Any suggestions?

Thanks a lot,
Shant

There is a possibility to write a filter for invisible actions using a DockActionOffer, I’ll upload an example in the evening, atm I have to go to work. This solution would require that you use one of the newer versions.

[Edit, to be exact: a new DockActionOffer would wrap around the DefaultDockActionOffer. It adds a custom wrapper-DockActionSource around the default DockActionSource. This wrapper source filters actions that are invisible. This would also require some new “ActionVisibleListener” to inform the custom source if an action was made visible or invisible.
This solution would have the advantage, that you can use the actions in the same way you do now.]

The other solution would be writing subclasses for all actions, these subclasses know to which CDockable the action belongs. The would add or remove themselves depending on the visibility. Unfortunatelly an action would need to know all the other actions to find its correct position. But that could be solved with a simple shared list. It’s an easy, but not very elegant solution.

Thank you Beni!

Looking forward for your example! :slight_smile:

My example got a bit bigger than anticipated :twisted: On the other hand this “example” is an application that you can copy and test for yourself.

You do not necessarily need to understand what is going on to use this code in your application. Just copy all the inner interfaces/classes into new files, add the line “control.getController().addActionOffer( new VisibilityActionOffer() );” to your application, and you have a visibility property.


import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.AbstractDockActionSource;
import bibliothek.gui.dock.action.ActionOffer;
import bibliothek.gui.dock.action.DefaultActionOffer;
import bibliothek.gui.dock.action.DockAction;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.LocationHint;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.CAction;
import bibliothek.gui.dock.common.action.CButton;
import bibliothek.gui.dock.common.action.core.CommonDockAction;
import bibliothek.gui.dock.event.DockActionSourceListener;

/**
 * This example application is going to show, how we can customize the framework in order to add a "visibility" property
 * to CActions. While this application has only one top-level class, all the inner classes are static and could easily
 * be moved to their own files. People interested in using this example are highly encouraged to extract all the inner
 * classes and interfaces!
 * <br><br>
 * The example consists of:
 * <ul>
 * 	<li>{@link TestDockable}: A dockable that offers some buttons to add and remove actions. </li>
 * 	<li>{@link CVisibleAction}: An interface any CAction with the visibility property has to implement. Any action
 * without this interface is always visible.</li>
 *  <li>{@link ActionVisibilityListener}: A listener that is added to a {@link CVisibleAction}, it is informed whenever
 *  the visibility state changes. </li>
 *  <li>{@link CustomButton}: A {@link CButton} that implements {@link CVisibleAction}, we add and remove some of these
 *  buttons to show that the code works.</li>
 *  <li>{@link ActionVisibility}: Not really necessary, but this class helps making the implementation of {@link CustomButton} easier.</li>
 *  <li>{@link VisibilityFilteredActionSource}: This is the hard part. This {@link DockActionSource} contains all the actions that belong
 *  to {@link TestDockable}, and it has a filter searching for the visibility property. Even more, it adds an {@link ActionVisibilityListener} to
 *  all actions, and thus any change of the visibility property has an immediate effect. </li>
 *  <li>{@link VisibilityActionOffer}: This class is needed to override the default behavior of how actions for a 
 *  dockable are collected. In our case the default behavior needs just a little update: we need to introduce
 *  our {@link VisibilityFilteredActionSource} as wrapper around the original list of actions.</li>
 * </ul> 
 *  
 * @author Beni
 *
 */
public class Dock02 {
	public static void main( String[] args ){
		JFrame frame = new JFrame("Test");
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setBounds( 20, 20, 400, 400 );
		
		CControl control = new CControl( frame );
		
		// This is an important line, here we install our custom ActionOffer
		control.getController().addActionOffer( new VisibilityActionOffer() );
		
		frame.add( control.getContentArea() );
		
		TestDockable dockable = new TestDockable();
		control.addDockable( dockable );
		dockable.setVisible( true );
		frame.setVisible( true );
	}
	
	/**
	 * This dockable offers controls for adding, removing and modifying {@link CVisibleAction}s.
	 */
	private static class TestDockable extends DefaultSingleCDockable{
		private ActionModel model = new ActionModel();
		private JTable table;
		
		public TestDockable(){
			super( "test", "Test" );
			
			table = new JTable( model );
			JButton addVisible = new JButton("Add visible");
			JButton addInvisible = new JButton("Add invisible");
			JButton remove = new JButton("Remove selection");
			
			Insets insets = new Insets( 0, 0, 0, 0 );
			setLayout( new GridBagLayout() );
			add( new JScrollPane( table ), new GridBagConstraints( 0, 0, 1, 1, 100.0, 100.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insets, 0, 0 ));
			JPanel buttons = new JPanel( new GridLayout( 1, 3 ));
			add( buttons, new GridBagConstraints( 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.LAST_LINE_END, GridBagConstraints.VERTICAL, insets, 0, 0 ));
			buttons.add( addVisible );
			buttons.add( addInvisible );
			buttons.add( remove );
			
			addVisible.addActionListener( new ActionListener(){
				@Override
				public void actionPerformed( ActionEvent e ){
					int index = table.getSelectedRow()+1;
					model.insert( index, true );
					table.getSelectionModel().setSelectionInterval( index, index );
				}
			});
			addInvisible.addActionListener( new ActionListener(){
				@Override
				public void actionPerformed( ActionEvent e ){
					int index = table.getSelectedRow()+1;
					model.insert( index, false );
					table.getSelectionModel().setSelectionInterval( index, index );
				}
			});
			remove.addActionListener( new ActionListener(){
				@Override
				public void actionPerformed( ActionEvent e ){
					int index = table.getSelectedRow();
					if( index != -1 ){
						model.remove( index );
					}
				}
			});
		}
		
		/**
		 * This {@link Icon} is added to the {@link CustomButton}s.
		 */
		private class ActionIcon implements Icon{
			private Color color;
			public ActionIcon( Color color ){
				this.color = color;
			}
			
			@Override
			public void paintIcon( Component c, Graphics g, int x, int y ){
				g.setColor( color );
				g.fillOval( x, y, 16, 16 );
			}
			@Override
			public int getIconWidth(){
				return 16;
			}
			@Override
			public int getIconHeight(){
				return 16;
			}
		}
		
		/**
		 * This {@link TableModel} shows all the {@link CustomButton}s that we added to this
		 * {@link TestDockable}, it also offers methods to add and remove buttons.
		 */
		private class ActionModel extends AbstractTableModel{
			private List<CustomButton> buttons = new ArrayList<CustomButton>();
			
			public void insert( int index, boolean visible ){
				Color color = new Color( (int)(0xFFFFFF * Math.random()));
				CustomButton button = new CustomButton();
				button.setIcon( new ActionIcon( color ) );
				button.setText( color.getRed() + " " + color.getGreen() + " " + color.getBlue() );
				button.setVisible( visible );
				
				insertAction( index, button );
				buttons.add( index, button );
				fireTableRowsInserted( index, index );
			}
			
			public void remove( int index ){
				removeAction( index );
				buttons.remove( index );
				fireTableRowsDeleted( index, index );
			}
			
			@Override
			public int getRowCount(){
				return buttons.size();
			}

			@Override
			public int getColumnCount(){
				return 3;
			}
			
			@Override
			public String getColumnName( int column ){
				switch( column ){
					case 0: return "";
					case 1: return "";
					case 2: return "Name";
					default: throw new IllegalArgumentException();
				}
			}
			
			@Override
			public Class<?> getColumnClass( int column ){
				switch( column ){
					case 0: return Boolean.class;
					case 1: return Icon.class;
					case 2: return String.class;
					default: throw new IllegalArgumentException();
				}
			}
			
			@Override
			public boolean isCellEditable( int rowIndex, int columnIndex ){
				return columnIndex == 0;
			}

			@Override
			public void setValueAt( Object aValue, int rowIndex, int columnIndex ){
				CustomButton button = buttons.get( rowIndex );
				button.setVisible( (Boolean)aValue );
				fireTableCellUpdated( rowIndex, columnIndex );
			}
			
			@Override
			public Object getValueAt( int rowIndex, int columnIndex ){
				CustomButton button = buttons.get( rowIndex );
				switch( columnIndex ){
					case 0: return button.isVisible();
					case 1: return button.getIcon();
					case 2: return button.getText();
					default: throw new IllegalArgumentException();
				}
			}
			
		}
	}
	
	/**
	 * This {@link ActionOffer} wrapps each {@link DockActionSource} in a {@link VisibilityFilteredActionSource} and
	 * thus enables support for the visibility property of {@link CVisibleAction}.
	 */
	public static class VisibilityActionOffer extends DefaultActionOffer{
		@Override
		public DockActionSource getSource( Dockable dockable, DockActionSource source, DockActionSource[] guards, DockActionSource parent, DockActionSource[] parents ){
			DockActionSource result = super.getSource( dockable, source, guards, parent, parents );
			return new VisibilityFilteredActionSource( result );
		}
	}
	
	/**
	 * This {@link DockActionSource} shows only those {@link CVisibleAction}s whose visibility property
	 * is set to <code>true</code>
	 */
	public static class VisibilityFilteredActionSource extends AbstractDockActionSource{
		/** all the actions that could be shown, even the invisble ones */
		private DockActionSource source;
		
		/**
		 * This listener is added to {@link #source} and to any {@link CVisibleAction} if there
		 * are listeners attached to <code>this</code>. 
		 */
		private Listener listener;
		
		public VisibilityFilteredActionSource( DockActionSource source ){
			this.source = source;
		}

		/**
		 * Tells whether <code>action</code> should be shown.
		 * @param action some action of {@link #source}
		 * @return whether <code>action</code> should be shown
		 */
		public boolean isVisible( DockAction action ){
			if( action instanceof CommonDockAction ){
				CAction caction = ((CommonDockAction)action).getAction();
				if( caction instanceof CVisibleAction ){
					return ((CVisibleAction)caction).isVisible();
				}
			}
			return true;
		}
		
		@Override
		public LocationHint getLocationHint(){
			return source.getLocationHint();
		}

		@Override
		public int getDockActionCount(){
			int sum = 0;
			for( DockAction action : source ){
				if( isVisible( action )){
					sum++;
				}
			}
			return sum;
		}

		@Override
		public DockAction getDockAction( int index ){
			int count = 0;
			for( DockAction action : source ){
				if( isVisible( action )){
					if( count == index ){
						return action;
					}
					else{
						count++;
					}
				}
			}
			throw new IndexOutOfBoundsException();
		}
		
		@Override
		public Iterator<DockAction> iterator(){
			return new Iterator<DockAction>(){
				private Iterator<DockAction> iterator = source.iterator();
				private DockAction next;
				
				private void forward(){
					next = null;
					while( next == null && iterator.hasNext() ){
						DockAction action = iterator.next();
						if( isVisible( action )){
							next = action;
						}
					}
				}
				
				@Override
				public boolean hasNext(){
					if( next == null ){
						forward();
					}
					return next != null;
				}
				
				@Override
				public DockAction next(){
					if( !hasNext() ){
						throw new NoSuchElementException();
					}
					DockAction result = next;
					next = null;
					return result;
				}
				
				@Override
				public void remove(){
					iterator.remove();
				}
			};
		}
		
		@Override
		public void addDockActionSourceListener( DockActionSourceListener listener ){
			if( !hasListeners() ){
				setListening( true );
			}
			super.addDockActionSourceListener( listener );
		}
		
		@Override
		public void removeDockActionSourceListener( DockActionSourceListener listener ){
			super.removeDockActionSourceListener( listener );
			if( !hasListeners() ){
				setListening( false );
			}
		}
		
		/**
		 * Either install or deinstalls {@link #listener} to all {@link CVisibleAction}s.
		 * @param listening whether to install or to deinstall the listener
		 */
		private void setListening( boolean listening ){
			if( listening ){
				if( listener == null ){
					listener = new Listener();
					source.addDockActionSourceListener( listener );
					for( DockAction action : source ){
						setListening( action, true );
					}
				}
			}
			else{
				if( listener != null ){
					source.removeDockActionSourceListener( listener );
					for( DockAction action : source ){
						setListening( action, false );
					}
					listener = null;
				}
			}
		}
		
		/**
		 * Either adds or removes {@link #listener} from the {@link CVisibleAction} that is represented
		 * by <code>action</code>.
		 * @param action the action which may or may not need a listener
		 * @param listening whether to add or to remove {@link #listener}
		 */
		private void setListening( DockAction action, boolean listening ){
			if( action instanceof CommonDockAction ){
				CAction caction = ((CommonDockAction)action).getAction();
				if( caction instanceof CVisibleAction ){
					CVisibleAction visibleAction = (CVisibleAction)caction;
					if( listening ){
						visibleAction.addVisibilityListener( listener );
					}
					else{
						visibleAction.removeVisibilityListener( listener );
					}
				}
			}
			
		}
		
		private class Listener implements DockActionSourceListener, ActionVisibilityListener{
			private List<DockAction> cache = new ArrayList<DockAction>();
			
			public Listener(){
				for( DockAction action : source ){
					cache.add( action );
				}
			}
			
			@Override
			public void visibilityChanged( CVisibleAction caction, boolean visible ){
				DockAction action = caction.asAction().intern();
				int index = 0;
				for( DockAction next : cache ){
					if( next == action ){
						break;
					}
					if( isVisible( next )){
						index++;
					}
				}
				
				if( visible ){
					fireAdded( index, index );
				}
				else{
					fireRemoved( index, index );
				}
			}

			@Override
			public void actionsAdded( DockActionSource source, int firstIndex, int lastIndex ){
				// find index of first and last new action
				int firstVisibleIndex = -1;
				int lastVisibleIndex = -1;
				
				int count = 0;
				int index = 0;
				
				for( DockAction action : source ){
					if( index >= firstIndex && index <= lastIndex ){
						cache.add( index, action );
						setListening( action, true );
					}
					
					if( isVisible( action )){
						if( index >= firstIndex ){
							if( firstVisibleIndex == -1 ){
								firstVisibleIndex = count;
							}
							lastVisibleIndex = count;
						}
						count++;
					}
					index++;
					if( index > lastIndex ){
						break;
					}
				}
				
				if( firstVisibleIndex != -1 && lastVisibleIndex != -1 ){
					fireAdded( firstVisibleIndex, lastVisibleIndex );
				}
			}

			@Override
			public void actionsRemoved( DockActionSource source, int firstIndex, int lastIndex ){
				// find index of first and last removed action
				int firstVisibleIndex = -1;
				int lastVisibleIndex = -1;
				
				int count = 0;
				int index = 0;
				
				for( DockAction action : cache ){
					if( isVisible( action )){
						if( index >= firstIndex ){
							if( firstVisibleIndex == -1 ){
								firstVisibleIndex = count;
							}
							lastVisibleIndex = count;
						}
						count++;
					}
					index++;
					if( index > lastIndex ){
						break;
					}
				}
				
				for( int i = lastIndex; i >= firstIndex; i-- ){
					setListening( cache.remove( i ), false );
				}
				
				if( firstVisibleIndex != -1 && lastVisibleIndex != -1 ){
					fireRemoved( firstVisibleIndex, lastVisibleIndex );
				}
			}
			
		}
	}
	
	/**
	 * The {@link ActionVisibilityListener} is added to {@link CVisibleAction}s, this observer
	 * is informed if the visibility property of the action changes
	 */
	private interface ActionVisibilityListener {
		public void visibilityChanged( CVisibleAction action, boolean visible );
	}

	/**
	 * The {@link CVisibleAction} interface can be added to any {@link CAction}, it
	 * allows to make the action visible or invisible 
	 */
	private interface CVisibleAction {
		public CAction asAction();
		public void setVisible( boolean visible );
		public boolean isVisible();
		public void addVisibilityListener( ActionVisibilityListener listener );
		public void removeVisibilityListener( ActionVisibilityListener listener );
	}
	
	/**
	 * A helper class to manage the visibility property of a {@link CVisibleAction}
	 * and the {@link ActionVisibilityListener}s.
	 */
	public static class ActionVisibility{
		private boolean visible = true;
		private List<ActionVisibilityListener> listeners = new ArrayList<ActionVisibilityListener>();
		private CVisibleAction action;
		
		public ActionVisibility( CVisibleAction action ){
			this.action = action;
		}
		
		public void addVisibilityListener( ActionVisibilityListener listener ){
			listeners.add( listener );
		}
		
		public void removeVisibilityListener( ActionVisibilityListener listener ){
			listeners.remove( listener );
		}
		
		public boolean isVisible(){
			return visible;
		}
		
		public void setVisible( boolean visible ){
			if( this.visible != visible ){
				this.visible = visible;
				for( ActionVisibilityListener listener : listeners ){
					listener.visibilityChanged( action, visible );
				}
			}
		}
	}
	
	/**
	 * A CButton that has an additional visibility property
	 */
	public static class CustomButton extends CButton implements CVisibleAction{
		private ActionVisibility visibility;
		
		public CustomButton(){
			visibility = new ActionVisibility( this );
		}
		
		@Override
		public CAction asAction(){
			return this;
		}
		
		@Override
		public void setVisible( boolean visible ){
			visibility.setVisible( visible );
		}

		@Override
		public boolean isVisible(){
			return visibility.isVisible();
		}

		@Override
		public void addVisibilityListener( ActionVisibilityListener listener ){
			visibility.addVisibilityListener( listener );
		}

		@Override
		public void removeVisibilityListener( ActionVisibilityListener listener ){
			visibility.removeVisibilityListener( listener );
		}
	}
}

Hi Beni,

Honestly, I don’t know how to thank you… :o

Not only your „example“ works, it is also a COOL application! :slight_smile:

BIG THANKS!

I tried to read and understand your implementation as much as possible, and basically I didn’t face any problems, I only have a concern though.

In my application, I have some constraints which force me to have two different sequences in handling the dockables and their actions.

The first sequence is the same as the one you have in your „example“, meaning:

  1. I create a dockable //TestDockable dockable = new TestDockable();
  2. I register the dockable in the control //control.addDockable( dockable );
  3. I add actions to the dockable //insertAction( index, button );

The second sequence which I am worried about is the following:

  1. I create a dockable
  2. I add actions to the dockabe
  3. I register the dockable in the control

Both cases are „working“, however they have a different code flow with respect to when the „VisibilityListener“ is added. I debugged a bit, and I understood the following, please correct me if I’m wrong.

In the first sequence (which is similar to your „example“), the dockable is already registered in the control on which we installed our custom ActionOffer by doing:

control.getController().addActionOffer( new VisibilityActionOffer() );

and hence whenever an action is added to the dockable, „fireAdded“ will invoke the „actionsAdded“ method on all registered DockActionSourceListeners including our custom Listener implementing ActionVisibilityListener. This is eventually resulting in adding a „VisibilityListener“ on the added action, at the time the action is added.

Whereas in the second sequence, since the dockable is not registered in the control (on which we installed our custom ActionOffer), when adding actions to the dockable, „fireAdded“ will not find any ActionVisibilityListener yet. Hence no „VisibilityListener“ will be added to the added action yet. That will happen, but at some later point.

So my concern is just to make sure whether the second sequence is safe to have, even if it results in a different code flow.

Hope I was able to explain myself clearly enough! :frowning:

If not, here’s a more concrete way of putting it:

Scenario one: I run your example as is and I observe what will initiate the call of the „addVisibilityListener“ method inside CustomButton. I can see that what initiates it is the call: insertAction( index, button ); whenever I add a new button at runtime.

Scenario two: I run the same example with the modified main method below. Logically, the line: dockable.insertAction( 0, hurriedButton ); will not add any visibility listener since the dockable is not registered in the control yet. I can see that what will initiate the call of the „addVisibilityListener“ method inside CustomButton is instead the call: dockable.setVisible( true );

All I want to know is: is that safe and OK?

Thanks a lot for your time,
Shant

public static void main( String[] args ) {
               JFrame frame = new JFrame("Test");
               frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
               frame.setBounds( 20, 20, 400, 400 );

               CControl control = new CControl( frame );

               // This is an important line, here we install our custom ActionOffer
               control.getController().addActionOffer( new VisibilityActionOffer() );

               frame.add( control.getContentArea() );

               TestDockable dockable = new TestDockable();
               
               //Add an action to the dockable before registering it in the control/////////////////////
               Color color = new Color( (int)(0xFFFFFF * Math.random()));
               CustomButton hurriedButton = new CustomButton();
               hurriedButton.setIcon( new ActionIcon( color ) );
               hurriedButton.setText( color.getRed() + " " + color.getGreen() + " " + color.getBlue() );
               hurriedButton.setVisible(true);
               dockable.insertAction( 0, hurriedButton );
               /////////////////////////////////////////////////////////////////////////////////////////
               
               control.addDockable( dockable );
               dockable.setVisible( true );
               
               frame.setVisible( true );
       }

That is safe. The “VisibilityFilteredActionSource” only adds the listeners if it exists (which does not happen before “setVisible”) and if it really needs to know about events. It only needs to know about visibility-events if some other module has added DockActionSourceListeners to the source.

And if you have a look at methods like “getDockActionCount” or “getDockAction” you will notice that they are implemented in a way such that they would always return the correct result even if there were no VisibilityListeners at all.

Great! Thanks Beni!

Now sorry to bother you with another related issue.

Logically, not all my actions are leaf actions (CButtons), some of them need to have some children menu items. And hence I implemented a CustomDropDownButton (similar to your CustomButton), this time extending CDropDownButton and implementing CVisibleAction. I also did the same for a CustomMenu (extending CMenu and implementing CVisibleAction).

Now the issue is: the visibility listeners are added on all first-level actions correctly (whether button or menu), but apparently not on second-level actions (or beyond), and hence the setVisible API won’t work on those actions.

To illustrate this, please check the attached image: to a DropDownButton I add a Button, or even again a Menu if the menu element has children of its own.

So is there a way to make the Visibility property of CActions work beyond the first-level actions as well?

Thanks a lot,
Shant

Both CMenu and CDropDownButton internally make use of DockActionSources, replacing that sources with our custom implementation would do the trick. Unfortunately the internal sources are not accessible, so currently the only way to implement this will be to copy a few existing classes - because otherwise we cannot modify them. I’m a bit hesitant to make them accessible, because one could break a lot code by using them the wrong way.

You can do the CMenu easily: copy the entire code of CMenu. At one point a “CommonSimpleMenuAction” is created. Instead of using “menu” as argument, use “new VisibilityFilteredActionSource(menu)” as argument. The CDropDownButton works similar, but there are more classes involved. I’ll send you some instructions and/or code how to modify the drop-down-button later.

Thank you Beni!
As usual, your tricks work like a charm! :smiley:

Looking forward to figure out the solution for CDropDownButton. :slight_smile:

Thank you,
Shant

Ok, CDropDownButton is a bit more tricky. You need to perform these steps:

  1. Copy the class I posted below.

  2. Copy “CDropDownButton”, instead of creating a “CommonSimpleDropDownAction” you need to create a “CustomCommonSimpleDropDownAction”. Make sure to update the generic parameter as well.

That should be all. I have some pressing things to do, hence I did not test these changes. Please let me know if they work (or not).

	/** the menu */
	private DefaultDockActionSource actions;
	private VisibilityFilteredActionSource filtered;
	private CAction action;
	
	public CustomCommonSimpleDropDownAction( CAction action ){
		this.action = action;
		actions = new DefaultDockActionSource();
		filtered = new VisibilityFilteredActionSource( actions );
	}
	
	@Override
	public CAction getAction(){
		return action;
	}
	
	public void add( DockAction action ){
		actions.add( action );
	}
	
	public void insert( int index, DockAction action ){
		actions.add( index, action );
	}
	
	public void insert( int index, DockAction... action ){
		actions.add( index, action );
	}
	
	public void remove( int index ){
		DockAction action = actions.getDockAction( index );
		actions.remove( index );
		
		if( getSelection() == action ){
			setSelection( (StandardDockAction)null );
		}
	}
	
	public int size(){
		return actions.getDockActionCount();
	}
	
	public void remove( DockAction action ){
		actions.remove( action );
		
		if( getSelection() == action )
			setSelection( (Dockable)null, (StandardDockAction)null );
	}
	
	protected DockActionSource getSubActions(){
		return filtered;
	}
}

Hi Beni,

Well yes and no :S

I have a specific case where the visibility property on CActions inside a CDropDownButton didn’t work.

This case satisifes the following 3 conditions:

(Please check the attached image for an illustration: the CAction in question is the red action.)

  1. The CAction in question (the one I want to toggle the visibility of) is not contained directly in the CDropDownButton, but inside a CMenu contained inside the CDropDownButton.

  2. I toggle the visibility at runtime.

  3. The first level container is a CDropDownButton, meaning:
    Dockable > CDropDownButton > …

I noticed that the combination of the three conditions above is required to make it NOT work because:

  1. If conditions (2) and (3) are satisified, but the CAction is a first-level child of the CDropDownButton (i.e. directly contained in the CDropDownButton), it works. so I cannot say that the “visibility fix” for CDropDownButton doesn’t work at all. Neither I can say that toggling visibility at runtime doesn’t work.

  2. If conditions (1) and (3) are satisfied, but we set the visibility statically (say to false), it works! So I cannot say that the “visibility fix” works for first-level children only.

  3. If conditions (1) and (2) are satisfied, but the first level container is a CMenu, meaning:
    Dockable > CMenu > …
    it also works! (Check the attached image)
    So we cannot say that the problem is due to toggling the visibility of a second-level child, at runtime.

Please note that when I say CMenu or CDropDownMenu I’m obviously referring to the modified versions of these classes with added visibility support (as you posted).

Also note that, as you can see in the attached image, toggling the visibility of the CAction in question didn’t work for the “iconified” actions view, but worked for the “right-click \ pop-up” actions view.

P.S. For some reason, I couldn’t attach my PNG image (although < 195KB), that’s why I attached it as .ZIP

Thank you Beni,
Shant

If I remember correct, actions can only disappear/appear while the menu is closed. But I’ll double check this anyway.

Hi Beni,

OK I agree. But that wasn’t the issue. I wasn’t toggling the visibility of the ‘red’ action while its parent menu is open. :S

Tell me if you want me to explain the issue again.

Thanks,
Shant

Sorry for the long wait, the forum has some troubles lately. I could not post anything yesterday.

It’s a bug in the framework. Will be corrected in 1.1.1p6b (I’ll upload that version this weekend). Afterwards your code should work without any issues.

Perfect! Glad to hear that. :slight_smile:

Thank you,
Shant

[url=]. . .