Stage loses fill (gradient) after simply creating a new control - javafx

I have reduced my recreate of this to the following. The line where a ToggleButton is instantiated causes my stage to lose its fill color; it goes white. I am just getting started with JavaFX, so please let me know if I'm doing something I shouldn't, here. This is using jre1.8.0_92 with Eclipse Neon (jfx8_2.3.0 plugin) on Windows 7 sp1.
public class Main extends Application {
public static void main(String[] args) {
if(args.length > 0) {
String s = args[0].toLowerCase();
if(s.equals("full"))
Machine.isFullScreen = true;
}
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Machine.startMachine(primaryStage);
}
}
public class Machine {
static boolean isFullScreen = false;
static Rectangle2D screenRect, backRect;
static Stage backStage;
static Scene backScene;
static Pane backPane;
private Machine() {}
static public void startMachine(Stage primaryStage) {
// backscreen
startScene(primaryStage);
// This line causes the fill to be lost
ToggleButton foo = new ToggleButton("hi");
}
static private void startScene(Stage primaryStage) {
// Stage
backStage = primaryStage;
backStage.initStyle(StageStyle.UNDECORATED);
backStage.setFullScreen(isFullScreen);
screenRect = Screen.getPrimary().getBounds();
if(!isFullScreen) {
int w = 1000, h = 500, t = 20;
backStage.setWidth(w);
backStage.setHeight(h);
backStage.setX((screenRect.getWidth() - w)/2);
backStage.setY(t);
}
backRect = new Rectangle2D(backStage.getX(), backStage.getY(),
backStage.getWidth(), backStage.getHeight());
// Scene
backScene = new Scene(backPane = new Pane());
// backScene.getStylesheets().add(Machine.class.getResource("mainStyle.css").toExternalForm());
// backScene.getRoot().setStyle("-fx-background-color: #CCFF99;");
backScene.setFill(new LinearGradient(0,0,1,1, true, CycleMethod.NO_CYCLE,
new Stop[]{
new Stop(0,Color.web("#4977A3")),
new Stop(0.5, Color.web("#B0C6DA")),
new Stop(1,Color.web("#9CB6CF")), } ));
// Logo
Text logo = new Text("AMT");
logo.setFill(Color.DEEPSKYBLUE);
Font font = Font.font("Times New Roman", FontWeight.BOLD, FontPosture.ITALIC, 96);
logo.setFont(font);
logo.setX(100);
logo.setY(150);
backPane.getChildren().add(logo);
backStage.setScene(backScene);
backStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
backStage.show();
}
}

The setFill() API suggests that this may be a stylesheet effect. The default stylesheet is installed statically when the first Control is instantiated. If that stylesheet is Modena, "the default fill is set to be a light gray color." Instead of backScene.setFill(), try backPane.setBackground(), as suggested here and here.
// Scene
backPane = new Pane();
backScene = new Scene(backPane);
LinearGradient linearGradient = new LinearGradient(
0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
new Stop(0, Color.web("#4977A3")),
new Stop(0.5, Color.web("#B0C6DA")),
new Stop(1, Color.web("#9CB6CF")));
backPane.setBackground(new Background(new BackgroundFill(
linearGradient, CornerRadii.EMPTY, Insets.EMPTY)));
As an aside, note that the varargs constructor parameter of LinearGradient allows you to add instances of Stop directly, without creating a new array.

Related

JavaFX WhatApp-Like ConversationView

I'm trying to make a WhatsApp-Like Conversation-View in JavaFX.
In order to make the sent messages appear on the right and the received messages appear on the left then I cannot use TextArea. How can I do it? I tried GridPane without TextArea but it didn't make things easier.
Moreover, is it a good practice to make controls static?
Extra: if you can also help me do the chat bubble behind the text, it would be great.
Here is my code:
public class ConversationView implements WhatAppView {
private static Label nameLabel, statusLabel;
private static TextField messageTextField;
static TextArea messagesTextArea;
private static GridPane conversationSection;
private static Label changeViewLink;
private static Button sendMsgButton;
// private static int rowIndex = 1;
public void showView() {
AppMain.stage.setResizable(false);
AppMain.stage.setWidth(350);
AppMain.stage.setHeight(550);
BorderPane rootPane = new BorderPane();
rootPane.setPadding(new Insets(5, 5, 5, 5));
final int sectionHeight = 55;
StackPane contactSection = new StackPane();
nameLabel = new Label("RW");
statusLabel = new Label("Online");
changeViewLink = new Label("Go Back");
changeViewLink.setStyle("-fx-text-fill: blue;");
changeViewLink.styleProperty().bind(
Bindings.when(changeViewLink.hoverProperty())
.then(new SimpleStringProperty("-fx-underline: true; -fx-text-fill: blue;"))
.otherwise(new SimpleStringProperty("-fx-underline: false; -fx-text-fill: blue;")));
changeViewLink.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
AppMain.changeView(new ChatsView());
}
});
contactSection.getChildren().addAll(nameLabel, statusLabel, changeViewLink);
StackPane.setAlignment(changeViewLink, Pos.TOP_RIGHT);
StackPane.setAlignment(statusLabel, Pos.BOTTOM_CENTER);
contactSection.setPrefHeight(sectionHeight);
conversationSection = new GridPane();
conversationSection.setStyle("-fx-background-image: url('whatsapp-wallpaper.jpg')");
messagesTextArea = new TextArea();
messagesTextArea.setEditable(false);
// conversationSection.getColumnConstraints().addAll(new
// ColumnConstraints(AppMain.stage.getWidth()/2 - 10), new
// ColumnConstraints(AppMain.stage.getWidth()/2 - 10));
conversationSection.add(messagesTextArea, 0, 0);
conversationSection.setPrefSize(AppMain.stage.getWidth(), AppMain.stage.getHeight());
// conversationSection.getStylesheets().add("conversation.css");
ScrollPane scroll = new ScrollPane();
scroll.setPrefSize(conversationSection.getWidth(), conversationSection.getHeight());
scroll.setContent(conversationSection);
FlowPane messageSection = new FlowPane();
sendMsgButton = new Button("_Send");
sendMsgButton.setDisable(true);
sendMsgButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
sendMsg();
}
});
sendMsgButton.setPrefHeight(sectionHeight);
Tooltip sendMsgToolTip = new Tooltip("Send Message");
Tooltip.install(sendMsgButton, sendMsgToolTip);
FlowPane.setMargin(sendMsgButton, new Insets(0, 0, 0, 5));
messageTextField = new TextField();
messageTextField.setPromptText("Type your message here...");
Platform.runLater(new Runnable() { // 100% focus
public void run() {
messageTextField.requestFocus();
}
});
messageTextField.setPrefWidth(AppMain.stage.getWidth() - AppMain.stage.getWidth() / 5);
messageTextField.setPrefHeight(sectionHeight);
messageTextField.setAlignment(Pos.TOP_LEFT);
messageTextField.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (messageTextField.getText() != null && !messageTextField.getText().isEmpty()) {
sendMsgButton.setDisable(false);
} else {
sendMsgButton.setDisable(true);
}
}
});
messageTextField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode().equals(KeyCode.ENTER) && messageTextField.getText() != null
&& !messageTextField.getText().isEmpty()) {
sendMsg();
}
}
});
messageSection.getChildren().add(messageTextField);
messageSection.getChildren().add(sendMsgButton);
messageSection.setPrefHeight(sectionHeight);
rootPane.setTop(contactSection);
rootPane.setCenter(conversationSection);
rootPane.setBottom(messageSection);
Scene scene = new Scene(rootPane);
AppMain.stage.setScene(scene);
AppMain.stage.setTitle("WhatsApp");
}
}
public class AppMain extends Application {
static Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
AppMain.stage.show();
changeView(new ConversationView());
}
public static void changeView(WhatAppView view) {
view.showView();
}
}
public interface WhatAppView {
public void showView();
}
You can create a custom control to determine message alignment and aesthetics such as the bubble like appearance. As a fan of HBox and VBox, I would recommend their usage in combination with an SVGPath to decorate the message.
SVGPath's let you draw custom shapes by providing information on the lines, arcs etc. These aren't unique to java so there are a few resources available to see some basic/advanced examples. My recommendation would be to read here: SVGPath and use the TryitEditor to experiment
Here are two quick examples:
When it comes to laying out the messages a VBox would suffice. You can bind the viewable children to an ObservableList of messages you would be able to iterate later. The added benefit of this is that adding to the list will update the UI automatically, and you'll also be able to iterate these later in the event you implement additional features such as delete, forward etc
I'd recommend reading up on the Bindings api, particularly bindContentBidirectional for more information on this
Using my above recommendations i've written a small example below you can reference. It's not visually impressive, but hopefully you can get some ideas from it, particularly this:
Extra: if you can also help me do the chat bubble behind the text, it
would be great.
The messages/speech bubbles:
enum SpeechDirection{
LEFT, RIGHT
}
public class SpeechBox extends HBox{
private Color DEFAULT_SENDER_COLOR = Color.GOLD;
private Color DEFAULT_RECEIVER_COLOR = Color.LIMEGREEN;
private Background DEFAULT_SENDER_BACKGROUND, DEFAULT_RECEIVER_BACKGROUND;
private String message;
private SpeechDirection direction;
private Label displayedText;
private SVGPath directionIndicator;
public SpeechBox(String message, SpeechDirection direction){
this.message = message;
this.direction = direction;
initialiseDefaults();
setupElements();
}
private void initialiseDefaults(){
DEFAULT_SENDER_BACKGROUND = new Background(
new BackgroundFill(DEFAULT_SENDER_COLOR, new CornerRadii(5,0,5,5,false), Insets.EMPTY));
DEFAULT_RECEIVER_BACKGROUND = new Background(
new BackgroundFill(DEFAULT_RECEIVER_COLOR, new CornerRadii(0,5,5,5,false), Insets.EMPTY));
}
private void setupElements(){
displayedText = new Label(message);
displayedText.setPadding(new Insets(5));
displayedText.setWrapText(true);
directionIndicator = new SVGPath();
if(direction == SpeechDirection.LEFT){
configureForReceiver();
}
else{
configureForSender();
}
}
private void configureForSender(){
displayedText.setBackground(DEFAULT_SENDER_BACKGROUND);
displayedText.setAlignment(Pos.CENTER_RIGHT);
directionIndicator.setContent("M10 0 L0 10 L0 0 Z");
directionIndicator.setFill(DEFAULT_SENDER_COLOR);
HBox container = new HBox(displayedText, directionIndicator);
//Use at most 75% of the width provided to the SpeechBox for displaying the message
container.maxWidthProperty().bind(widthProperty().multiply(0.75));
getChildren().setAll(container);
setAlignment(Pos.CENTER_RIGHT);
}
private void configureForReceiver(){
displayedText.setBackground(DEFAULT_RECEIVER_BACKGROUND);
displayedText.setAlignment(Pos.CENTER_LEFT);
directionIndicator.setContent("M0 0 L10 0 L10 10 Z");
directionIndicator.setFill(DEFAULT_RECEIVER_COLOR);
HBox container = new HBox(directionIndicator, displayedText);
//Use at most 75% of the width provided to the SpeechBox for displaying the message
container.maxWidthProperty().bind(widthProperty().multiply(0.75));
getChildren().setAll(container);
setAlignment(Pos.CENTER_LEFT);
}
}
Conversation window:
public class ConversationView extends VBox{
private String conversationPartner;
private ObservableList<Node> speechBubbles = FXCollections.observableArrayList();
private Label contactHeader;
private ScrollPane messageScroller;
private VBox messageContainer;
private HBox inputContainer;
public ConversationView(String conversationPartner){
super(5);
this.conversationPartner = conversationPartner;
setupElements();
}
private void setupElements(){
setupContactHeader();
setupMessageDisplay();
setupInputDisplay();
getChildren().setAll(contactHeader, messageScroller, inputContainer);
setPadding(new Insets(5));
}
private void setupContactHeader(){
contactHeader = new Label(conversationPartner);
contactHeader.setAlignment(Pos.CENTER);
contactHeader.setFont(Font.font("Comic Sans MS", 14));
}
private void setupMessageDisplay(){
messageContainer = new VBox(5);
Bindings.bindContentBidirectional(speechBubbles, messageContainer.getChildren());
messageScroller = new ScrollPane(messageContainer);
messageScroller.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
messageScroller.setHbarPolicy(ScrollBarPolicy.NEVER);
messageScroller.setPrefHeight(300);
messageScroller.prefWidthProperty().bind(messageContainer.prefWidthProperty().subtract(5));
messageScroller.setFitToWidth(true);
//Make the scroller scroll to the bottom when a new message is added
speechBubbles.addListener((ListChangeListener<Node>) change -> {
while (change.next()) {
if(change.wasAdded()){
messageScroller.setVvalue(messageScroller.getVmax());
}
}
});
}
private void setupInputDisplay(){
inputContainer = new HBox(5);
TextField userInput = new TextField();
userInput.setPromptText("Enter message");
Button sendMessageButton = new Button("Send");
sendMessageButton.disableProperty().bind(userInput.lengthProperty().isEqualTo(0));
sendMessageButton.setOnAction(event-> {
sendMessage(userInput.getText());
userInput.setText("");
});
//For testing purposes
Button receiveMessageButton = new Button("Receive");
receiveMessageButton.disableProperty().bind(userInput.lengthProperty().isEqualTo(0));
receiveMessageButton.setOnAction(event-> {
receiveMessage(userInput.getText());
userInput.setText("");
});
inputContainer.getChildren().setAll(userInput, sendMessageButton, receiveMessageButton);
}
public void sendMessage(String message){
speechBubbles.add(new SpeechBox(message, SpeechDirection.RIGHT));
}
public void receiveMessage(String message){
speechBubbles.add(new SpeechBox(message, SpeechDirection.LEFT));
}
}
Output:

JavaFX ColorPicker show uncolor option

I want to show un-color option in my ColorPicker.
How i can show it?
Thanks.
The solution is a bit of a hack, but it avoids using private API.
These are the required steps:
Get the Popup control that shows up when you click on the ColorPicker.
You can find it here or here.
Get the square colors on that popup, so we can change one of them. I'll use the last one.
Once we have the popup, we'll get the set of square colors by using lookups: Set<Node> squares = popup.lookupAll(".color-rect");
Let's use the last color to add our customized 'un-color'.
Find out how to draw that red diagonal line.
I've come up with a LinearGradient:
final LinearGradient redLine = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
new Stop(0, Color.WHITE), new Stop(0.45, Color.WHITE),
new Stop(0.46, Color.RED), new Stop(0.54, Color.RED),
new Stop(0.55, Color.WHITE), new Stop(1, Color.WHITE));
That works fine, but sadly the gradient breaks the ColorPicker control, that is an extension of ComboBoxBase<Color>, and all the fills used for the rectangles will be casted to Color instead of Paint. That means we'll have to use a color (for instance Color.TRANSPARENT) during the transitions.
Solve other issues like the square color that will be seen when the popup closes, or the square color that shows up on hovering.
For this, we need to lookup for both the square color in the color picker and hovered square, and when those match our transparent one, replace the color with the gradient.
This is the code:
public class UnColorPicker extends Application {
private final LinearGradient redLine = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
new Stop(0, Color.WHITE), new Stop(0.45, Color.WHITE), new Stop(0.46, Color.RED),
new Stop(0.54, Color.RED), new Stop(0.55, Color.WHITE), new Stop(1, Color.WHITE));
#Override
public void start(Stage primaryStage) {
ColorPicker picker = new ColorPicker();
StackPane root = new StackPane(picker);
Scene scene = new Scene(root, 500, 400);
primaryStage.setScene(scene);
primaryStage.show();
Rectangle rect = (Rectangle) root.lookup(".picker-color-rect");
Label label = (Label) root.lookup(".color-picker-label");
picker.showingProperty().addListener((obs, b, b1) -> {
if (b1) {
PopupWindow popupWindow = getPopupWindow();
Node popup = popupWindow.getScene().getRoot().getChildrenUnmodifiable().get(0);
StackPane hover = (StackPane) popup.lookup(".hover-square");
Rectangle rectH = (Rectangle) hover.getChildren().get(0);
Set<Node> squares = popup.lookupAll(".color-rect");
squares.stream()
.skip(squares.size()-2)
.map(Rectangle.class::cast)
.findFirst()
.ifPresent(r -> {
r.getParent().setOnMousePressed(e -> {
// avoid CastException
r.setFill(Color.TRANSPARENT);
e.consume();
});
r.getParent().setOnMouseReleased(e -> {
Platform.runLater(() -> {
rect.setFill(redLine);
label.setText("Un-color");
});
});
r.setFill(redLine);
Tooltip.install(r.getParent(), new Tooltip("Un-color"));
});
hover.visibleProperty().addListener((obs2, ov, nv) -> {
if (nv && rectH.getFill().equals(Color.TRANSPARENT)) {
Platform.runLater(() -> rectH.setFill(redLine));
}
});
}
});
}
private PopupWindow getPopupWindow() {
#SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
return (PopupWindow)window;
}
}
return null;
}
public static void main(String[] args) {
launch(args);
}
}
The approach posted above didn't work for me anymore so I came up with a slightly different solution, though the idea is the same. It also avoids using deprecated functions.
I subclassed the ColorPicker class to build my own CustomColorPicker which can be used instead.
Here is my code:
#SuppressWarnings("restriction")
public class CustomColorPicker extends ColorPicker {
final static LinearGradient RED_LINE = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
new Stop(0, Color.WHITE), new Stop(0.45, Color.WHITE),
new Stop(0.46, Color.RED), new Stop(0.54, Color.RED),
new Stop(0.55, Color.WHITE), new Stop(1, Color.WHITE));
#Override
protected Skin<?> createDefaultSkin() {
final CustomColorPickerSkin skin = new CustomColorPickerSkin(this);
final Label lbl = (Label)skin.getDisplayNode();
final StackPane pane = (StackPane)lbl.getGraphic();
final Rectangle rect = (Rectangle)pane.getChildren().get(0);
// set initial color to red line if transparent is shown
if (getValue().equals(Color.TRANSPARENT))
rect.setFill(RED_LINE);
// set color to red line when transparent is selected
rect.fillProperty().addListener((o, oldVal, newVal) -> {
if (newVal != null && newVal.equals(Color.TRANSPARENT))
rect.setFill(RED_LINE);
});
return skin;
}
private class CustomColorPickerSkin extends ColorPickerSkin {
private boolean initialized = false;
public CustomColorPickerSkin(ColorPicker colorPicker) {
super(colorPicker);
}
#Override
protected Node getPopupContent() {
final ColorPalette popupContent = (ColorPalette)super.getPopupContent();
// make sure listeners and geometry are only created once
if (!initialized) {
final VBox paletteBox = (VBox)popupContent.getChildrenUnmodifiable().get(0);
final StackPane hoverSquare = (StackPane)popupContent.getChildrenUnmodifiable().get(1); // ColorSquare
final Rectangle hoverRect = (Rectangle)hoverSquare.getChildren().get(0); // ColorSquare
final GridPane grid = (GridPane)paletteBox.getChildren().get(0); // ColorPalette
final StackPane colorSquare = (StackPane)grid.getChildren().get(grid.getChildren().size()-1); // ColorSquare
final Rectangle colorRect = (Rectangle)colorSquare.getChildren().get(0);
// set fill color of original color rectangle to transparent
// (can't be set to red line gradient because ComboBoxBase<Color> tries to cast it to Color)
colorRect.setFill(Color.TRANSPARENT);
// put another rectangle with red line on top of it
colorSquare.getChildren().add(new Rectangle(colorRect.getWidth(), colorRect.getHeight(), RED_LINE));
// show red line gradient also in hover rectangle when the transparent color is selected
hoverRect.fillProperty().addListener((o, oldVal, newVal) -> {
if (newVal.equals(Color.TRANSPARENT))
hoverRect.setFill(RED_LINE);
});
initialized = true;
}
return popupContent;
}
}
}

JavaFx choicebox event handler method doesnt change variable?

I'm new at javafx but I'm writing an app and I want my "to" value to change depending on which option is chosen in the choicebox but my current code always keeps it at 0..help? I want to be able to change the to depending on state
public void start(Stage primaryStage) {
double to=0;
primaryStage.setTitle("ShCal");
GridPane pane = new GridPane();
` pane.setAlignment(Pos.CENTER);
pane.setHgap(10);
pane.setVgap(10);
pane.setPadding(new Insets(25, 25, 25, 25));
Scene scene = new Scene(pane, 300, 275);
//button
Button button=new Button("to");
pane.add(button, 0, 3);
//Pick state
Label State=new Label("State");
pane.add(State,0,0);
//choicebox
ChoiceBox<String> choicesBox=new ChoiceBox<>();
choicesBox.getItems().addAll("NJ","NY");
pane.add(choicesBox,1,0);
//set default
choicesBox.setValue(null);
button.setOnAction(e->getChoice(choicesBox,to));
primaryStage.setScene(scene);
primaryStage.show();
}
private double getChoice(ChoiceBox<String> choicesBox, double tx) {
String state=choicesBox.getValue();
System.out.print(tx);
if(state=="NJ")
{
tx=10/100;
}
System.out.print(state);
return tx;
}
public static void main(String[] args) {
launch(args);
}
}
That is because your to value is of the primitive type double, defined in the scope of your start method. The method getChoice returns the new value, but you are not updating it.
Here are two approaches that you can try:
Define to as member:
private double to = 0;
private double getChoice(ChoiceBox<String> choicesBox) {
String state=choicesBox.getValue();
if(state=="NJ") {
tx=10/100;
}
}
However I personally would prefer a solution that is more inline with JavaFX: Define the to variable as member property:
private DoubleProperty to = new SimpleDoubleProperty(0);
private double getChoice(ChoiceBox<String> choicesBox) {
String state=choicesBox.getValue();
if(state=="NJ") {
tx.setValue(10/100);
}
}
Doing it this way you can then for example have a label displaying the value without the hassle of requiring to update it on each change:
Label lbl = new Label();
lbl.textProperty().bind(to.asString());

How can I assign an EventListener to a Path in javafx?

I have this piece of code which doesn't work correctly.
I want to set a listener for when a user clicks inside the square, yet
neither the pop-up nor the message "clicked" are displayed when I click
inside the square.
What am I missing?
This method is inside the Coords class.
public static void drawMyShape(final GraphicsContext ctx) {
Path path = new Path();
MoveTo mT = new MoveTo();
LineTo lT[] = new LineTo[4];
mT.setX(200.0);
mT.setY(200.0);
lT[0] = new LineTo(400.0, 200.0);
lT[1] = new LineTo(400.0, 400.0);
lT[2] = new LineTo(200.0, 400.0);
lT[3] = new LineTo(200.0, 200.0);
path.setStroke(Color.BEIGE);
path.getElements().addAll(mT, lT[0], lT[1], lT[2], lT[3]);
path.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
final Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.initOwner(Main.prim_stage);
VBox box = new VBox(20);
box.getChildren().add(new Text("Hey"));
Scene s = new Scene(box, 300, 200);
dialog.setScene(s);
dialog.show();
System.out.println("Clicked");
}
});
ctx.setLineWidth(4.0);
ctx.beginPath();
ctx.moveTo(mT.getX(), mT.getY());
for (int i = 0; i < lT.length; i++) {
ctx.lineTo(lT[i].getX(), lT[i].getY());
}
ctx.closePath();
ctx.stroke();
}
EDITED ON SUGGESTION by users.
So his is the main program:
public class Main extends Application {
public static Pane root;
private static Canvas main_canvas;
private static GraphicsContext ctx;
private static Rectangle2D bounds;
private static Scene scene;
public static Stage prim_stage;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Switzerland Advertising");
initElements(primaryStage);
Coords.drawMyShape(ctx);
primaryStage.show();
System.out.println("Launched");
}
public static void main(String[] args) {
launch(args);
}
}
Everything is instanciated inside the following function, which works correctly and displays a full screen application with a canvas and a square drawn into it (image at the bottom).
private void initElements(final Stage primaryStage) {
prim_stage = primaryStage;
// ----------------------------------------
bounds = Screen.getPrimary().getVisualBounds();
double w = bounds.getWidth();
double h = bounds.getHeight();
// ----------------------------------------
// init elements of scene
root = new Pane();
main_canvas = new Canvas(w, h);
// ----------------------------------------
// init scene elements
scene = new Scene(root, w, h);
primaryStage.setX(bounds.getMinX());
primaryStage.setY(bounds.getMinY());
primaryStage.setWidth(w);
primaryStage.setHeight(h);
primaryStage.setScene(scene);
// ----------------------------------------
ctx = main_canvas.getGraphicsContext2D();
// set elements in main pane
root.getChildren().add(main_canvas);
// ----------------------------------------
}
So how can I make the pop-up window appear whenever I click inside the region drawn on the canvas?
This is the program
Your path is just a local variabl within your method. It has to be attached to the scene graph in order to get events. But when you attach it to the scene graph, drawing the same path on a canvas also does not make much sense.

Change between two FXML files in JavaFX2.2 on change Listener

i am new in JavaFX programming. I have an Application, with a simple login page as described in the example here, and i add a StringProperty to the actiontarget element. So when the text changes inside the actiontarget i want a new FXML file with a webview inside, to load from the FXMLLoader and be dipslayed on the screen. Below is the exception i get. I can load any other fxml file, without a webview inside it, without a problem. Thanks in advance.Code samples below
The exception :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
at com.sun.webpane.sg.prism.InvokerImpl.checkEventThread(InvokerImpl.java:33)
at com.sun.webpane.platform.WebPage.<init>(WebPage.java:189)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:57)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:51)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:704)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:691)
at javafx.scene.web.WebView.<init>(WebView.java:245)
at student.WebBrowser.<init>(WebBrowser.java:31)
at Login.Login.replaceSceneContent(Login.java:171)
at Login.Login.access$000(Login.java:66)
at Login.Login$2.changed(Login.java:143)
at Login.Login$2.changed(Login.java:137)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at Login.Client.run(Client.java:66)
First my listener:
// Add change listener
sp.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
if(t1.equalsIgnoreCase("user authenticated successfully")){
try {
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
System.out.println("everything ok");
} catch (Exception ex) {
System.out.println("something went wrong");
ex.printStackTrace();
}
}
}
});
Second my method : replaceSceneContent(String fxml, Stage stage)
private Parent replaceSceneContent(String fxml, Stage stage) throws Exception {
Parent page = (Parent) FXMLLoader.load(getClass().getResource("/FXML_Files/"+fxml), null, new JavaFXBuilderFactory());
Scene scene = stage.getScene();
if (scene == null) {
scene = new Scene(page, 700, 450);
stage.setScene(scene);
} else {
stage.getScene().setRoot(page);
}
if(fxml.equalsIgnoreCase("Student.fxml")){
Pane spane = (Pane) page.lookup("#pane");
WebBrowser wb = new WebBrowser();
spane.getChildren().add(wb);
}
return page;
}
And my WebBrowser class similar to the example in NetBeans7.2:
public class WebBrowser extends Pane {
public WebBrowser() {
WebView view;
final WebEngine eng;
view = new WebView();
view.setMinSize(10, 10);
view.setPrefSize(500, 400);
eng = view.getEngine();
eng.load("http://www.oracle.com/us/index.html");
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
final TextField locationField = new TextField("http://www.oracle.com/us/index.html");
locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Go");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.load(locationField.getText().startsWith("http://") ? locationField.getText() :
"http://" + locationField.getText());
}
};
goButton.setOnAction(goAction);
locationField.setOnAction(goAction);
eng.locationProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
locationField.setText(newValue);
}
});
GridPane grid = new GridPane();
ButtonsEvents be = new ButtonsEvents();
TilePane tp = be;
tp.setAlignment(Pos.CENTER);
grid.setVgap(5);
grid.setHgap(5);
GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES);
GridPane.setConstraints(goButton,1,0);
GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(tp, 0, 2, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.NEVER);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.ALWAYS, HPos.CENTER, true)
);
grid.getChildren().addAll(locationField, goButton,view, tp);
getChildren().add(grid);
}
#Override
protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
As stated in the exception, changes in the JavaFX scene graph can only be made in the JavaFX application thread. To make sure your code is run in this thread, try the following:
Platform.runLater(new Runnable() {
#Override
public void run() {
// This method is invoked on JavaFX thread
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
}
});
The javafx manager will run this code at some point in the future on the correct thread.
More information:
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

Resources