Im folgenden soll die Verwendung von Command Actions noch einmal anhand eines Beispiels verdeutlicht werden. Die folgenden Abbildung zeigen vorab fertige Ergebnis:
In dem Textfeld wurde der Text „Hello World“ durch den Benutzer eingegeben. Dadurch ist der Save Button neben dem Text Feld sowie in der Toolbar aktiviert. Zudem befindet sich die Action auch noch im File Menu (nicht auf dem Screenshot sichtbar).
Nachdem die Save Action ausgelöst wurde, ist das folgende zu sehen:
Ein Dialog erscheint, dass die Aktion ausgeführt wurde. Wird dieser geschlossen und öffnet man das File Menu, sieht man das folgende:
Der Tooltip zu der Save Action im File Menü zeigt den Grund für
die Deaktivierung im Tooltip. Die Save Action soll genau dann
enabled
sein, wenn der Text im Textfeld sich
seit dem letzten Speichern geändert hat.
Der folgende Code zeigt die Implementierung (das vollständige Snipped inklusive Imports befindet sich hier):
1 public final class CommandActionSnipped implements IApplication { 2 3 @Override 4 public void start(final IApplicationLifecycle lifecycle) { 5 6 //create a root frame 7 final IFrameBluePrint frameBp = BPF.frame(); 8 frameBp.setSize(new Dimension(400, 300)).setTitle("Command actions"); 9 final IFrame frame = Toolkit.createRootFrame(frameBp, lifecycle); 10 11 //Create the menu bar 12 final IMenuBarModel menuBar = frame.getMenuBarModel(); 13 14 //Use a border layout 15 frame.setLayout(BorderLayout.builder().gap(0).build()); 16 17 //add a toolbar to the top 18 final IToolBarModel toolBar = frame.add(BPF.toolBar(), BorderLayout.TOP).getModel(); 19 20 //add a composite to the center 21 final IComposite composite = frame.add(BPF.composite().setBorder(), BorderLayout.CENTER); 22 composite.setLayout(new MigLayoutDescriptor("[grow][]", "[]")); 23 24 //add a input field and save button to the composite 25 final IInputField<String> inputField = composite.add(BPF.inputFieldString(), "growx"); 26 final IButton saveButton = composite.add(BPF.button()); 27 28 //create save action 29 final IAction saveAction = createSaveAction(inputField); 30 31 //create a menu and add save action 32 final MenuModel menu = new MenuModel("File"); 33 menu.addAction(saveAction); 34 35 //add the menu to the menu bar 36 menuBar.addMenu(menu); 37 38 //add the action to the toolbar 39 toolBar.addAction(saveAction); 40 41 //bind the action to the save button 42 saveButton.setAction(saveAction); 43 44 //set the root frame visible 45 frame.setVisible(true); 46 } 47 48 private static IAction createSaveAction(final IInputComponent<String> inputComponent) { 49 final IActionBuilder builder = Action.builder(); 50 builder.setText("Save"); 51 builder.setToolTipText("Saves the text"); 52 builder.setAccelerator(VirtualKey.S, Modifier.CTRL); 53 builder.setIcon(IconsSmall.DISK); 54 55 //save command implements ICommandExecutor and IEnabledChecker, 56 //so set them both 57 final SaveCommand saveCommand = new SaveCommand(inputComponent); 58 builder.setCommand(saveCommand, saveCommand); 59 60 return builder.build(); 61 } 62 63 private static final class SaveCommand 64 extends AbstractEnabledChecker implements ICommandExecutor, IEnabledChecker { 65 66 private final IInputComponent<?> inputComponent; 67 68 private SaveCommand(final IInputComponent<?> inputComponent) { 69 this.inputComponent = inputComponent; 70 71 inputComponent.addInputListener(new IInputListener() { 72 @Override 73 public void inputChanged() { 74 fireEnabledStateChanged(); 75 } 76 }); 77 } 78 79 @Override 80 public void execute(final IExecutionContext executionContext) throws Exception { 81 inputComponent.resetModificationState(); 82 fireEnabledStateChanged(); 83 final String message = "'" + inputComponent.getValue() + "' saved!"; 84 MessagePane.showInfo(executionContext, message); 85 } 86 87 @Override 88 public IEnabledState getEnabledState() { 89 if (!inputComponent.hasModifications()) { 90 return EnabledState.disabled("No changes to save"); 91 } 92 else { 93 return EnabledState.ENABLED; 94 } 95 } 96 97 } 98 99 }
Die Klasse SaveCommand
implementiert sowohl
die Schnittstelle ICommandExecutor
als auch
IEnabledChecker
. Dies ist im Beispiel
einfacher, da sich nach dem Zurücksetzen des Modification State
in Zeile 81 auch der EnabledState ändert. In Zeile 82 werden
daher die registrierten Listener darüber informiert. Es ist bei
diesem Vorgehen zu beachten, dass in Zeile 58 die
saveCommand
Instanz doppelt angegeben wird,
einmal als ICommandExecutor
und einmal als
IEnabledChecker
.
Beide Schnittstellen in einer Klasse zu implementieren ist nicht
ungewöhnlich. In manchem Fällen lassen sich aber auch
IEnabledChecker
für unterschiedliche Actions
wiederverwenden, was eine Trennung der Implementierung nahelegt.