package bluej.utility.javafx.dialog;
import javafx.animation.RotateTransition;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;
import javafx.util.Duration;
import bluej.utility.javafx.FXPlatformRunnable;
import bluej.utility.javafx.JavaFXUtil;
import threadchecker.OnThread;
import threadchecker.Tag;
| A custom DialogPane that runs a "jiggle" animation on a given
|* error label whenever the user mouses over the disabled OK button,
* or clicks an enabled OK button with a non-empty error label.
*/
@OnThread(value = Tag.FXPlatform, ignoreParent = true)
|
|public class DialogPaneAnimateError extends DialogPane{
|
|/** An extra action to run when the OK button is mouse overed
private final FXPlatformRunnable extraMouseEnter;
| The jiggle animation. Null when not running
|
private RotateTransition animation = null;
| The error label to animate
|
private Node errorLabel;
| Whether the error label is empty
|
private SimpleBooleanProperty errorLabelEmpty = new SimpleBooleanProperty(true);
| The actual OK button
|
private Button okButton;
| Constructs a new Dialog Pane. Note that the constructor does not actually
| set the buttons in the dialog (you should do this yourself, meaning you can
| choose OK-Cancel or OK by itself) or add stylesheets or anything else.
|
| @param errorLabel The error label to animate
| @param extraMouseEnter An extra action to run when the OK button is mouse overed.
| This may well affect the disabled state of the OK button;
| we only decide whether to run the animation after this action has run.
|
public DialogPaneAnimateError(Label errorLabel, FXPlatformRunnable extraMouseEnter)
{
this.errorLabel = errorLabel;
this.errorLabelEmpty.bind(errorLabel.textProperty().isEmpty());
this.extraMouseEnter = extraMouseEnter;
}
@Override
protected Node createButton(ButtonType buttonType)
{
Node normal = super.createButton(buttonType);
if (buttonType == ButtonType.OK)
{
okButton = (Button)normal;
AnchorPane okWrapper = new AnchorPane(normal);
AnchorPane.setTopAnchor(okButton, 0.0);
AnchorPane.setBottomAnchor(okButton, 0.0);
AnchorPane.setLeftAnchor(okButton, 0.0);
AnchorPane.setRightAnchor(okButton, 0.0);
ButtonBar.setButtonData(okWrapper, buttonType.getButtonData());
ButtonBar.setButtonUniformSize(okWrapper, true);
okWrapper.setOnMouseEntered(e -> {
extraMouseEnter.run();
if (okButton.isDisable())
{
animate();
}
});
okButton.addEventFilter(ActionEvent.ACTION, event -> {
JavaFXUtil.runAfterCurrent(() -> {
if (!errorLabelEmpty.get())
animate();
});
});
return okWrapper;
}
return normal;
}
private void animate()
{
if (animation == null)
{
animation = new RotateTransition(Duration.millis(70), errorLabel);
animation.setByAngle(5);
animation.setAutoReverse(true);
animation.setCycleCount(4);
animation.setOnFinished(ev -> {
animation = null;
});
animation.play();
}
}
| Returns the reference to the actual OK button (e.g. for disabling it).
| This is different than lookupButton(ButtonType.OK) which will only
| return the wrapper for the button, not the button itself.
|
public Button getOKButton()
{
return okButton;
}
}
. DialogPaneAnimateError
. createButton
. animate
. getOKButton
131 neLoCode
+ 17 LoComm