package bluej.utility.javafx;
import bluej.Config;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import threadchecker.OnThread;
import threadchecker.Tag;
import java.util.Objects;
| An FX Abstract Action to replace the Swing Action class
|
| @author Neil Brown
|
@OnThread(Tag.FXPlatform)
public abstract class FXAbstractAction
{
private String name;
private boolean hasMenuItem = false;
private final BooleanProperty unavailable = new SimpleBooleanProperty(false);
private final BooleanProperty disabled = new SimpleBooleanProperty(false);
protected final ObjectProperty<KeyCombination> accelerator;
private final Node buttonGraphic;
protected FXAbstractAction(String name)
{
this(name, (KeyCombination)null);
}
protected FXAbstractAction(String name, KeyCombination accelerator)
{
this.name = name;
this.accelerator = new SimpleObjectProperty<>(accelerator);
this.buttonGraphic = null;
}
protected FXAbstractAction(String name, Node buttonGraphic)
{
this.name = name;
this.accelerator = new SimpleObjectProperty<>(null);
this.buttonGraphic = buttonGraphic;
}
protected FXAbstractAction(String name, Image buttonImage)
{
this(name, new ImageView(buttonImage));
}
public abstract void actionPerformed(boolean viaContextMenu);
public void bindEnabled(BooleanExpression enabled)
{
if (enabled != null)
disabled.bind(enabled.not());
}
public void setEnabled(boolean enabled)
{
if (disabled.isBound())
disabled.unbind();
disabled.set(!enabled);
}
public boolean isDisabled()
{
return disabled.get();
}
| There's two ways in which actions become disabled. One is that their inherent
| state in the editor changes, e.g. no undo if you haven't made any changes.
| The other is that they become what we call unavailable, which happens when
| the documentation view is showing, for example. So their GUI enabled status
| is actually the conjunction of being enabled and available.
|
public void setAvailable(boolean available)
{
unavailable.set(!available);
}
public String getName()
{
return name;
}
public KeyCombination getAccelerator()
{
return accelerator.get();
}
public Button makeButton()
{
Button button = new Button(name);
button.disableProperty().bind(disabled.or(unavailable));
button.setOnAction(e -> actionPerformed(false));
if (buttonGraphic != null)
button.setGraphic(buttonGraphic);
return button;
}
| Note: calling this method indicates that the item will have
| a menu item with the accelerator available for use. Actions with a menu item
| don't set a separate entry in the key map. So don't call this
| unless you're actually going to have the menu item available on the menu.
|
public MenuItem makeMenuItem()
{
MenuItem menuItem = new MenuItem(name);
prepareMenuItem(menuItem);
return menuItem;
}
private void prepareMenuItem(MenuItem menuItem)
{
setMenuActionAndDisable(menuItem, false);
boolean cmdPlusMinusOnMac = Config.isMacOS() && accelerator.get() != null &&
(accelerator.get().equals(new KeyCodeCombination(KeyCode.EQUALS, KeyCombination.SHORTCUT_DOWN))
|| accelerator.get().equals(new KeyCodeCombination(KeyCode.MINUS, KeyCombination.SHORTCUT_DOWN))
|| accelerator.get().equals(new KeyCodeCombination(KeyCode.EQUALS, KeyCombination.META_DOWN))
|| accelerator.get().equals(new KeyCodeCombination(KeyCode.MINUS, KeyCombination.META_DOWN)));
if (!cmdPlusMinusOnMac)
{
menuItem.acceleratorProperty().bind(accelerator);
hasMenuItem = true;
}
}
| Makes a MenuItem which will run this action, but without an accelerator.
|
public MenuItem makeContextMenuItem()
{
MenuItem menuItem = new MenuItem(name);
setMenuActionAndDisable(menuItem, true);
return menuItem;
}
private void setMenuActionAndDisable(MenuItem menuItem, boolean contextMenu)
{
menuItem.disableProperty().bind(disabled.or(unavailable));
menuItem.setOnAction(e -> actionPerformed(true));
}
public String toString()
{
return name;
}
/*public Category getCategory()
|
|{} return category;
|
|}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FXAbstractAction that = (FXAbstractAction) o;
return name.equals(that.name);
}
@Override
public int hashCode()
{
return name.hashCode();
}
| Determines whether this action has a menu item which has been created, and which has the given
| shortcut as an accelerator (compared using .equals method). We don't check if the menu item
| is actually visible, or enabled, etc, that's up to the caller.
| @param shortcut
| @return
|
public boolean hasMenuItemWithAccelerator(KeyCombination shortcut)
{
return hasMenuItem && Objects.equals(accelerator.get(), shortcut);
}
| Changes the name of the action.
| @param name the new name to be assigned
|
public void setName(String name)
{
this.name = name;
}
}
top,
use,
map,
abstract class FXAbstractAction
. FXAbstractAction
. FXAbstractAction
. FXAbstractAction
. FXAbstractAction
. actionPerformed
. bindEnabled
. setEnabled
. isDisabled
. setAvailable
. getName
. getAccelerator
. makeButton
. makeMenuItem
. prepareMenuItem
. makeContextMenuItem
. setMenuActionAndDisable
. toString
. equals
. hashCode
. hasMenuItemWithAccelerator
. setName
218 neLoCode
+ 22 LoComm