/**
* Takes the provided {@link XAction} and returns a {@link MenuItem}
* instance with all relevant properties bound to the properties of the
* Action.
* <p>
* NOTE: This is a revision of the method in ControlsFX ActionUtils, as
* we need to extend coverage for RadioMenuItem needs.
*
* @param action
* The {@link XAction} that the {@link MenuItem} should bind
* to.
* @return A {@link MenuItem} that is bound to the state of the provided
* {@link Action}
*/
public static MenuItem createMenuItem( final XAction action ) {
// NOTE: This is messy logic because we cannot yet use annotation alone
// in order to distinguish the required type of Menu Item to return.
// Preferably we would use a switch statement for the Action Verb cases.
final MenuItem menuItem = ( action.getClass().isAnnotationPresent( ActionCheck.class )
|| action.isCheck() || action.isToggle() )
? new CheckMenuItem()
: action.isChoice() ? new RadioMenuItem() : new MenuItem();
return configure( menuItem, action );
}
// NOTE: This is a revision of the method in ControlsFX ActionUtils, to
// extend coverage for RadioMenuItem needs.
private static < T extends MenuItem > T configure( final T menuItem, final Action action ) {
if ( action == null ) {
throw new NullPointerException( "Action cannot be null" ); //$NON-NLS-1$
}
// Button bind to action properties.
bindStyle( menuItem, action );
menuItem.textProperty().bind( action.textProperty() );
menuItem.disableProperty().bind( action.disabledProperty() );
menuItem.acceleratorProperty().bind( action.acceleratorProperty() );
// NOTE: This is the only setting unique to XAction and XActionGroup,
// but we can't make a nested call to ActionUtils.configure() as it is
// Private API, so we copy/paste and extend here instead.
if ( ( action instanceof XAction ) && ( ( XAction ) action ).isHideIfDisabled() ) {
menuItem.visibleProperty().bind( action.disabledProperty().not() );
}
else if ( ( action instanceof XActionGroup )
&& ( ( XActionGroup ) action ).isHideIfDisabled() ) {
menuItem.visibleProperty().bind( action.disabledProperty().not() );
}
menuItem.graphicProperty().bind( new ObjectBinding< Node >() {
{
bind( action.graphicProperty() );
}
@Override
protected Node computeValue() {
return copyNode( action.graphicProperty().get() );
}
@Override
public void removeListener( final InvalidationListener listener ) {
super.removeListener( listener );
unbind( action.graphicProperty() );
}
} );
// Add all the properties of the action into the button, and set up
// a listener so they are always copied across.
menuItem.getProperties().putAll( action.getProperties() );
action.getProperties()
.addListener( new MenuItemPropertiesMapChangeListener<>( menuItem, action ) );
// Handle the selected state of the menu item if it is a
// CheckMenuItem or RadioMenuItem.
if ( menuItem instanceof RadioMenuItem ) {
( ( RadioMenuItem ) menuItem ).selectedProperty()
.bindBidirectional( action.selectedProperty() );
}
else if ( menuItem instanceof CheckMenuItem ) {
( ( CheckMenuItem ) menuItem ).selectedProperty()
.bindBidirectional( action.selectedProperty() );
}
// Just call the execute method on the action itself when the action
// event occurs on the button.
menuItem.setOnAction( action );
return menuItem;
}