JavaFX - move window with effect - javafx-8

I have undecorated non-fullscreen window, which I like to move outside screen boundaries when mouse leaves it's area, but do so smoothly. I found some JavaFX functionality to do so - Timeline, but KeyValue for that Timeline doesn't supports stage.xProperty - because this property is readonlyProperty. Is there way to move my window smoothly using JavaFX functions?

You can setup proxy properties that you manipulate via KeyValues in a Timeline. A listener on the proxy can modify the actual stage location.
import javafx.animation.*;
import javafx.application.*;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.*;
import javafx.util.Duration;
public class StageSwiper extends Application {
private static final int W = 350;
private static final Duration DURATION = Duration.seconds(0.5);
#Override
public void start(Stage stage) throws Exception {
Label instructions = new Label(
"Window will slide off-screen when the mouse exits it.\n" +
"Click the window to close the application."
);
instructions.setTextAlignment(TextAlignment.CENTER);
final StackPane root = new StackPane(instructions);
root.setStyle("-fx-background-color: null;");
DoubleProperty stageX = new SimpleDoubleProperty();
stageX.addListener((observable, oldValue, newValue) -> {
if (newValue != null && newValue.doubleValue() != Double.NaN) {
stage.setX(newValue.doubleValue());
}
});
final Timeline slideLeft = new Timeline(
new KeyFrame(
DURATION,
new KeyValue(
stageX,
-W,
Interpolator.EASE_BOTH
)
),
new KeyFrame(
DURATION.multiply(2)
)
);
slideLeft.setOnFinished(event -> {
slideLeft.jumpTo(Duration.ZERO);
stage.centerOnScreen();
stageX.setValue(stage.getX());
});
root.setOnMouseClicked(event -> Platform.exit());
root.setOnMouseExited(event -> slideLeft.play());
stage.setScene(new Scene(root, W, 100, Color.BURLYWOOD));
stage.initStyle(StageStyle.UNDECORATED);
stage.show();
stage.centerOnScreen();
stageX.set(stage.getX());
}
public static void main(String[] args) {
launch(args);
}
}

Related

Javafx Stage is blank for a few seconds before showing elements

I am trying to create a game with JavaFX. I have created 2 stages, one is for the splashscreen, the second one is for the actual game itself. This is my first ever JavaFX program, i am a student learning JavaFX. The problem i am having is that when i hide the first stage and show the second one, the second stage stays blank for a few seconds and then continues to show all element(s). For now, there is only a gif showing in the stage with some music(which is not delayed in loading, because it is an instance variable). I don't want to create many instance variables for each of the elements i wish to put into the stage. Below is the code, where only the necessary code is shown to understand the problem:
import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.File;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
Dimension window = Toolkit.getDefaultToolkit().getScreenSize();
private double screenWidth = window.getWidth();
private double screenHeight = window.getHeight();
private boolean fullscreen = false;
private boolean music = false;
private double value = 0;
private Group root, root2;
private Image start;
private Image startHover;
private ImageView startViewer;
private Stage stage;
private boolean firstWindowExists = true;
private CheckBox full;
private CheckBox sound;
private Slider slider;
private Image BG;
private ImageView BGView;
private String path = "bin/Audio/8_bit_march.mp3";
private Media media;
private MediaPlayer player;
public static void main(String[] args) {
launch();
}
private void run() {
Image title = new Image("Title.png", 300, 0, true, true);
ImageView titleView= new ImageView();
titleView.setX(200);
titleView.setY(10);
titleView.setImage(title);
add(titleView);
start = new Image("Start1.png", 200, 0, true, true);
startHover = new Image("Start_Hover1.png", 200, 0, true, true);
startViewer= new ImageView();
startViewer.setX(250);
startViewer.setY(300);
startViewer.setOnMouseEntered(mouseEnter);
startViewer.setOnMouseExited(mouseExit);
startViewer.setOnMouseReleased(mouseReleased);
startViewer.setImage(start);
add(startViewer);
if (!firstWindowExists) {//this code works
Image title2 = new Image("BG.gif", (screenWidth/1.25) + 4, (screenHeight/1.25)+4, false, true);
ImageView titleView2= new ImageView();
titleView2.setX(-2);
titleView2.setY(-2);
titleView2.setImage(title2);
add2(titleView2);
}
}
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
public void handle(ActionEvent e)
{
if (sound.isSelected()) { // the checkbox is checked to be enabled
slider.setVisible(true);
}
else {
slider.setVisible(false);
}
}
};
EventHandler<MouseEvent> mouseEnter = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) { // irrelevant for this problem
startViewer.setImage(startHover);
}
};
EventHandler<MouseEvent> mouseExit = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
startViewer.setImage(start);
}
};
EventHandler<MouseEvent> mouseReleased = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
stage.hide(); //hide first window
firstWindowExists = false;
Stage stage2 = new Stage(); // creating a new stage
root2 = new Group(); //creating new group
Scene scene2 = new Scene(root2, Color.WHITE);// creating a scene and adding the newly created Group
Canvas canvas2 = new Canvas(screenWidth/1.25, screenHeight/1.25); // creating a canvas for the screen
root2.getChildren().add(canvas2); // adding canvas to the group (window)
stage2.setTitle("Game"); // setting the title of the window
stage2.setScene(scene2); // Adds scene to the stage
stage2.setFullScreen(fullscreen);
stage2.show(); //after showing the screen here, it stays blank and then adds the title2 gif
stage2.centerOnScreen();
if (music) { //plays music
player.play();
player.setVolume(value);
player.setCycleCount(MediaPlayer.INDEFINITE);
}
run(); // runs the method with heavy lifting stuff
}
};
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
}));
private void add(Node node) {
if (firstWindowExists) {
root.getChildren().add(node);
}
}
private void add2(Node node) {
root2.getChildren().add(node);
}
#Override
public void start(Stage stage) throws Exception { // main method which creates the first window
this.stage = stage;
stage.setTitle("Test GUI");
root = new Group();
Scene scene = new Scene(root, Color.DARKGRAY);
Canvas canvas = new Canvas(700, 350);
root.getChildren().add(canvas);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
stage.centerOnScreen();
root.requestFocus();
run();
media = new Media(new File(path).toURI().toString());
player = new MediaPlayer(media);
}
}

JavaFX Custom Table Cell - Strange behavior

I have this code:
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Example extends Application
{
#Override
public void start(Stage stage) throws Exception
{
TableView<Color> table = new TableView<>();
ObservableList<Color> colors = FXCollections.observableArrayList();
table.setItems(colors);
table.setEditable(true);
TableColumn<Color, Color> column = new TableColumn<>();
column.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue()));
column.setCellFactory(param ->
{
ObservableList<Color> menuColors = FXCollections.observableArrayList();
menuColors.addAll(Color.RED, Color.GREEN, Color.BLUE);
return new ComboBoxTableCell(menuColors);
});
Button button = new Button("Add row");
button.setOnAction(event -> colors.add(Color.BLACK));
VBox box = new VBox(table, button);
table.getColumns().add(column);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
stage.setScene(new Scene(box));
stage.show();
}
public class ComboBoxTableCell extends TableCell<Color, Color>
{
private ComboBox<Color> comboBox;
public ComboBoxTableCell(ObservableList<Color> colors)
{
comboBox = createFancyComboBox(colors);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
public void updateItem(Color item, boolean empty)
{
if (item == null || empty)
setGraphic(null);
else
setGraphic(comboBox);
}
}
private ComboBox<Color> createFancyComboBox(ObservableList<Color> colors)
{
ComboBox<Color> comboBox = new ComboBox<>(colors);
Callback<ListView<Color>, ListCell<Color>> factory = list -> new ColorSquare();
comboBox.setCellFactory(factory);
comboBox.setButtonCell(factory.call(null));
comboBox.setValue(colors.get(0));
return comboBox;
}
public static class ColorSquare extends ListCell<Color>
{
#Override
public void updateItem(Color item, boolean empty)
{
super.updateItem(item, empty);
Rectangle rect = new Rectangle(18, 18);
if (item != null)
{
rect.setFill(item);
setGraphic(rect);
}
}
}
public static void main(String[] args)
{
launch(args);
}
}
If I try to run it, click on the button, change the color to for Green and click 8 times on the button, the green square will disapper.
How do I fix this and why is it happening? The real code isn't much different from this, this is the only problem I have. Thank you.
I have noticed that at times the rerendering of a table cell doesn't work right after a certain number of attempts. I had one where I was updating a remaining amount when the user entered a value in another cell.
To solve this I would toggle visibility of the column.
column.setVisible(false);
column.setVisible(true);
I would do this immediately after the action that changed the value in the cell.
It's a hack but seems to work.

EventFilter consume() does not prevent SpaceChars in TextField

I have a JavaFX GUI where I wish to intercept the pressing of the SpaceBar and use it to call a method. I wrote an EventFilter that seems to do the trick. It includes the command event.consume() which I believe is supposed to keep the KeyEvent from propagating to the various controls.
My issue is that when I added a TextField, and this field has the focus, the Spacebar presses are not being consumed as I thought they would. The " " are captured by the TextField. I would like to intercept and prevent the " " from being added to the TextField.
What am I leaving out in the code below in order to keep " " from reaching the TextField? The api, if I am reading it correctly, says that filters registered with a parent control can intercept an event before it reaches the children nodes. But even when putting the filter directly on the TextField, I am still having " " chars appear in the TextField.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
public class SpaceIntercept extends Application implements EventHandler <KeyEvent>
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage primaryStage)
{
TextField textField = new TextField("asdf");
Group root = new Group();
Scene scene = new Scene(root, 200, 100);
scene.addEventFilter(KeyEvent.ANY, event -> handle(event));
// root.addEventFilter(KeyEvent.ANY, event -> handle(event));
// textField.addEventFilter(KeyEvent.ANY, event -> handle(event));
root.getChildren().add(textField);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.SPACE)
{
if (event.getEventType() == KeyEvent.KEY_PRESSED)
{
System.out.println("Code that responds to SpaceBar");
}
event.consume();
}
}
}
The text field is probably listening for KEY_TYPED events. As is well-documented, getCode() returns KeyCode.UNDEFINED for a KEY_TYPED event. Thus you do not catch this case.
You can check for the character variable as well as the code variable to handle all cases:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
public class SpaceIntercept extends Application implements EventHandler <KeyEvent>
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage primaryStage)
{
TextField textField = new TextField("asdf");
Group root = new Group();
Scene scene = new Scene(root, 200, 100);
scene.addEventFilter(KeyEvent.ANY, event -> handle(event));
// root.addEventFilter(KeyEvent.ANY, event -> handle(event));
// textField.addEventFilter(KeyEvent.ANY, event -> handle(event));
root.getChildren().add(textField);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.SPACE || " ".equals(event.getCharacter()))
{
if (event.getEventType() == KeyEvent.KEY_PRESSED)
{
System.out.println("Code that responds to SpaceBar");
}
event.consume();
}
}
}
A simple solution i can think,which although doesn't blocks the space from being added to the TextField,but it replaces it after it has been added almost instantly is adding a changeListener to the TextProperty of the TextField:
textField.textProperty().addListener((observable,oldValue,newValue)->{
textField.setText(textField.getText().replace(" ", ""));
});
This may also be helpfull http://fxexperience.com/2012/02/restricting-input-on-a-textfield/

JavaFX: automatically expand ChoiceBox when on focus

I have a ChoiceBox for which I want to show the dropdown menu if it has gained focus when cycling though the input controls (focus traversal). I.e. I don't want the user to press SPACE first as he must make a choice anyway. I have the following code so far:
import java.util.Arrays;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class SampleApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
GridPane pane = new GridPane();
ChoiceBox<String> box1 = new ChoiceBox<String>();
box1.getItems().addAll("1", "2", "3");
ChoiceBox<String> box2 = new ChoiceBox<String>();
box2.getItems().addAll("a", "b", "c");
for (ChoiceBox<String> choiceBox : Arrays.asList(box1, box2)) {
choiceBox.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue && !oldValue) {
// transition from unfocused to focused -> expand choicebox
if (!choiceBox.isShowing()) {
choiceBox.show();
}
}
});
}
pane.add(box1, 0, 0);
pane.add(box2, 1, 0);
root.getChildren().add(pane);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
This code works fine when I use the keyboard for focus traversal, but if I click the the (unfocused) ChoiceBox with the mouse, the dropdown shows for a few miliseconds and then hides instantly again. I guess that the ChoiceBox has a predefined EventHandler for mouse events which "toggles" whether the items are shown or not. Apparently, the FocusedProperty is changing first, showing the items, and then the MouseEvents hides them again.
How can I fix this?
Use the consume method of MouseEvents to stop further propagation of the Event through the dispatch chain.
final ChangeListener<? super Boolean> showHideBox = ( __, ___, isFocused ) ->
{
if ( isFocused.booleanValue() )
{
choiceBox.show();
}
else
{
choiceBox.hide();
}
};
choiceBox.focusedProperty().addListener( showHideBox );
choiceBox.addEventFilter( MouseEvent.MOUSE_RELEASED, release ->
{
release.consume();
choiceBox.requestFocus();
} );
Full example : https://gist.github.com/flasheater/0cc365227a235c3fb794 .

JavaFX 8 TextArea loose focus on tab

Is it possible to change the default behaviour of a JavaFX TextArea, so that pressing Tab passes the focus to the next component?
While #ItachiUchiha solution works, as he states, it depends on the layout (box in his sample).
Based on this question, you can modify the default behavior of a TextArea, regardless of the layout.
But you will need to use for this private API, which may change at any time without notice.
In this sample Tab and Shitf+Tab will have the desired behavior, while Ctrl+Tab will insert "\t" on the text area.
#Override
public void start(Stage primaryStage) {
TextArea area = new TextArea();
area.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> {
if (event.getCode() == KeyCode.TAB) {
TextAreaSkin skin = (TextAreaSkin) area.getSkin();
if (skin.getBehavior() instanceof TextAreaBehavior) {
TextAreaBehavior behavior = (TextAreaBehavior) skin.getBehavior();
if (event.isControlDown()) {
behavior.callAction("InsertTab");
} else if (event.isShiftDown()) {
behavior.callAction("TraversePrevious");
} else {
behavior.callAction("TraverseNext");
}
event.consume();
}
}
});
VBox root = new VBox(20, new Button("Button 1"), area, new Button("Button 2"));
Scene scene = new Scene(root, 400, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
Well, you definitely can do this, but it depends on the Layout to which the TextArea is added to. I have created a simple example where a TextArea and a TextField are both added to a VBox. There is a keyEventHandler which monitors the keyPress event on the TextArea and sends the focus to the next child(if any)
import java.util.Iterator;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextAreaTabFocus extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
VBox box = new VBox();
TextArea textArea = new TextArea();
TextField textField = new TextField();
box.getChildren().addAll(textArea, textField);
final EventHandler<KeyEvent> keyEventHandler =
keyEvent -> {
if (keyEvent.getCode() == KeyCode.TAB) {
Iterator<Node> itr = box.getChildren().iterator();
while(itr.hasNext()) {
if(itr.next() == keyEvent.getSource()) {
if(itr.hasNext()){
itr.next().requestFocus();
}
//If TextArea is the last child
else {
box.getChildren().get(0).requestFocus();
}
break;
}
}
keyEvent.consume();
}
};
textArea.setOnKeyPressed(keyEventHandler);
Scene scene = new Scene(box, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}