Text with Icon on MenuItem JavaFx - java

I am working on a project using JavaFX. I have included FontAwesome in my project to avoid using images for simple icons. I created the following function in a constants class that generates an HBox with an icon and text that will be called in the setGraphic(Node node). The function is as follows:
public static HBox iconText(String icon, String text) {
return ConstantsClass.iconText(icon, text, 5);
}
public static HBox iconText(String icon, String text, int spacing) {
HBox box = new HBox(spacing);
Label iconLabel = new Label(icon);
iconLabel.setFont(ConstantsClass.fontAwesome);
Label textLabel = new Label(text);
box.getChildren().addAll(iconLabel, textLabel);
return box;
}
The method works perfectly on buttons, such as having a back button with an arrow icon. However it does not seem to work on MenuItems.
I have a menu bar at the top of my application with Menus in them, and MenuItems in those. I tried the same process with a "settings" MenuItem, but the text does not appear unless the cursor is over the item.
MenuItem settings = new MenuItem();
settings.setGraphic(ConstantsClass.iconText(FontAwesome.COG, "Settings")); //Obscuring name of Constants Class
This code has the following results:
When the user just clicks on the menu drop down
When the user hovers over the Menu Item
How can I make the MenuItem always show the icon and text?

It's kind of weird it looks like a bug. If there is no label in the graphic, the graphic seems to display OK (for instance a rectangle seems to work correctly as a graphic). My guess is it is some kind of mess-up in the interaction between the CSS styling rules and the menu skin implementation.
A workaround is to use a snapshot, but that somehow makes the thing being snapshot slightly bold in appearance.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class MenuDisplay extends Application {
public void start(Stage stage) throws Exception {
Label label = new Label("(*)");
label.setStyle("-fx-background-color: null;");
Scene dummyScene = new Scene(label, Color.TRANSPARENT);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image snapshot = label.snapshot(params, null);
ImageView imageView = new ImageView(snapshot);
Menu menu = new Menu("Choices");
menu.getItems().addAll(
new MenuItem("Broken Label Graphic", new Label("(*)")),
new MenuItem("OK Rect", new Rectangle(16, 16, Color.FORESTGREEN)),
new MenuItem("Fixed Snapshot", imageView)
);
MenuBar menuBar = new MenuBar(menu);
Scene scene = new Scene(
new VBox(menuBar), 100, 100
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maybe somebody else can come up with a better workaround (or fix).

Related

TabPane is not working on my Application

I have a project from uni where I have to make an application with Java (in a model view controller format), and I want to make tabs in my application, but it doesn't seem to be working.
I looked up a lot of tutorials, and they all tell me the same way of how to use TabPane, but it doesn't work for me.
Here is the code I have in my Application Loader class:
package main;
import controller.ModuleChooserController;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
import model.StudentProfile;
import view.ModuleChooserRootPane;
public class ApplicationLoader extends Application {
private ModuleChooserRootPane view;
#Override
public void init() {
//create model and view and pass their references to the controller
StudentProfile model = new StudentProfile();
view = new ModuleChooserRootPane();
new ModuleChooserController(view, model);
}
#Override
public void start(Stage stage) throws Exception {
//whilst you can set a min width and height (example shown below) for the stage window,
//you should not set a max width or height and the application should
//be able to be maximised to fill the screen and ideally behave sensibly when resized
stage.setMinWidth(530);
stage.setMinHeight(500);
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
stage.setTitle("Final Year Module Chooser Tool");
stage.setScene(new Scene(view));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have the TabPane implemented, but nothing comes up. I also tried implementing the TabPane in my "view" package, but I had no luck there either.
Here is the code for the ModuleRootChooserPane:
package view;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
//You may change this class to extend another type if you wish
public class ModuleChooserRootPane extends BorderPane {
private ButtonPane bp;
private ProfileCreator profileCreator;
public ModuleChooserRootPane() {
//This sets the colour of background
this.setStyle("-fx-background-color: #EDF1F3;");
//Creates a new instance of the buttonPane (Used from ButtonPane.java) and ProfileCreator
bp = new ButtonPane();
profileCreator = new ProfileCreator();
//This adds the padding on the left so that "submit" button is in line with text fields
bp.setPadding(new Insets(0, 0, 0, 120));
//Creates a new VBox which adds the ProfileCreator and the button pane
VBox rootContainer = new VBox(profileCreator, bp);
rootContainer.setPadding(new Insets(100,100,100,100));
this.getChildren().add(rootContainer);
}
}
You choose to work without fxml file, so you need to create your view into the class ModuleChooserRootPane, every graphic element have to be here, or in other classes used here.
So you have to add your TabPane in its constructor too :
public ModuleChooserRootPane() {
...
//this.getChildren().add(rootContainer);
setLeft(rootContainer); // or Top/Bottom/Right/Center
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
setCenter(tabPane); // or Top/Bottom/Right/Left
}
A BorderPane is good idea for root element because it's has several zones to add element, but fo sor you need to use setLeft(), setRight(), setCenter(), setTop() and setBottom() rather than just getChildren().add() where you cannot control the place
Example for adding content in the different tabs :
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tab.setContent(new VBox(new Label("Here is the testing place"), new Circle(15, 12, 10)));
Tab tab2 = new Tab("Testing2");
HBox hboxContentTab2 = new HBox();
hboxContentTab2.getChildren().add(new Ellipse(10, 10, 10, 13));
hboxContentTab2.getChildren().add(new Label("Here is the BIS testing place"));
tab2.setContent(hboxContentTab2); // add a Node created before, ot can be whatever you wan, borderpane, gridpane, hbox, vbox, label ...
tabPane.getTabs().addAll(tab, tab2);

JavaFX create popover without external libraries

How can I create a popover like in the DatePicker class from javafx.scene.control.DatePicker as seen here:
The popover should when displayed be on top of all the other components as seen here (the popover is above the TextField):
Found a pretty simple solution to my problem, here is a code snippet in case people encounter the same problem
package main;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane rootPane = new BorderPane();
MenuButton openButton = new MenuButton("Open Context Menu");
BorderPane contentPane = new BorderPane();
CustomMenuItem item = new CustomMenuItem(contentPane);
openButton.setStyle("-fx-selection-bar: transparent;"); //this is optional. it makes the blue background that appears when something is focused transparent
contentPane.setPrefSize(300, 300);
Label text = new Label("The ContextMenu will only close when you click the\nbutton below OR click outside of the ContextMenu.\nHow neat is that?");
text.setStyle(" -fx-text-fill: -fx-text-base-color;"); //needs to bet set if you want the selection-bar to be transparent. if not set the text will become invisible
contentPane.setTop(text);
Button closeButton = new Button("Close this popover");
closeButton.setOnAction(x -> {
openButton.hide();
});
contentPane.setBottom(closeButton);
item.setHideOnClick(false); // this will stop the ContextMenu from being hidden when clicking inside of it.
openButton.getItems().add(item);
rootPane.setCenter(openButton);
Scene scene = new Scene(rootPane, 550, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I just placed a Pane with all of my content inside of a CustomMenuItem and added that CustomMenuItem to my MenuButton.

A Menu that triggers purely on hover in JavaFX?

I have looked at a few JavaFX classes related to menu, but I could not find what I want. I want to implement something that works like a MenuBar, except that I want it to open its menu on hover, not on click. While I can definitely build something from scratch on my own, it would be a waste of time if there is already something that allows me to do something similar to that. Anyone knows how do I do that?
Well it is possible to make it if you use the lookup(). You can easily take the container holding the Menus and then apply on them the setOnMouseEntered() or setOnMouseExited() to catch mouse events on each Menu.
Here is an example :
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane mainPane = new BorderPane();
MenuBar menuBar = new MenuBar();
Menu editMenu = new Menu("File");
MenuItem fileMenuItem1 = new MenuItem("Item 1");
MenuItem fileMenuItem2 = new MenuItem("Item 2");
MenuItem fileMenuItem3 = new MenuItem("Item 3");
editMenu.getItems().addAll(fileMenuItem1, fileMenuItem2, fileMenuItem3);
Menu fileMenu = new Menu("Edit");
MenuItem fileMenu1 = new MenuItem("Item 1");
MenuItem fileMenu2 = new MenuItem("Item 2");
MenuItem fileMenu3 = new MenuItem("Item 3");
fileMenu.getItems().addAll(fileMenu1, fileMenu2, fileMenu3);
menuBar.getMenus().addAll(editMenu, fileMenu);
mainPane.setTop(menuBar);
Scene scene = new Scene(mainPane, 300, 300);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
// Find menubar HBox holding the menus
HBox container = (HBox) menuBar.lookup("HBox");
for(int i = 0 ; i < container.getChildren().size() ; i++) {
Node parentNode = container.getChildren().get(i);
Menu menu = menuBar.getMenus().get(i);
parentNode.setOnMouseMoved(e->{
menu.show();
});
}
}
public static void main(String[] args) {
launch(args);
}
}
You might wonder why i use the setOnMouseMoved instead of setOnMouseEntered. Well that's because the setOnMouseEntered creates a strange behavior while moving the mouse between menus and the usage of setOnMouseMoved fix it.
In addition in case you wondering how did I know to use the lookup("HBox") the truth is my first attempt was System.out.println( menuBar.lookupAll("*")); which gave me the current informations :
[MenuBar#5e57a6ce[styleClass=menu-bar], HBox#13ccf856[styleClass=container], MenuBarSkin$MenuBarButton[id=null, styleClass=menu-button menu]'File', MenuButtonSkinBase$MenuLabeledImpl#25917f7f[styleClass=label]'File', Text[text="File", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=12.0], fontSmoothingType=LCD, fill=0x333333ff], StackPane#c341eae[styleClass=arrow-button], StackPane#7b8501c0[styleClass=arrow], MenuBarSkin$MenuBarButton[id=null, styleClass=menu-button menu]'Edit', MenuButtonSkinBase$MenuLabeledImpl#4d8224b7[styleClass=label]'Edit', Text[text="Edit", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL_VERTICAL_CENTER, font=Font[name=System Regular, family=System, style=Regular, size=12.0], fontSmoothingType=LCD, fill=0x333333ff], StackPane#3d75fcac[styleClass=arrow-button], StackPane#71f02eed[styleClass=arrow]]
I think you are looking for class javafx.scene.control.ContextMenu and the method show(Node anchor, double screenX, double screenY)

JavaFX: Hardcode a "Ctrl+C" keypress

I have a JavaFX application that has various TextField widgets in the main frame. I have a MenuBar that includes the MenuItem objects "Copy" and "Paste" like a standard production application would have. Since any or none of the various TextField objects could be selected at any given time, it seems easier to just hardcode a "Ctrl+C" or "Ctrl+V" key press in the setOnAction events of the "Copy" and "Paste" MenuItem objects rather than use a Clipboard object and loop iterating through all TextFields to find the highlighted text (if any).
Is there a way to hardcode this key press action in Java? I looked into the KeyCombination class but it does not actually trigger the action described by the given key combination.
I think by "Since any or none of the various TextField objects could be selected at any given time" you are referring to which (if any) text field has the keyboard focus.
You can easily get this information from the scene: just do
Node focusOwner = scene.getFocusOwner();
if (focusOwner instanceof TextField) {
TextField textField = (TextField) focusOwner ;
String selectedText = textField.getSelectedText();
// ...
}
Note also that TextInputControl defines a copy() method that copies the selected text to the system clipboard. (Similarly, there's a paste() method too.) So you can leverage those to make the functionality easy.
Here's a SSCCE:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FocusMenuTest extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
VBox textFields = new VBox(5, new TextField("One"), new TextField("Two"), new TextField("Three"));
MenuBar menuBar = new MenuBar();
Menu edit = new Menu("Edit");
MenuItem copy = new MenuItem("Copy");
copy.setOnAction(e -> {
Node focusOwner = menuBar.getScene().getFocusOwner();
if (focusOwner instanceof TextInputControl) {
((TextInputControl)focusOwner).copy();
}
});
MenuItem paste = new MenuItem("Paste");
paste.setOnAction(e -> {
Node focusOwner = menuBar.getScene().getFocusOwner();
if (focusOwner instanceof TextInputControl) {
((TextInputControl)focusOwner).paste();
}
});
menuBar.getMenus().add(edit);
edit.getItems().addAll(copy, paste);
root.setCenter(textFields);
root.setTop(menuBar);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

toggle sliders on/off with radio button in javafx

I want to toggle off sliders when a radiobutton is pressed, and toggle them on when the other is pressed:
Anyways, when I click on toggle off I want the label, sliders and textfields to not be able to be selected. and when you click on Toggle on you can select the sliders etc again.
I get that I need to use ToggleGroup, but not sure how I would go about toggling off the sliders.
I'd probably use a single CheckBox for this UI rather than multiple radio buttons, then you can just bind your slider pane's disable property to the selected property of the CheckBox, but I'll just give an answer here for the UI you have displayed.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ToggleSetup extends Application {
#Override
public void start(Stage stage) throws Exception {
RadioButton on = new RadioButton("on");
RadioButton off = new RadioButton("off");
ToggleGroup toggleState = new ToggleGroup();
on.setToggleGroup(toggleState);
off.setToggleGroup(toggleState);
toggleState.selectToggle(on);
VBox sliderPane = new VBox(
10,
new Slider(),
new Slider(),
new Slider()
);
sliderPane.disableProperty().bind(
Bindings.equal(off, toggleState.selectedToggleProperty())
);
VBox layout = new VBox(10, on, off, sliderPane);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) { launch(args); }
}

Categories

Resources