Javafx - Cursor intersect shape - object

I have a few objects on a board and I'd like to get those objects's indexes by the coordinates.
I've tried making a MouseEvent handler and using the getBoundInParent() combined with MouseInfo.getPointerInfo().getLocation(), but unsuccessful. These methods gave me different coordinates and could not match them.
Should I make a rectangle by the cursor's coordinates and use the getBoundInParent().intersects method?
Any advices?

Solution
On each of the shapes, provide setOnMouseEntered and setOnMouseExited handlers to catch the mouse entering and exiting events and record the index of the shape which the mouse is over.
Assumption
I assume you need to intersect the cursor hotspot (e.g. the tip of the mouse pointer arrow) and not the cursor shape or the cursor's rectangular bounds (as intersection of the hotspot is the standard way that cursors work).
Sample Application Output
When you mouse over the circle, the circle will be highlighted and the circle's index (1) will display
When you mouse over the rectangle, the rectangle will be highlighted and the rectangles's index (2) will display.
When you don't mouse over either shape, neither shape will be highlighted and no index will display.
Sample Code
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
public class ShapeIntersector extends Application {
private static final Shape[] shapes = {
new Circle(50, Color.AQUAMARINE),
new Rectangle(100, 100, Color.PALEGREEN)
};
private static final DropShadow highlight =
new DropShadow(20, Color.GOLDENROD);
#Override
public void start(Stage stage) throws Exception {
HBox layout = new HBox(40);
layout.setPadding(new Insets(30));
layout.setAlignment(Pos.CENTER);
Label highlightedShapeLabel = new Label(" ");
highlightedShapeLabel.setStyle(
"-fx-font-family: monospace; -fx-font-size: 80px; -fx-text-fill: olive"
);
for (Shape shape: shapes) {
layout.getChildren().add(shape);
shape.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
shape.setEffect(highlight);
int idx = layout.getChildren().indexOf(shape) + 1;
highlightedShapeLabel.setText(
"" + idx
);
}
});
shape.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
shape.setEffect(null);
highlightedShapeLabel.setText(" ");
}
});
}
layout.getChildren().add(highlightedShapeLabel);
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) { launch(args); }
}

Related

JavaFX maximize the size of node children

I want to have squares added to a pane, and those squares maximized.
One square takes up the whole pane.
Two squares split the pane.
Three squares make it all in thirds.
When it overflows it goes to the next "row" and continues the process.
All the squares should be the same size.
Is there a way of using the standard layouts or which should I modify?
Thanks
Here's my take on this problem. I don't think it's a great solution, but at least it might help point out some techniques somebody might build on to get a better solution. Basically the solution overrides layoutChildren() to recalculate the preferred tile size as the number of tiles or the available space changes. I'm not sure getWidth or getHeight should really be called from layoutChildren (though it seems to work in this case).
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.Random;
public class TilePaneSample extends Application {
private static final Random random = new Random(42);
#Override
public void start(Stage stage) {
TilePane tiles = createTiles();
VBox layout = new VBox(
createControls(tiles),
tiles
);
VBox.setVgrow(tiles, Priority.ALWAYS);
stage.setScene(new Scene(layout, 400, 300));
stage.show();
}
private TilePane createTiles() {
TilePane tiles = new TilePane() {
#Override
protected void layoutChildren() {
if (getChildren().size() > 0) {
setPrefTileWidth(
Math.floor(
Math.min(
Math.max(
Tile.MIN_SIZE,
getWidth() / getChildren().size()
),
getHeight()
)
)
);
setPrefTileHeight(getPrefTileWidth());
}
super.layoutChildren();
}
};
tiles.setStyle("-fx-background-color: papayawhip;");
tiles.setPrefColumns(5);
tiles.setPrefRows(5);
tiles.getChildren().add(new Tile());
return tiles;
}
private ToolBar createControls(TilePane tiles) {
Button addTile = new Button("Add Tile");
addTile.setOnAction(action -> tiles.getChildren().add(new Tile()));
Button removeTiles = new Button("Remove Tiles");
removeTiles.setOnAction(action -> tiles.getChildren().clear());
ToolBar controls = new ToolBar(addTile, removeTiles);
controls.setMinHeight(ToolBar.USE_PREF_SIZE);
return controls;
}
private class Tile extends StackPane {
public static final int MIN_SIZE = 100;
public Tile() {
setStyle(
"-fx-background-color: " +
"rgb(" + random.nextInt(256) + ", " +
+ random.nextInt(256) + ", "
+ random.nextInt(256) + ");"
);
setMinSize(MIN_SIZE, MIN_SIZE);
}
}
public static void main(String[] args) { launch(args); }
}

Drawing a border around a JavaFX Text node

I want to draw red borders around arbitrary javafx.scene.text.Text nodes in my JavaFX scenegraph, for example those in a Button object.
It is easy to retrieve all the Text nodes but not to find where they are in the scene, they have an x and y property which doesn't seem to get set properly but they do not have a width and height.
So far I have tried to add rectangles with a red stroke to a stack pane but the x and y are always wrong and I can't get the size.
One solution is to wrap the text nodes in layout pane (such as an HBox) and use CSS on the layout pane:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TextBorderExample extends Application {
#Override
public void start(Stage primaryStage) {
final HBox root = new HBox(5);
root.getChildren().addAll(
new Text("This"), new Text("Is"), new Text("A"), createBorderedText("Red"), new Text("Bordered"), new Text("Text")
);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private Node createBorderedText(String text) {
final HBox hbox = new HBox();
hbox.getChildren().add(new Text(text));
hbox.setStyle("-fx-border-color: red;");
return hbox ;
}
public static void main(String[] args) {
launch(args);
}
}
Another way is to use a Rectangle, as follows:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TextBorderExample extends Application {
#Override
public void start(Stage primaryStage) {
final HBox root = new HBox(5);
final Text red = new Text("Red");
final Rectangle redBorder = new Rectangle(0, 0, Color.TRANSPARENT);
redBorder.setStroke(Color.RED);
redBorder.setManaged(false);
red.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> observable,
Bounds oldValue, Bounds newValue) {
redBorder.setLayoutX(red.getBoundsInParent().getMinX());
redBorder.setLayoutY(red.getBoundsInParent().getMinY());
redBorder.setWidth(red.getBoundsInParent().getWidth());
redBorder.setHeight(red.getBoundsInParent().getHeight());
}
});
root.getChildren().addAll(new Text("This"), new Text("Is"), new Text("A"), red, new Text("Bordered"), new Text("Text"));
root.getChildren().add(redBorder);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX blur effect and layoutX and layoutY

I did a try with effect of the framework, but it has some weird behaviour when I blur a textfield into a Parent, the textfield is positioned at a different place, please take a look :
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BlurTest extends Application {
CTextView subPane = new CTextView(100,100);
#Override
public void start(Stage stage) throws Exception {
VBox myBox = new VBox();
CheckBox chkBlur = new CheckBox("Show");
chkBlur.selectedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> v,
Boolean oldValue, Boolean newValue) {
if(oldValue)
subPane.getTxt().setEffect(new GaussianBlur());
else
subPane.getTxt().setEffect(null);
}
});
myBox.getChildren().addAll(new TextField("Not blur"), subPane, new TextField("Not blur"), chkBlur);
myBox.setPrefSize(250, 500);
Scene scene = new Scene(myBox);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And my custom textview :
import javafx.scene.Parent;
import javafx.scene.control.TextField;
public class CTextView extends Parent {
private TextField txt;
public CTextView(double w, double h) {
super();
this.txt = new TextField("Default");
this.txt.setLayoutX(20);
this.txt.setLayoutY(20);
this.getChildren().add(this.txt);
}
public TextField getTxt() {
return txt;
}
}
I don't understand why the textfield is repositioned in the Parent after blur effect.. :/
Thanks for your help
> Why is the textfield repositioned?
The GaussianBlur's default radius value is 10. When this effect applied to the node, that node's local bounds expands extra these blurring radii, but the node's width and height remains the same. The Parent does not apply CSS style and does not layout its children, however as seen in your example, it takes into account the local bounds and repositioned the node.
> Why do the textfield's setLayoutX and setLayoutY not worked?
The Parent does consider the local bounds of its child but it does not layout them according the child's layout values. Use a Region (or its subclasses) which takes care its children layout values.
public class CTextView extends Region {
private TextField txt;
public CTextView(double w, double h) {
super();
this.txt = new TextField("Default");
this.txt.setLayoutX(20);
this.txt.setLayoutY(20);
this.getChildren().add(this.txt);
}
public TextField getTxt() {
return txt;
}
}

JavaFX ProgressBar: how to change bar color?

I'm trying to change the color of bar in ProgressBar with
pBar.setStyle("-fx-accent: green");
but I have encountered a problem: that doesn't seem to work right for me! (Or I just don't understand something)
here is the code:
public class JavaFXApplication36 extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
ProgressBar pbRed = new ProgressBar(0.4);
ProgressBar pbGreen = new ProgressBar(0.6);
pbRed.setLayoutY(10);
pbGreen.setLayoutY(30);
pbRed.setStyle("-fx-accent: red;"); // line (1)
pbGreen.setStyle("-fx-accent: green;"); // line (2)
root.getChildren().addAll(pbRed, pbGreen);
Scene scene = new Scene(root, 150, 50);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
}
I always get 2 red progressbars with it! It seems that code in line (1) changes the style of ProgressBar class, not the instance.
Another strange moment is that deleting line (1) don't result in 2 green progress bars. So I can figure that line (2) is completely useless!! WHY?! That's definitely getting odd.
Is there any way to set separate colors for separate progressbars?
Answer updated to add a simple non-animated example with multiple progress bars
The code in your question should display two different colored progress bars, the fact that it doesn't is a bug in the JavaFX css processing system. Log the bug against the runtime project here: http://javafx-jira.kenai.com.
As a workaround, instead of calling setStyle on the progress bars, define the accent colors used to color progress bars within a stylesheet and add a style class to the progress bars. Then, you can create multiple progress bars within the same application, all with different colors.
As Uluk points out, you can use JavaFX 2.2 caspian.css in conjunction with the JavaFX 2 css reference guide and the JavaFX 2 css tutorial to determine how to style things.
Here is some sample code which customizes a progress bar based upon the information in those references.
Sample css:
/** progress.css
place in same directory as
ColoredProgressBarStyleSheet.java or SimpleColoredProgressBar.java
ensure build system copies the css file to the build output path */
.root { -fx-background-color: cornsilk; -fx-padding: 15; }
.progress-bar { -fx-box-border: goldenrod; }
.green-bar { -fx-accent: green; }
.yellow-bar { -fx-accent: yellow; }
.orange-bar { -fx-accent: orange; }
.red-bar { -fx-accent: red; }
Simple sample program:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
// shows multiple progress bars drawn in different colors.
public class SimpleColoredProgressBar extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.getChildren().setAll(
new ColoredProgressBar("red-bar", 0.2),
new ColoredProgressBar("orange-bar", 0.4),
new ColoredProgressBar("yellow-bar", 0.6),
new ColoredProgressBar("green-bar", 0.8)
);
layout.getStylesheets().add(getClass().getResource("progress.css").toExternalForm());
stage.setScene(new Scene(layout));
stage.show();
}
class ColoredProgressBar extends ProgressBar {
ColoredProgressBar(String styleClass, double progress) {
super(progress);
getStyleClass().add(styleClass);
}
}
}
Simple sample program output:
More complicated sample program with a single animated progress bar which changes color dynamically depending on the amount of progress made:
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
// shows a progress bar whose bar changes color depending on the amount of progress.
public class ColoredProgressBarStyleSheet extends Application {
public static void main(String[] args) { launch(args); }
private static final String RED_BAR = "red-bar";
private static final String YELLOW_BAR = "yellow-bar";
private static final String ORANGE_BAR = "orange-bar";
private static final String GREEN_BAR = "green-bar";
private static final String[] barColorStyleClasses = { RED_BAR, ORANGE_BAR, YELLOW_BAR, GREEN_BAR };
#Override public void start(Stage stage) {
final ProgressBar bar = new ProgressBar();
final Timeline timeline = new Timeline(
new KeyFrame(Duration.millis(0), new KeyValue(bar.progressProperty(), 0)),
new KeyFrame(Duration.millis(3000), new KeyValue(bar.progressProperty(), 1))
);
Button reset = new Button("Reset");
reset.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
timeline.playFromStart();
}
});
bar.progressProperty().addListener(new ChangeListener<Number>() {
#Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
double progress = newValue == null ? 0 : newValue.doubleValue();
if (progress < 0.2) {
setBarStyleClass(bar, RED_BAR);
} else if (progress < 0.4) {
setBarStyleClass(bar, ORANGE_BAR);
} else if (progress < 0.6) {
setBarStyleClass(bar, YELLOW_BAR);
} else {
setBarStyleClass(bar, GREEN_BAR);
}
}
private void setBarStyleClass(ProgressBar bar, String barStyleClass) {
bar.getStyleClass().removeAll(barColorStyleClasses);
bar.getStyleClass().add(barStyleClass);
}
});
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.getChildren().setAll(bar, reset);
layout.getStylesheets().add(getClass().getResource("progress.css").toExternalForm());
stage.setScene(new Scene(layout));
stage.show();
timeline.play();
}
}
More complicated sample program output:
You should to override (or customize) the style with JavaFX CSS selectors. See caspian.css for more information. In your own stylesheet define:
.progress-bar .bar {
-fx-background-color:
-fx-box-border,
linear-gradient(to bottom, derive(-fx-accent,95%), derive(-fx-accent,10%)),
red; /* this line is the background color of the bar */
-fx-background-insets: 0, 1, 2;
-fx-padding: 0.416667em; /* 5 */
}
For those who want a simple answer (and without needing to add CSS files):
ProgressBar pbGreen = new ProgressBar(0.6);
pbGreen.setStyle("-fx-accent: green");

How to make an undecorated window movable / draggable in JavaFX?

I have to create an application in which minimize and maximize button will be disabled.
I have used "StageStyle.UNDECORATED" with which the application will not be movable or draggable anymore, so I am searching for any other alternative to make my application.
Do anyone having solution for this?
To achieve the window to be undecorated but still movable/dragable you have to handle the appropriate MouseEvent on any node of your choice.
Example:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class SimpleWindowApplication extends Application {
private double xOffset = 0;
private double yOffset = 0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.initStyle(StageStyle.UNDECORATED);
BorderPane root = new BorderPane();
root.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
}
});
root.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
primaryStage.setX(event.getScreenX() - xOffset);
primaryStage.setY(event.getScreenY() - yOffset);
}
});
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Learn more from the very valuable examples contained on Oracle's JavaFX download page under: JavaFX Demos and Samples
Sole purpose of this class is to allow undecorated Window to be dragged. It also performs the duty to ensure TaskBar remains visible with FullScreen, and ensure undecorated window not dragged out of screen.
Lastly its provides a bug fix to the error "css resource not found."
Simply paste below code in the main class in the overridden start() method just about when the Stage is ABOUT READY to be shown or after.
WindowStyle.allowDrag(root, stage);
WindowStyle.stageDimension(stage.getWidth(), stage.getHeight());
NOTE: Paste the above when the Stage is ABOUT READY to be shown or after.
For full screen window use:
WindowStyle.fullScreen(Stage stage);
To resize back to previous use:
WindowStyle.restoreScreen(Stage stage);
To add custom stylesheets to your scene, Simply paste below code in the main class in the overridden start() method after defining your scene.
scene.getStylesheets().add(WindowStyle.addStyleSheet(String css));
The name of css to be used for styling can be in the form of: main.css or styles/main.css
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.input.MouseEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;
/**
* #author: BENJAH
*/
public class WindowStyle {
private static final Rectangle2D SCREEN_BOUNDS= Screen.getPrimary()
.getVisualBounds();
private static double[] pref_WH, offset_XY;
private static String styleSheet;
private WindowStyle(String css) {
styleSheet= getClass().getResource(css).toString();
}
protected static void allowDrag(Parent root, Stage stage) {
root.setOnMousePressed((MouseEvent p) -> {
offset_XY= new double[]{p.getSceneX(), p.getSceneY()};
});
root.setOnMouseDragged((MouseEvent d) -> {
//Ensures the stage is not dragged past the taskbar
if (d.getScreenY()<(SCREEN_BOUNDS.getMaxY()-20))
stage.setY(d.getScreenY() - offset_XY[1]);
stage.setX(d.getScreenX() - offset_XY[0]);
});
root.setOnMouseReleased((MouseEvent r)-> {
//Ensures the stage is not dragged past top of screen
if (stage.getY()<0.0) stage.setY(0.0);
});
}
//Sets the default stage prefered width and height.
protected static void stageDimension(Double width, Double height) {
pref_WH= new double[]{width, height};
}
protected static void fullScreen(Stage stage) {
stage.setX(SCREEN_BOUNDS.getMinX());
stage.setY(SCREEN_BOUNDS.getMinY());
stage.setWidth(SCREEN_BOUNDS.getWidth());
stage.setHeight(SCREEN_BOUNDS.getHeight());
}
protected static void restoreScreen(Stage stage) {
stage.setX((SCREEN_BOUNDS.getMaxX() - pref_WH[0])/2);
stage.setY((SCREEN_BOUNDS.getMaxY() - pref_WH[1])/2);
stage.setWidth(pref_WH[0]);
stage.setHeight(pref_WH[1]);
}
protected static String addStyleSheet(String css) {
new WindowStyle(css);
return styleSheet;
}
}

Resources