Lambda expression button.setOnAction is not working outside the Main class - javafx

I'm trying to attach a lambda expression to a button to fire a method. I want that lambda outside the Main method of class and outside a method. I have imported javafx.event.ActionEvent not to mix up with java.awt.event.ActionEvent. I might not be explaining the problem in an understandable way but hopefully the code will help.
Note: I have tried to use lambda expression within a method, outside a method, just to see if it works but it doesn't work outside a.
Error message is "Can't resolve symbol'setOnAction' "
package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
public class DashboardController {
#FXML
private Label lblHidden;
#FXML
private Button btnReveal;
#FXML
void backToLogin(ActionEvent event) throws Exception{
Parent myRoot = FXMLLoader.load(getClass().getResource("login.fxml"));
Scene loginScence = new Scene(myRoot);
Stage myStage = (Stage)((Node)event.getSource()).getScene().getWindow();
myStage.setScene(loginScence);
myStage.setTitle("Login");
myStage.show();
//Lambda works though not need here - inside a method
btnReveal.setOnAction(e->{
lblHidden.setText("Lambda inside a method - works");
});
}
//Lambda doesn't work here - -- I want it to work here
btnReveal.setOnAction(e->{`enter code here`
lblHidden.setText("Lambda doesn't work here");
});
}

Related

Duplicate the view of a component in javafx

I am looking for a way to duplicate just the view of a component in javafx. This duplicate will not have any of the mouse or key events, and will basically be like a canvas.
As long as this duplicate doesn't change, I can use the snapshot(...) method that nodes have. My problem is that I want this to be dynamic. For example, I have one pane which displays some sort of animation (which can depend on user input), and I want a second pane which show the exact same image (but cannot have in itself user input like mouse presses).
The reflection effect is very specific case of what I need. Is there any way to do it in general?
One way to do it (which I used so far) is to just create a second duplicate component and connect all its input to the first. The problem is that this is a lot of work for every component that I want to copy, and it cannot be done generically.
A second way is just to take a snapshot every time the original component changes and copy it to the duplicate. This should be the solution, but I think that there is supposed to be a more low level solution instead of listening to changes, create an Image duplicate and then update an ImageView.
You could use some kind of binding. This is a basic example
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.control.Separator;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXTestingGround extends Application
{
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
Circle circle = new Circle(0, 200, 25, Color.BLUE);
Circle duplicateCircle = new Circle(25, Color.BLUE);
#Override
public void start(Stage primaryStage)
{
duplicateCircle.centerXProperty().bind(circle.centerXProperty());
duplicateCircle.centerYProperty().bind(circle.centerYProperty());
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1.0 / 40.0), (ActionEvent t) -> {
circle.setCenterX(circle.getCenterX() + 1);
}));
timeline.setCycleCount(Timeline.INDEFINITE);
circle.setOnMouseClicked((t) -> {
switch (timeline.getStatus()) {
case STOPPED:
timeline.play();
break;
case RUNNING:
timeline.stop();
break;
}
});
Pane root = new Pane(circle);
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
Pane duplicateRoot = new Pane(duplicateCircle);
Stage duplicatStage = new Stage();
duplicatStage.setScene(new Scene(duplicateRoot, 400, 400));
duplicatStage.setX(primaryStage.getX() + primaryStage.getWidth() + 10);
duplicatStage.setY(primaryStage.getY());
duplicatStage.show();
}
}

How to vertically center content of Alert/DialogPane?

I created an Alert that is undecorated and I want the content to be centered in the alert. However, there seems to be some padding at the bottom that I can not get rid of.
Here is an MCVE:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Alert alert = new Alert(Alert.AlertType.NONE);
DialogPane pane = new DialogPane();
HBox contentPane = new HBox(10);
contentPane.setPadding(new Insets(10));
contentPane.setAlignment(Pos.CENTER);
// Add progress indicator and label to contentPane
contentPane.getChildren().addAll(
new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS) {{
setPrefSize(30, 30);
}},
new Label("Loading ...")
);
// Add border to demonstate the lower padding
contentPane.setStyle("-fx-border-color: black");
pane.setPadding(new Insets(10));
pane.setStyle("-fx-border-color: black");
pane.setContent(contentPane);
alert.setDialogPane(pane);
alert.initStyle(StageStyle.UNDECORATED);
alert.showAndWait();
}
}
The result is this window:
How do I create an Alert or DialogPane without that gap at the bottom?
Using:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private static Border createBorder(Paint stroke) {
return new Border(new BorderStroke(stroke, BorderStrokeStyle.SOLID,
null, new BorderWidths(2)));
}
#Override
public void start(Stage primaryStage) throws Exception {
ProgressIndicator indicator = new ProgressIndicator();
indicator.setPrefSize(30, 30);
Label label = new Label("Loading...");
label.setMinWidth(Label.USE_PREF_SIZE);
HBox content = new HBox(10, indicator, label);
content.setBorder(createBorder(Color.BLUE));
content.setPadding(new Insets(10));
content.setAlignment(Pos.CENTER);
Alert alert = new Alert(Alert.AlertType.NONE);
alert.initStyle(StageStyle.UNDECORATED);
alert.getDialogPane().getStylesheets()
.add(getClass().getResource("Main.css").toExternalForm());
alert.getDialogPane().setPadding(new Insets(10));
alert.getDialogPane().setContent(content);
alert.getDialogPane().setBorder(createBorder(Color.RED));
alert.show();
}
}
Where this is Main.css:
.dialog-pane .button-bar .container {
-fx-padding: 0px;
}
.dialog-pane:no-header .graphic-container {
-fx-padding: 0px;
}
Resulted in the following:
Some of the needed style-classes I got from here (JavaFX CSS Reference). However, I mostly figured this out from looking at modena.css (where they have the styles for dialog-pane).
If you don't want to use external CSS you can replace alert.getDialogPane().getStylesheets().add(...) with:
alert.getDialogPane().applyCss(); // Seems to stop the lookup calls
// from returning null
alert.getDialogPane()
.lookup(".button-bar")
.lookup(".container")
.setStyle("-fx-padding: 0px;");
alert.getDialogPane().lookup(".graphic-container").setStyle("-fx-padding: 0px;");
alert.show();
Update following your comments.
I initially tried this using Java 10.0.2 but I just tried using Java 8u181 and it still worked for me. I notice in your MCVE you are using a new DialogPane whereas I am using the DialogPane that initially comes with the Alert. Note that the applyCss() method will only work if the Node is a part of a Scene. Apparently the DialogPane is made part of a Scene as soon as it's part of the Alert. So one way you could avoid the NullPointerExceptions is to use the DialogPane that comes with the Alert, as I do.
If you still want to use your own DialogPane you just have to call alert.setDialogPane before calling applyCss(). Note that when I tried this, however, the call lookup(".graphic-container") still returned null. I guess that Node is only present on the original DialogPane. Removing that lookup call fixed it and the other lookup call (for .button-bar > .container) still worked as expected.
All this seems like implementation behavior that should not be relied upon but it works for me on both Java 8 and Java 10. I'd watch this code when changing Java versions just in case though.

Add a JavaFX Group a Max Width and Height

I have a JavaFX Group with a set of SVGPath's and the problem occurs when i try put a specific size for the Group. The group doesn't response me and put the original size of the file. I try put the group into a AnchorPane and them put a size in the AnchorPane and doesn't work.
package es.raulgf.mapfrance2;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.SVGPath;
public class MapFrance2 extends AnchorPane {
public double originalWeight=450;
public double originalHeight=650;
Group map;
public MapFrance2(double height,double width){
setMaxHeight(height);
setPrefHeight(height);
setPrefWidth(width);
setMaxWidth(width);
try {
map=FXMLLoader.load(getClass().getResource("MapFranceEmpty.fxml"));
map.setScaleX(height/originalHeight);
map.setScaleY(width/originalWeight);
getChildren().add(map);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
other questions is how can resize my Group of SVGPath's and the SVG draw not exceed the AnchorPane border?
Put the group with the SVGPath inside StackPane that will fix your problem.

Superscript and Subscript in javafx

Can someone suggest a workaround to add Superscript and Subscript controls in the existing javafx HTML editor.
I am trying to develop a Formula field editor having Bold, Italics, Superscript, Subscript and font selector as controls.
This isn't possible (AFAIK) without some pretty serious hacking, involving accessing parts of the API that are intended not to be accessed. The following more or less works; I based it on the source code for the HTMLEditorSkin. You may need to persuade your IDE to let you access the relevant packages. This is not particularly recommended, and it almost certainly will not work in Java 9.
import com.sun.javafx.webkit.Accessor;
import com.sun.webkit.WebPage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class HTMLEditorHack extends Application {
#Override
public void start(Stage primaryStage) {
HTMLEditor editor = new HTMLEditor();
Scene scene = new Scene(editor);
editor.applyCss();
editor.layout();
WebView webView = (WebView) editor.lookup(".web-view");
ToolBar toolbar = (ToolBar) editor.lookup(".bottom-toolbar");
ToggleGroup toggleGroup = new ToggleGroup();
createToggleButton("superscript", "Super", toggleGroup, webView, toolbar);
createToggleButton("subscript", "Sub", toggleGroup, webView, toolbar);
primaryStage.setScene(scene);
primaryStage.show();
}
private void createToggleButton(String command, String label, ToggleGroup toggleGroup, WebView webView, ToolBar toolbar) {
ToggleButton button = new ToggleButton(label);
button.setFocusTraversable(false);
button.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
WebPage page = Accessor.getPageFor(webView.getEngine());
if (page.queryCommandState(command) != isSelected) {
page.executeCommand(command, null);
}
});
button.setToggleGroup(toggleGroup);
toolbar.getItems().add(button);
EventHandler<Event> updateState = e -> {
Platform.runLater(() -> {
WebPage page = Accessor.getPageFor(webView.getEngine());
button.setDisable(! page.queryCommandEnabled(command));
button.setSelected(page.queryCommandState(command));
});
};
webView.addEventHandler(KeyEvent.ANY, updateState);
webView.addEventHandler(MouseEvent.MOUSE_PRESSED, updateState);
webView.addEventHandler(MouseEvent.MOUSE_RELEASED, updateState);
}
public static void main(String[] args) {
launch(args);
}
}
If you need a robust approach for creating an equation editor, I would probably consider building your own, instead of using/hacking HTMLEditor. There is a third party library, RichTextFX that can be used to create an editable text area with varying styles. Start there and add your own controls for styling.

Why my Stage.close is not Working

Stage.close() is not working for me.
I've checked on:
JavaFX 2.0: Closing a stage (window)
Here is my codes:
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.stage.Modality;
public class MsgBox {
public Stage MessageBox(String Title, String Message){
VBox Pnl = new VBox();
Pnl.setPadding(new Insets(10,10,10,10));
Pnl.setSpacing(10);
Pnl.setAlignment(Pos.CENTER);
Label LblMsg = new Label(Message);
Button CmdOK = new Button("OK");
Pnl.getChildren().addAll(LblMsg, CmdOK);
Scene SCN = new Scene(Pnl);
Stage Window = new Stage();
Window.initModality(Modality.APPLICATION_MODAL);
Window.setTitle(Title);
Window.setScene(SCN);
Window.showAndWait();
CmdOK.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent ev){
Window.close();
}
});
return Window;
}
}
Here is the code that calls the Message Box Class:
CmdUpdate.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent ev){
new MsgBox().MessageBox("Hello", "Hello World");
}
});
Calling Stage#showAndWait waits until the stage closes before returning, so in fact the next line never gets a chance to run.
Move the line
Window.showAndWait();
to be the last in the method (crucially - after you set the handler to allow closing the stage), or else just use Stage#show, and your problem should be solved.

Resources