Cannot add CSS classes to my JavaFX Label - java

I am making a simple todolist app. Here is a watered-down version of it:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
ListView<Label> todoListView = new ListView();
Label doneTodo = new Label ("This todo is meant to be finished and struck through");
doneTodo.getStyleClass().add("done"); // Add the "done" class to this to-do
Label undoneTodo = new Label("This todo is meant to be undone and therefore isn't struck through");
// Add both to-dos to the listview
addTodo(doneTodo, todoListView);
addTodo(undoneTodo, todoListView);
// Set the listview as the scene, and add the stylesheet
Scene scene = new Scene(todoListView, 600, 550);
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm());
primaryStage.setTitle("Label not taking on Strikethrough");
primaryStage.setScene(scene);
primaryStage.show();
}
// Adds the to-do (in this case just a simple Label) to the listview
private static void addTodo(Label todo, ListView<Label> todoList) {
todoList.getItems().add(todo);
}
public static void main(String[] args) {
launch(args);
}
}
Here is my CSS class:
.done {
-fx-strikethrough: true;
}
When I run the code, the -fx-strikethrough property does not show up on my Label. Note that although this is a simplified version of my app, the issue remains the same: The text inside the JavaFX Label is not being struck through.
Again, sorry for any inadequacies in my question. I am fairly new to Stack Overflow!
Thanks in advance.

The CSS rule should apply to the text node under the label node:
.done .text {
-fx-strikethrough: true;
}

Related

JavaFX: Button event still executes without having to press the button for a second time

my problem is the following:
I have a JavaFX application with a Button called "bindB";
Button bindB = new Button("None");
bindB.setOnAction(event -> {
bindB.setText("...");
BindKey.bindKey(scene, bindB);
});
with the text "None". Is this button pressed, his text first changes to "..."
and by then calling the method "BindKey.bindKey();", the text will change to the
name of the key, the user is pressing on his keyboard.
This is the code of the method "BindKey.bindKey();":
public static void bindKey(Scene scene, Button bindB){
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
});
}
As you can see, in the args of the method we give the button "bindB", so that the method knows
what button to change the name of, aswell as the current scene.
This code does work, but the problem is, that even after the button was
pressed, and its text has already be changed, the text still changes to the name of different
keys if you press them afterwards WITHOUT having to press the button a second time.
I thought that you had to end the "setOnAction" event by calling
event.consume();
but that didnt work...
So how do I make the buttons text only change, if the button has actually been pressed a second or third time?
Otherwise, the task which the button performs is toggled by EVERY key because technically
every key is the toggle key as the task reads the name of the button to know what key is for toggling.
Full code example:
Main class:
public class Main {
// Initialize GUI
public static void main(String[] args) {
GUI gui = new GUI();
gui.run();
}
}
GUI class:
public class GUI extends Application {
public void run() {
launch();
}
#Override
public void start(Stage window) throws Exception {
// When closing Window
window.setOnCloseRequest(event -> {
exitApplication(window);
});
// GridPane
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10,10));
grid.setVgap(15);
grid.setHgap(30);
// Scene
Scene scene = new Scene(grid, 200, 200);
window.setScene(scene);
// Bind Button
Button bindB = new Button("None");
GridPane.setConstraints(bindB, 1, 1);
bindB.setOnAction(event -> {
bindB.setText("...");
BindKey.bindKey(scene, bindB);
});
// Add to Grid
grid.getChildren().addAll(bindB);
// Show Window
window.show();
}
// Provide a clean exit
private void exitApplication(Stage window){
window.close();
Platform.exit();
System.exit(0);
}
}
BindKey class:
public class BindKey {
// Changes Buttons text
public static void bindKey(Scene scene, Button bindB){
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
});
}
}
I am not to 100% sure if this is the problem, but I think the only thing you have to do is the following:
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
scene.setOnKeyPressed(null);
});
}
You just have to remove the Key-Listener from the scene after you set the text. Because otherwise it will listen for keys the entire time.

Change the property of a rectangle by using another class JavaFX

I'm working on a project and I want a rectangle to appear when I press a button. However, I want to do this by directing the button click to a different class. Here is what I've tried:
Here is my first class, "Main"
static boolean btnClicked = false;
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Make Popup Visible");
Rectangle menu = new Rectangle(40,40,200,200);
menu.setFill(Color.BLACK);
menu.setOpacity(0);
btn.addEventHandler(MouseEvent.MOUSE_CLICKED,(MouseEvent e) ->{
AddRect.showMenu();
});
if(btnClicked == true) {
menu.setOpacity(1);
}
Group root = new Group();
root.getChildren().addAll(btn, menu);
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
And my second class, "AddRect"
public class AddRect {
static void showMenu() {
Main.btnClicked = true;
}
}
However, this isn't working, and I don't know why. Can somebody help me? I don't even know if this is the best way to do it (Using two classes), but if there is a better way please let me know. Thanks in advance!
I figured it out! I just had to move the conditional inside the EventHandler
btn.addEventHandler(MouseEvent.MOUSE_CLICKED,(MouseEvent e) ->{
AddRect.showMenu();
if(btnClicked == true) {
menu.setOpacity(1);
}
});

Refactor JFoenix JFXDialogLayout alert notification code to a simpler form so that it can be reused for other classes

how can I refactor the following code so that only the code in deleteButton.setOnAction(deleteEvent -> {//only this code varies} changes. Everything else will stay the same but the block of code in the lambda expression varies from time to time when I call the class from another class. The block of code that goes through the lambda expression is supposed to be a void method.
public class A {
public void test() {
// ensure that user can't close the alert
Stage primaryStage = (Stage) RootLayoutController.getRootLayout().getScene().getWindow();
JFXAlert<javafx.scene.control.ButtonType> alert = new JFXAlert<>(primaryStage);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setOverlayClose(false);
//create font awesome icon
String ICON = "\uf071";
Label labelIcon = new Label(ICON);
labelIcon.setStyle("-fx-font-family: 'FontAwesome'; -fx-font-size: 60px; -fx-text-fill: #D34336;");
labelIcon.setPadding(new Insets(0,5,0,0));
// Create the content of the JFXAlert with JFXDialogLayout
JFXDialogLayout layout = new JFXDialogLayout();
Label labelHeading = new Label("Alert Notification");
Label labelBody = new Label("Are you sure you want to delete this?");
layout.setHeading(labelHeading);
layout.setBody(new VBox(new HBox(labelIcon, labelBody)));
// Buttons get added into the actions section of the layout.
JFXButton deleteButton = new JFXButton("Delete");
deleteButton.setDefaultButton(true);
deleteButton.setOnAction(deleteEvent -> {
//only this block of code changes
alert.hideWithAnimation();
});
JFXButton cancelButton = new JFXButton("Cancel");
cancelButton.setCancelButton(true);
cancelButton.setOnAction(closeEvent -> alert.hideWithAnimation());
layout.setActions(deleteButton, cancelButton);
alert.setContent(layout);
alert.showAndWait();
}
}
It is not entirely clear from your question what you are trying to accomplish, but I will take a wild stab at it.
If you are looking to be able to pass a code block to the deleteButton.setOnAction() method, you could use an Interface and pass implementations of that interface to the A class. Then just pass that reference to an internal method for the onAction lambda.
Here is a very quick example of how you could do something like this:
Main.java:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Action button
Button btnDoSomething = new Button("Do something...");
btnDoSomething.setOnAction(e -> doTheThings(new ImplDoSomething()));
Button btnDoSomethingElse = new Button("Do something else...");
btnDoSomethingElse.setOnAction(e -> doTheThings(new ImplDoSomethingElse()));
VBox mainPane = new VBox(5);
mainPane.setAlignment(Pos.CENTER);
mainPane.setPadding(new Insets(10));
mainPane.getChildren().addAll(btnDoSomething, btnDoSomethingElse);
primaryStage.setScene(new Scene(mainPane));
primaryStage.show();
}
private void doTheThings(IParameterMethod parameterMethod) {
parameterMethod.call();
}
}
The IParameterMethod.java Interface:
public interface IParameterMethod {
void call();
}
Then you can create as many classes as you like that implement that interface, each with their own call() method, allowing you to execute different code.
ImplDoSomething.java
public class ImplDoSomething implements IParameterMethod {
#Override
public void call() {
System.out.println("Doing something!");
}
}
ImplDoSomethingElse.java:
public class ImplDoSomethingElse implements IParameterMethod {
#Override
public void call() {
System.out.println("Doing something else!");
}
}
This should be easily adapted for your project.

How can I change the GUI of JavaFX outside start()?

I am crazy about the feature of JavaFX, in Swing, I could do,
#Override
public void onPluginRegistered(final GamePlugin plugin) {
JRadioButtonMenuItem gameMenuItem = new JRadioButtonMenuItem(plugin.getGameName());
gameMenuItem.setSelected(false);
gameMenuItem.addActionListener(event -> {
if (core.getPlayers().isEmpty()) {
// Can't start a game with no players.
showErrorDialog(frame, ERROR_NO_PLAYERS_TITLE, ERROR_NO_PLAYERS_MSG);
gameGroup.clearSelection();
} else {
core.startNewGame(plugin);
}
});
gameGroup.add(gameMenuItem);
newGameMenu.add(gameMenuItem);
}
if I want to add a radio item whenever a plugin has registered.
However in JavaFX, it seems, you can't declare any global item of JavaFX, because once the start() is called, it starts a new constructor and everything you've done before is nothing (there is no variable share to me).
Here is my Javafx code.
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 500, 500);
scene.getStylesheets().add("./Buttons.css");
Region spacer = new Region();
spacer.setMinWidth(10);
primaryStage.setScene(scene);
primaryStage.show();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
Tab tabDisplay = new Tab("Visualize your data");
tabPane.getTabs().add(tabDisplay);
pluginGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>(){
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle old_toggle, Toggle new_toggle) {
if (pluginGroup.getSelectedToggle() != null) {
RadioButton chk = (RadioButton) new_toggle.getToggleGroup().getSelectedToggle();
chk.getText();
}
}
});
root.setCenter(tabPane);
FlowPane inputPanel = new FlowPane();
TextField source = new TextField ();
Button confirmButton = new Button("Get Your Resource!");
confirmButton.getStyleClass().add("GREEN");
inputPanel.getChildren().addAll(new Label("Input your source:"),
spacer, source, confirmButton);
root.setBottom(inputPanel);
RadioButton defaultBtn = new RadioButton("No data plugin are registered");
FlowPane pane = new FlowPane();
pane.getChildren().addAll(new Label("Select your data source"), spacer);
if (radioButtonBox != null) {
pane.getChildren().add(radioButtonBox);
}
tabData.setContent(pane);
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
public void caller(String[] args) {
launch(args);
}
I want to initialize the javafx program from,
public static void main(String[] args) throws Exception {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
gui.caller(args);
core.registerPlugin(new CsvData());
}
It is weird that I can't add any radio button to the existing radioButtonBox every time I call onPluginRegistered(DataPlugin plugin) (The new radiobutton does not show up)
You should consider the start() method as the replacement for the main method. If your application needs access to some kind of service or model, create it in the start() (or init()) method. I would actually recommend making the Application subclass (which is inherently not reusable) as minimal as possible - it should just do the startup work - and factoring the remaining GUI code into a separate class. (If you use FXML, the FXML file can define the UI, and the Application subclass is then already pretty minimal: it just loads and displays the FXML.)
You haven't really provided enough context to make it clear what's going on here, but I'm guessing GuiFramework is the Application subclass you've shown part of, and DataFramework is an interface of some kind. I also assume GuiFramework is implementing some interface that defines the onPluginRegistered method.
So I would do:
public class GuiFramework implements PluginAware {
private final BorderPane root ;
private final DataFramework dataFramework ;
public GuiFramework(DataFramework dataFramework) {
this.dataframework = dataFramework ;
this.root = new BorderPane();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
// etc etc (remaining code from your start() method)
}
public Parent getView() {
return root ;
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
}
and define a Main class for starting the application:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
Scene scene = new Scene(gui.getView(), 500, 500);
scene.getStylesheets().add("./Buttons.css");
primaryStage.setScene(scene);
primaryStage.show();
core.registerPlugin(new CsvData());
}
// for environments not supporting JavaFX launch automatically:
public static void main(String[] args) {
launch(args);
}
}

Text box with data validation

I want to ask the user for their gender. I want to create a text box that they can answer the question in. The do-while loop is to ensure they answer with either "boy" or "girl". There are no errors but it won't run.
Note I have all the necessary imports...
public class Culminating_JavaFX extends Application {
String gender;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
TextField textField = new TextField ();
do
{
textField.setPromptText("Are you a boy or a girl?");
textField.setText("");
gender = br.readLine().toLowerCase();
}
while (!(gender.equals("boy")) && !(gender.equals("girl")));
GridPane.setConstraints(textField, 0, 1);
grid.getChildren().add(textField);
}
public static void main(String [] args) {
launch(args);
}
}
public class Culminating_JavaFX extends Application {
private GridPane grid = new GridPane();
private TextField textField = new TextField();
private Label label = new Label("Are you boy or girl?");
private Button btn;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
btn = new Button();
btn.setText("Answer");
// set action listener -> runs when button is pressed
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// process the form
process();
}
});
// set constraints
GridPane.setConstraints(textField, 0, 0);
GridPane.setConstraints(label, 0, 1);
GridPane.setConstraints(btn, 0, 2);
// add components to grid
grid.getChildren().add(textField);
grid.getChildren().add(label);
grid.getChildren().add(btn);
// show scene
primaryStage.setScene(new Scene(grid, 300, 250));
primaryStage.show();
}
private void process() {
// get text
String text = textField.getText();
// process text
if (text.equals("boy")) {
label.setText("You are a boy.");
} else if (text.equals("girl")) {
label.setText("You are a girl.");
}
}}
image of required imports
I wrote a short example, please check it above. Your program goes into do-while loop and stays there. It never gets to the point where it would draw the window and components. That's why it doesn't run.
On a side note, make sure from now on you try to keep your logic code and your graphical user interface code as separate as possible. Never try to cram everything into the GUI class.
Next thing is that the general idea of GUI's is that their logic can't be tied up in loops before being ran. When your program runs and calls start(), it will proceed downwards and execute code and needs to hit a line called window.show();. This displays the window to the user. If your program is stuck in that loop above, it won't ever be able to even display the GUI to the user and thus won't work.
Instead, rethink how your program will work. Since the user needs to select boy or girl, why not use a ChoiceBox for that, or better yet, a RadioButton. Have the user select the choice they want, then perhaps have a Button for them to click to submit or have the ChoiceBox or RadioButton listen for changes by calling:
yourRadioButton.setOnAction(e ->
{
/*
* Set the Boy Girl value here by calling
* yourRadioButton.getValue()
*/
}

Categories

Resources