All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.command.package-info Maven / Gradle / Ivy

/*
 * Copyright (c) 2022, JFXcore. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  JFXcore designates this
 * particular file as subject to the "Classpath" exception as provided
 * in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/**
 * Provides the set of classes for the Commanding API.
 *
 * 

Contents

*
    *
  1. 1. Overview *
  2. 2. Creating and using commands *
      *
    1. 2.1. Event bindings *
    2. 2.1. How commands affect the disabled state of a node *
    *
  3. 3. Asynchronous commands *
      *
    1. 3.1. Cancelling a running operation *
    2. 3.2. Progress reporting for asynchronous operations *
    *
  4. 4. Command behaviors *
      *
    1. 4.1. Command-based behaviors *
    2. 4.2. Control-based behaviors *
    *
* * *

1. Overview

* Commanding enables applications to handle user input in a loosely coupled, object-oriented, and reusable way. *

* A {@link javafx.scene.command.Command} represents an operation that can be invoked in various ways, such as * by clicking a button, pressing a key, or typing a shortcut. * Decoupling the command implementation from its mode of invocation allows applications to define an operation * once, and use it in different places and with different input modalities. *

* Commands also communicate their state to the user interface. For example, if a command is not executable, * a button that invokes the command is automatically {@link javafx.scene.Node#disabledProperty() disabled} * to reflect the state of the command. * * *

2. Creating and using commands

* A command can be created by extending the {@link javafx.scene.command.Command} class. * For ease of use, JFXcore comes with several predefined command implementations. * In this example, {@link javafx.scene.command.RelayCommand} is used to create a simple command that prints * some text when invoked: *
{@code
 *     Command myCommand = new RelayCommand<>(() -> {
 *         System.out.println("Command was executed.");
 *     });
 * }
* In order to use this command, it must be bound to an event of a UI control: *
{@code
 *     var button = new Button();
 *     button.setOnAction(new ActionEventBinding(myCommand));
 * }
* Now, clicking the button will invoke the command. * * *

2.1. Event bindings

* Bindings between commands and events are represented by an {@link javafx.scene.command.EventBinding}. * Since {@code EventBinding} implements the {@link javafx.event.EventHandler} interface, it can be assigned * to event handler properties like {@link javafx.scene.Node#onKeyPressedProperty() onKeyPressed} or * {@link javafx.scene.control.Button#onActionProperty() onAction}. *

* JFXcore comes with {@code EventBinding} implementations for the most commonly used event types: *

* * * * * * *
Event typeEvent binding type
{@link javafx.event.ActionEvent}{@link javafx.scene.command.ActionEventBinding}
{@link javafx.scene.input.KeyEvent}{@link javafx.scene.command.KeyEventBinding}
{@link javafx.scene.input.MouseEvent}{@link javafx.scene.command.MouseEventBinding}
{@link javafx.scene.input.TouchEvent}{@link javafx.scene.command.TouchEventBinding}
* When {@code EventBinding} receives an event, it calls the {@link javafx.scene.command.Command#execute(Object)} * method of the bound command with the value of its {@link javafx.scene.command.EventBinding#parameterProperty() parameter} * property as an argument. The {@code parameter} property can be used to pass a value from the invoker of the * command to the command implementation. * * *

2.2. How commands affect the disabled state of a node

* When a command is bound to an event of a UI control using {@code EventBinding}, the control * is automatically {@link javafx.scene.Node#disabledProperty() disabled} when the command is * not {@link javafx.scene.command.Command#executableProperty() executable}. * When multiple commands are bound to the same control, the control will be disabled when any * of the bound commands are not executable. *

* When the command is an instance of {@link javafx.scene.command.AsyncCommand}, the control will * also be disabled while the operation is running (as indicated by {@code AsyncCommand}'s * {@link javafx.scene.command.AsyncCommand#executingProperty() executing} property), preventing * users from accidentally invoking a running command multiple times. * This behavior can be configured by setting the {@code EventBinding}'s * {@link javafx.scene.command.EventBinding#disabledWhenExecutingProperty() disabledWhenExecuting} property. * * *

3. Asynchronous commands

* Commands that encapsulate asynchronous operations can be created by extending the * {@link javafx.scene.command.AsyncCommand} class. *

* JFXcore comes with two implementations of {@code AsyncCommand}: *

    *
  • {@link javafx.scene.command.TaskCommand}, which creates and executes a {@link javafx.concurrent.Task} * when the command is invoked *
  • {@link javafx.scene.command.ServiceCommand}, which wraps a {@link javafx.concurrent.Service} instance *
* In this example, {@code TaskCommand} is used to create a command that wraps a long-running asynchronous operation: *
{@code
 *     AsyncCommand myAsyncCommand = new TaskCommand<>(() -> new Task() {
 *         @Override
 *         protected Void call() throws InterruptedException {
 *             System.out.println("Task started");
 *             Thread.sleep(1000);
 *             System.out.println("Task completed");
 *             return null;
 *         }
 *     });
 * }
* * *

3.1. Cancelling a running operation

* The {@link javafx.scene.command.AsyncCommand} class introduces the * {@link javafx.scene.command.AsyncCommand#cancel()} method, which can be used to cancel a running operation. *

* When {@link javafx.scene.command.EventBinding} receives an event that invokes its bound command, and *

    *
  1. if the operation is not running, {@code EventBinding} calls {@link javafx.scene.command.Command#execute(Object)} * to start the operation; *
  2. if the operation is running, {@code EventBinding} calls {@link javafx.scene.command.AsyncCommand#cancel()} * to cancel the operation. *
*

* In this example, a user interface for a cancellable operation is implemented in two different ways: *

    *
  1. Using a single button to start and stop the operation *
    {@code
     *     // Set disabledWhenExecuting to false, which keeps the button enabled when the command is running
     *     var binding = new ActionEventBinding(myAsyncCommand);
     *     binding.setDisabledWhenExecuting(false);
     *
     *     var button = new Button();
     *     button.setOnAction(binding);
     * }
    *
  2. Using separate buttons to start and stop the operation *
    {@code
     *     // Create a button that invokes the command
     *     var startButton = new Button("Start");
     *     var startBinding = new ActionEventBinding(myAsyncCommand);
     *     startButton.setOnAction(startBinding);
     *
     *     // Create a button that cancels the command, and is only enabled when the command is running
     *     var cancelButton = new Button("Cancel");
     *     var cancelBinding = new ActionEventBinding(myAsyncCommand);
     *     cancelBinding.setDisabledWhenExecuting(false);
     *     cancelButton.setOnAction(cancelBinding);
     *     cancelButton.disableProperty().bind(myAsyncCommand.executingProperty().not());
     * }
    *
* * *

3.2. Progress reporting for asynchronous operations

* It is often desirable for asynchronous operations to report their progress back to the user interface. * For example, a user interface might show a progress bar to visualize the progress of a long-running operation. *

* The {@link javafx.scene.command.ProgressiveCommand} class extends {@link javafx.scene.command.AsyncCommand} * to support this scenario by adding the {@link javafx.scene.command.ProgressiveCommand#progressProperty() progress} * property. *

* Note: {@link javafx.scene.command.TaskCommand} and {@link javafx.scene.command.ServiceCommand} implement * the {@code ProgressiveCommand} class to support progress reporting. * * *

4. Command behaviors

* Commands can communicate their state back to the user interface in various ways. By default, a control will * be {@link javafx.scene.Node#disabledProperty() disabled} when one of its bound commands is either not executable * (as indicated by the {@link javafx.scene.command.Command#executableProperty() executable} property) or * currently executing (as indicated by the {@link javafx.scene.command.AsyncCommand#executingProperty() executing} * property). Behaviors such as these are called command behaviors. *

* Custom command behaviors can be implemented in two different ways, depending on the purpose of the behavior: *

    *
  • Command-based behaviors are implemented by overriding the command's * {@link javafx.scene.command.Command#onAttached(javafx.scene.Node)} and * {@link javafx.scene.command.Command#onDetached(javafx.scene.Node)} methods. * Since the behavior is a part of the command itself, it generally applies to * all controls to which the command is bound. *
  • Control-based behaviors are implemented as a {@link javafx.scene.command.CommandHandler} * that is attached to a specific {@link javafx.scene.control.Control}. * Since a control-based behavior only applies to a specific control, it allows * more fine-grained customization when compared to a command-based behavior. *
* While it is legal to have multiple behaviors affect a single control, care must be taken to ensure that * the different implementations do not interfere with each other. For example, when a command-based behavior * sets the control's {@link javafx.scene.control.Labeled#textProperty()}, no {@code CommandHandler} should * be added that also sets the same property. * * *

4.1. Command-based behavior

* A command-based behavior is implemented by overriding the * {@link javafx.scene.command.Command#onAttached(javafx.scene.Node)} and * {@link javafx.scene.command.Command#onDetached(javafx.scene.Node)} methods. *

* In this example, the {@code MyLabeledCommand} class implements a behavior that binds the * {@link javafx.scene.control.Labeled#textProperty() text} and * {@link javafx.scene.control.Labeled#textProperty() graphic} properties of a {@link javafx.scene.control.Labeled} * control to corresponding properties of the command. * *

{@code
 *     public class MyLabeledCommand extends RelayCommand {
 *         private final StringProperty text = new SimpleStringProperty(this, "text");
 *         public final StringProperty textProperty() { return text; }
 *
 *         private final StringProperty iconUrl = new SimpleStringProperty(this, "iconUrl");
 *         public final StringProperty iconUrlProperty() { return iconUrl; }
 *
 *         public MyLabeledCommand(Runnable execute) {
 *             super(execute);
 *         }
 *
 *         @Override
 *         protected void onAttached(Node node) {
 *             if (node instanceof Labeled labeled) {
 *                 labeled.textProperty().bind(text);
 *                 labeled.graphicProperty().bind(
 *                     Bindings.createObjectBinding(
 *                         () -> {
 *                             if (iconUrl.get() == null) return null;
 *                             return new ImageView(iconUrl.get());
 *                         },
 *                         iconUrl));
 *             }
 *         }
 *
 *         @Override
 *         protected void onDetached(Node node) {
 *             if (node instanceof Labeled labeled) {
 *                 labeled.textProperty().unbind();
 *                 labeled.graphicProperty().unbind();
 *             }
 *         }
 *     }
 * }
* * *

4.2. Control-based behavior

* A control-based behavior is created by implementing the {@link javafx.scene.command.CommandHandler} interface. * In this example, the {@code MyActivityBehavior} class adds an activity indicator graphic to a button, * visualizing a running {@link javafx.scene.command.AsyncCommand}. *
{@code
 *     public class MyActivityBehavior implements CommandHandler {
 *         @Override
 *         public void onAttached(Node node, Command command) {
 *             if (node instanceof Labeled l && command instanceof AsyncCommand c) {
 *                 l.graphicProperty().bind(
 *                     Bindings.createObjectBinding(
 *                         () -> {
 *                             if (c.isExecuting())
 *                                 return new ImageView("activityIndicator.gif");
 *                             return null;
 *                         },
 *                         c.executingProperty()));
 *             }
 *         }
 *
 *         @Override
 *         public void onDetached(Node node, Command command) {
 *             if (node instanceof Labeled l) {
 *                 l.graphicProperty().unbind();
 *             }
 *         }
 *     }
 * }
* * The previously created behavior can then be added to a control by setting the control's * {@link javafx.scene.control.Control#commandHandlerProperty() commandHandler} property: *
{@code
 *     // Using the control-based behavior with a button:
 *     var button = new Button();
 *     button.setCommandHandler(new MyActivityBehavior());
 * }
* * Similarly, the same effect can also be achieved by subclassing a control and adding the behavior in its * constructor. This approach is useful when the behavior implementation needs to access non-public methods * or fields of the control. *

* Note that in this example, the {@link javafx.scene.Node#addCommandHandler(javafx.scene.command.CommandHandler)} * API is used instead of setting the control's {@link javafx.scene.control.Control#commandHandlerProperty() commandHandler} * property. This keeps the behavior an implementation detail of the control, and doesn't leak it to the * outside world. *

{@code
 *     public class MyActivityButton extends Button {
 *         public MyActivityButton() {
 *             addCommandHandler(new MyActivityBehavior());
 *         }
 *     }
 * }
* * As another option, the behavior can be added to a custom {@link javafx.scene.control.Skin Skin}. * This approach has the advantage of being able to use CSS to apply behavioral skins to controls. * As before, note the use of the {@link javafx.scene.Node#addCommandHandler(javafx.scene.command.CommandHandler)} * and {@link javafx.scene.Node#removeCommandHandler(javafx.scene.command.CommandHandler)} APIs: *
{@code
 *     public class MyActivitySkin extends ButtonSkin {
 *         private final CommandHandler behavior = new MyActivityBehavior();
 *
 *         public MyActivitySkin(Button control) {
 *             super(control);
 *             control.addCommandHandler(behavior);
 *         }
 *
 *         @Override
 *         public void dispose() {
 *             getSkinnable().removeCommandHandler(behavior);
 *             super.dispose();
 *         }
 *     }
 * }
* */ package javafx.scene.command;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy