how to actually hide Tab from TabPane with JavaFX - java

previously I was working on Java Swing and now I'm trying to work with JavaFX. My Java Swing code last time:
//These line of code is to call method that declared in ContentPage.java
contentPage.adminFeatureEnabled(adminEnabled);
contentPage.managerFeatureEnabled(managerEnabled);
and in my ContentPage.java
//By default, all feature (or tab) are enabled.
//This method is to remove register account if the user login into the system is manager and staff
public void adminFeatureEnabled(boolean a) {
if (!a) {
tabPane.removeTabAt(tabPane.indexOfComponent(registerAccount));
}
}
//This method is to remove register account and purchase order if the user who log into the system is staff
public void managerFeatureEnabled(boolean a) {
if(!a) {
tabPane.removeTabAt(tabPane.indexOfComponent(purchaseOrder));
}
}
and in my code:
if (role.equals("admin")){
contentPage.contentFrame.setTitle("Menu - Admin!");
contentPage.disUser.setEditable(true);
contentPage.chgRoles.setEnabled(true);
} else if(role.equals("manager")){
contentPage.contentFrame.setTitle("Menu - Manager!");
contentPage.chgRoles.setSelectedItem("manager");
adminEnabled = false;
}else if (role.equals("staff")){
contentPage.contentFrame.setTitle("Menu - Staff!");
contentPage.chgRoles.setSelectedItem("staff");
adminEnabled = false;
managerEnabled = false;
}
The code above will perform like this:
when the user login with admin account, all the feature (Tab) enabled
when the user login as manager, some feature (tab) will be hide
My current problem now:
I wanted the same feature as above in JavaFX but I don't know how as none of the method works as I wanted.
anyone can help me with this?

Simply modify the tabs list:
The following example adds/removes Tabs, when the CheckBoxes are (un)selected.
#Override
public void start(Stage primaryStage) {
Tab tab1 = new Tab("Tab 1", new Label("1"));
Tab tab2 = new Tab("Tab 2", new Label("2"));
TabPane tabPane = new TabPane();
tabPane.setPrefSize(400, 400);
CheckBox cb1 = new CheckBox("1");
CheckBox cb2 = new CheckBox("2");
cb1.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
tabPane.getTabs().add(0, tab1);
} else {
tabPane.getTabs().remove(tab1);
}
});
cb2.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
tabPane.getTabs().add(tab2);
} else {
tabPane.getTabs().remove(tab2);
}
});
Scene scene = new Scene(new VBox(new HBox(cb1, cb2), tabPane));
primaryStage.setScene(scene);
primaryStage.show();
}

It's been so long since the question asked, but this maybe helpful for someone.
You can try something like this.
you have a tabPane with three tabs tabOne, tabTwo and tabThree.
position index of tabs
tabOne - 0
tabTwo - 1
tabThree - 2
to hide tabTwo, you can use remove function and again reappear you can use set function.
to remove tab
tabPane.getTabs().remove(tabTwo);
set again with the relevant index to display at the correct location.
tabPane.getTabs().set(1, tabTwo);

Related

Is it possible to return a variable in javaFX from a close method? Is there a more efficient way to do this?

I would like to run a method that would close a pop up window when an answer is selected or the window is closed manually and then return the value selected (a boolean). Unfortunately, I do not know how I would retreive the data because I initially call the method that displays the query, not the close method.
I have tried many different things but when I try to use a close method I am unable to retrieve the data returned from it. Otherwise, the data returns before the user is able to mutate it. Neither result achieves my goal
This is my method that creates the box. I currently just close the window instead of closing via an alternate closing method.
public class ConfirmBox
{
static boolean answer;
public static boolean display(String title, String question) {
Stage window = new Stage();
//Block events to other windows
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(title);
window.setMinWidth(250);
//LabelQ
Label label = new Label();
label.setText(question);
//YesBox
Button yesB= new Button("Yes");
yesB.getStyleClass().add("button-green");
yesB.setOnAction(e -> {
answer=true;
window.close();
});
//NoBox
Button noB= new Button("No");
noB.getStyleClass().add("button-red");
yesB.setOnAction(e -> {
answer=false;
window.close();
});
window.setOnCloseRequest(e ->{
e.consume();
window.close();
});
VBox layout = new VBox(10);
layout.getChildren().addAll(label, yesB, noB);
layout.setAlignment(Pos.CENTER);
//Display window and wait for it to be closed before returning
Scene scene = new Scene(layout, 800, 200);
scene.getStylesheets().add("styles.css");
window.setScene(scene);
window.showAndWait();
return answer;
}
}
This is my method that runs everything thus far.
public class Main extends Application
{
Stage window;
String gender;
String name;
public static void main (String[] args){
launch (args);
}
public void start(Stage primaryStage) throws Exception{
window= primaryStage;
window.setTitle("Pokemon Gray");
GridPane grid= new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(8);
grid.setHgap(10);
// Name Label
Label nameLabel= new Label("UserName");
GridPane.setConstraints(nameLabel,0, 0);
//Name input
TextField nameInput = new TextField();
GridPane.setConstraints(nameInput,1, 0);
//GenderLabel
Label genderLabel= new Label("Gender");
GridPane.setConstraints(genderLabel,0, 1);
//Gender Boy
Button boySelect= new Button("Boy");
boySelect.getStyleClass().add("button-blue");
GridPane.setConstraints(boySelect, 1, 1);
boySelect.setOnAction(e-> gender="Male");
//Gender Girl
Button girlSelect= new Button("Girl");
girlSelect.getStyleClass().add("button-red");
GridPane.setConstraints(girlSelect, 2, 1);
girlSelect.setOnAction(e-> gender="Female");
//Login
Button saveName= new Button("Save Name");
GridPane.setConstraints(saveName, 1, 2);
saveName.setOnAction( e -> {
boolean valid =verifyText(nameInput.getText());
if ((valid)&&((gender == "Male")||(gender=="Female"))){
String qStr= "Are you sure your name is "+nameInput.getText()+" and you are a "+gender+"?";
boolean confirmed= ConfirmBox.display("Confirmation", qStr );
if (confirmed == true){
name= nameInput.getText();
}
}
else{
ErrorBox.display("Error", "You must enter a name less than 20 characters long and a gender");
}
});
grid.getChildren().addAll(nameLabel, nameInput, genderLabel, boySelect, girlSelect, saveName);
Scene scene= new Scene(grid, 1000, 1000);
scene.getStylesheets().add("styles.css");
window.setScene(scene);
window.show();
}
public boolean verifyText(String text){
boolean valid;
if ((text.length() > 0) && (text.length() <= 20)){
valid= true;
}
else{
valid=false;
}
return valid;
}
}
I want to be able to run a seperate method that closes it and returns the answer boolean in the ConfirmBox to the Main method. But currently it returns null because it returns immediately.
Edit: It should be noted I currently do not have a close method. I deleted my previous one because it caused many issues.
I think that you can't do that, you need to do another screen like:
If(button.getResult==yes){"charge yesscreen")else{"charge no screen"}
The controllers for differents screens can't interectionate with another screens, you only can call the differents screens from to a controller but you can't pass values with hims, try do something like this I work with javafx with this mentality all screens are independents and you only invoce the differents screens . you can do something like this:
class first screen{
is a girl?
is a charizar?
is a x)
if(isgirl && is a x...){
call screen x
}else if(ismen&& is a z..){
call screen z
}
{

How to implement CAPS LOCK alert bubble on password field in JavaFX?

I’m trying to implement a caps lock alert on password field. If caps lock is ON then the bubble will appear below the password field. I’ve searched a lot but didn’t get any solution that how can I implement such bubble on input fields in JavaFX. I’ve found some source code to get the caps lock state.
boolean isOn=Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
scene.setOnKeyReleased( event -> {
if ( event.getCode() == KeyCode.CAPS ) {
System.out.println("Capslock pressed");
System.out.println("Capslock state: " + isOn);
}
});
But my problem is how to implement the bubble alert on text field.
Here you can see what I have to do.
It would be helpful if you suggest me some possible ways as I’m new in JavaFX. Is there any JavaFX library to do such bubble alert on input fields?
It sounds like you have figured out how to get the input state you could try something like this for the listener
public class Main extends Application {
private Label capsLabel = new Label("Caps is ON");
private boolean capsIsOn;
#Override
public void start(Stage stage) {
System.out.println(Toolkit.getDefaultToolkit().getLockingKeyState(20));
//Try adding this line to get state on startup
capsLabel.setVisible(Toolkit.getDefaultToolkit().getLockingKeyState(20));
TextField textField = new TextField();
//Also try adding this line and to check again so when the field
//is selected it will check again
textField.setOnMouseClicked(event -> capsLabel.setVisible(Toolkit.getDefaultToolkit().getLockingKeyState(20)));
textField.setOnKeyReleased(keyEvent -> {
if(keyEvent.getCode().toString().equals("CAPS")){
capsIsOn = !capsIsOn;
capsLabel.setVisible(capsIsOn);
}
});
VBox vBox = new VBox();
vBox.getChildren().addAll(textField, capsLabel);
stage = new Stage();
stage.setScene(new Scene(vBox));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Alternatively you could set this on a timer and have it constantly checking personally I don't like the idea of constant use of computer resources but its not my project.
JavaFX doesn’t have any way to detect CapsLock. In theory, you could install a Scene-wide listener, but that wouldn’t catch when the state changes while other applications have focus.
Mixing AWT/Swing and JavaFX is perilous, because each has its own thread on which nearly all of its methods must be executed. Since CapsLock needs to be polled anyway, it makes sense to use javax.swing.Timer, which both executes an action regularly and ensures that action is run in the proper thread (the AWT event dispatch thread):
BooleanProperty capsLockOn = new SimpleBooleanProperty();
EventQueue.invokeLater(() -> {
Timer timer = new Timer(500, e -> {
boolean state = Toolkit.getDefaultToolkit().getLockingKeyState(
KeyEvent.VK_CAPS_LOCK);
Platform.runLater(() -> capsLockOn.set(state));
});
timer.start();
Platform.runLater(() -> {
Window window = passwordField.getScene().getWindow();
window.setOnShown(e -> EventQueue.invokeLater(timer::restart));
window.setOnHidden(e -> EventQueue.invokeLater(timer::stop));
});
});
Region message = new BorderPane(new Label("Caps Lock is on"));
message.setStyle(
"-fx-background-color: #f4f4f4;" +
"-fx-border-color: black;" +
"-fx-border-width: 1px;" +
"-fx-padding: 1em 1em 0.75em 1em;" +
"-fx-shape: 'M 0 10 h 20 l 10 -10 l 10 10 h 150 v 90 h -190 z';"
);
Popup capsLockWarning = new Popup();
capsLockWarning.getContent().add(message);
capsLockOn.addListener((o, wasOn, on) -> {
if (on) {
Point2D location =
passwordField.localToScreen(-15, passwordField.getHeight());
capsLockWarning.show(passwordField,
location.getX(), location.getY());
} else {
capsLockWarning.hide();
}
});

How to hide TabPane content on Tab clicked in JavaFX

Here is a code:
package tabpane;
import javafx.application.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class HideShowTabContentOnClicked extends Application {
public static void main(String[] args) {
launch(args);
}
private BorderPane createContent() {
BorderPane borderPane = new BorderPane();
TabPane tabPane = new TabPane();
tabPane.setSide(Side.LEFT);
StackPane stackPane = new StackPane();
stackPane.setStyle("-fx-background-color: lightblue");
stackPane.setMinWidth(200);
Tab firstTab = new Tab("First");
firstTab.setClosable(false);
firstTab.setContent(stackPane);
firstTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
firstTab.setContent(null);
} else {
firstTab.setContent(stackPane);
}
});
Tab secondTab = new Tab("Second");
StackPane stackPane2 = new StackPane();
stackPane2.setStyle("-fx-background-color: yellow");
secondTab.setContent(stackPane2);
secondTab.setClosable(false);
secondTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
secondTab.setContent(null);
} else {
secondTab.setContent(stackPane2);
}
});
StackPane center = new StackPane();
center.setStyle("-fx-background-color: cyan");
borderPane.setCenter(center);
tabPane.getTabs().addAll(firstTab, secondTab);
borderPane.setLeft(tabPane);
return borderPane;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setMaximized(true);
stage.show();
}
}
Here I tried to solve a problem by using selectedProperty() by setting content to null, but it doesn't working, I want to make Tab like toggle button so that when I click on it showed and hide TabPanes content.
Before
And when clicked
As an example I want to implement TabPane like Intellij IDEA Tool Buttons (like "Project", "Structure" Tool Buttons etc).
If you are going to keep your content into StackPane, you can bind stackPane.visibleProperty() with toggleButton.selectedProperty():
stackPane.visibleProperty()
.bind(Bindings.when(toggleButton.selectedProperty())
.then(false)
.otherwise(true)
);
in this exampl: toggleButton.isSelected() --> !stackPane.isVisible() and !toggleButton.isSelected() --> stackPane.isVisible(),
or listen ToggleButton's events:
// toggleButton.setOnAction(e ->{ //new .setOnAction() -> Override previous
toggleButton.addEventHandler(ActionEvent.ACTION, e ->{ //can add any quantity for your needs
if(toggleButton.isSelected())
stackPane.setVisible(false);
else stackPane.setVisible(true);
});
But the problem is instead of toggle button I want to use Tab, so that it behaves like toggle button. i.e. when click "First Tab" in my example code if content visible it should be invisible and vice versa. I mean only tabs should be shown
I found solution.Tab does not have click-handler... but
Tab tab = new Tab();
tab.setContent(stackPane);
Label lable = new Label("Label"); //create Label
tab.setGraphic(lable); //set Lable as Graphic to Tab
lable.setOnMouseClicked(event ->{ //setOnMouseClicked, for example
if(stackPane.isVisible()){
stackPane.setVisible(false);
}else{
stackPane.setVisible(true);
}
});
, you can use Label(for example) as Tab-text and add setOnMouseClicked()-handler to Label. You can use any Node with Handler/ActionListener -> It's up to you.
For example, you can use CheckBox to show/hide StackPane, and Tab text (you can combine FXML and Java-code to produce graphics):
Tab tab = new Tab("Tab2"); //Tab with Text
tab.setContent(stackPane);
CheckBox checkBox = new CheckBox(); //create CheckBox
tab.setGraphic(checkBox); //set CheckBox as Graphic to Tab
stackPane.visibleProperty()
.bind(Bindings.when(checkBox.selectedProperty())
.then(false)
.otherwise(true)
);
or
#FXML
private Tab tab;
// ...
tab.setGraphic(checkBox);
// ...
I have came up with this solution:
AtomicReference<Tab> currentTab = new AtomicReference<>(tabPane.getSelectionModel().getSelectedItem());
AtomicReference<Tab> lastTab = new AtomicReference<>(null);
tabPane.setOnMouseReleased(event -> {
// Check if current node is actually tab
Node n = event.getPickResult().getIntersectedNode();
while (n != null && !(n.getStyleClass().contains("headers-region"))) {
n = n.getParent();
}
if (n == null)
return;
lastTab.set(currentTab.get());
currentTab.set(tabPane.getSelectionModel().getSelectedItem());
if (currentTab.get() == lastTab.get()) {
// Hide
tabPane.setPrefSize(28, 28);
//tabPane.getSelectionModel().clearSelection(); // notify selection model
currentTab.set(null);
} else {
// Show
tabPane.setPrefSize(-1,-1);
currentTab.set(tabPane.getSelectionModel().getSelectedItem());
}
});
First of all, I have added mouse event to the tabPane. Inside this mouse event, check if node under cursor is actually Tab node. If it is, do some logic to identify what user is trying to do: hide or show. Hiding is a bit tricky, so I ended up with setting preferred size of TabPane to 28 px wide.
I have also tried to notify selection model with an empty newValue:
tabPane.getSelectionModel().clearSelection();
But this it is not working properly. Calling select(-1) should call clearSelection(), but behavior is different somehow.
When I select another tab after calling clearSelection(), selection model handler called with oldValue == null, that possibly does not update internal index and tab does not swithes to selected one.

Double tab change event on tab closing in JavaFX

I have the following program:
public class MainClass extends Application {
public static void main(String[] arg) {
launch(arg);
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
System.out.println("Tab change: " + oldTab + "/" + newTab);
});
Tab tab = new Tab("Test tab");
tab.setOnCloseRequest((event) -> {
System.out.println("Removing tab");
event.consume();
//I need to remove tab manually
tabPane.getTabs().remove(tab);
});
System.out.println("Adding tab");
tabPane.getTabs().add(tab);
Group root = new Group(tabPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
When I run it and click close icon on Tab I have the following output of the program:
Adding tab
Tab change: null/javafx.scene.control.Tab#70b1aa69
Removing tab
Tab change: javafx.scene.control.Tab#70b1aa69/null
Tab change: null/javafx.scene.control.Tab#70b1aa69
As you see I get two Tab change events when I closing tab but I need only one. How to fix it?
Interesting bug - was puzzled as to why/how the removed tab can still be the selected tab even though no longer in the tabs list.
First question was, where exactly the selection happens: that's done in the mousePressedHandler installed by the TabHeaderSkin
setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent me) {
if (getTab().isDisable()) {
return;
}
if (me.getButton().equals(MouseButton.MIDDLE)) {
if (showCloseButton()) {
Tab tab = getTab();
if (behavior.canCloseTab(tab)) {
removeListeners(tab);
behavior.closeTab(tab);
}
}
} else if (me.getButton().equals(MouseButton.PRIMARY)) {
behavior.selectTab(getTab());
}
}
});
But then: how comes that this handler is still active after removal of the tab (and hopefully its visuals as well)? The cleanup of the visual parts is the task of TabPaneSkin, it listens to the tabs list and removes the TabHeaderSkin (aka: the component that shows the tab above its content). But the cleanup is not immediately complete for two reasons:
fade-out animation keeps the header alive until the animation is ready, that's fine
header's internal cleanup (messaged via header.removeListeners) is incomplete, as it removes children and listeners, but fails to remove the mouseHandler - and that's the bug.
Code from TabHeaderSkin:
private void removeListeners(Tab tab) {
listener.dispose();
inner.getChildren().clear();
getChildren().clear();
// following line is missing:
setOnMousePressed(null)
}
A way to hack around is to register our own listener on tabs, and force the handler to null on removal. Note: the listener must be notified after core did its job, so either install in a custom skin or after the tabPane's skin has been set.
To illustrate, I modified your example accordingly:
public class TabPaneRemoveSelected extends Application {
public static void main(String[] arg) {
launch(arg);
}
public static class MyTabSkin extends TabPaneSkin {
public MyTabSkin(TabPane pane) {
super(pane);
pane.getTabs().addListener(this::tabsChanged);
}
protected void tabsChanged(Change<? extends Tab> c) {
while (c.next()) {
if (c.wasRemoved()) {
// lookup all TabHeaderSkins
Set<Node> tabHeaders = getSkinnable().lookupAll(".tab");
tabHeaders.stream()
.filter(p -> p instanceof Parent)
.map(p -> (Parent) p)
.forEach(p -> {
// all children removed indicates being in the process
// of being removed
if (p.getChildrenUnmodifiable().size() == 0) {
// complete removeListeners
p.setOnMousePressed(null);
}
}
);
}
}
}
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane() {
#Override
protected Skin<?> createDefaultSkin() {
return new MyTabSkin(this);
}
};
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
System.out.println("Tab change: " + oldTab + "/" + newTab );
});
Tab tab = new Tab("Test tab");
Tab second = new Tab("second");
installHandler(tabPane, tab, second);
installHandler(tabPane, second);
System.out.println("Adding tab");
tabPane.getTabs().addAll(tab, second);
Group root = new Group(tabPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
protected void installHandler(TabPane tabPane, Tab... tab) {
for (Tab tab2 : tab) {
tab2.setOnCloseRequest((event) -> {
System.out.println("Removing tab");
event.consume();
//I need to remove tab manually
tabPane.getTabs().remove(tab2);
});
}
}
}
It seems to be a bug so I opened a bug issue http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8189424 (https://bugs.openjdk.java.net/browse/JDK-8189424) and accept this answer (as soon as SO lets me do it).

How to remove MenuItems from MenuButtons with Javafx

I am trying to make a dynamically sized MenuButton.
I am designing a library program. Books go on Shelves. Over the course of the program, the number of shelves could increase or decrease.
I want to make a menu Button that can reflect the shelves in the library - the set of MenuItems should increase if the number of shelves increase and decrease if the number of shelves decrease.
This is my current code. However, it doesn't remove any MenuItems. Also, it duplicates all the MenuItems already included.
previous code omitted…
//the button “shelfBtn.getItems” is a MenuButton defined elsewhere
Button btn = new Button(“Refresh”);
btn.setTranslateX(-20);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for(String shelf : shelfs){
MenuItem newShelf = new MenuItem(shelf);
newShelf.setOnAction(new EventHandler<ActionEvent() {
#Override
public void handle(ActionEvent event) {
// ignore this
shelfField.setText(shelf);
}
});
shelfBtn.getItems().add(newShelf);
}
}
});
remaining code omitted…
I have also tried using iteration to limit extra menuItems from being created - to no avail.
Additionally:
1) Is there a way to just delete a menuItem?
2) Is there a way to clear a MenuButton?
Thanks
Simply modifiy the items ObservableList:
#Override
public void start(Stage primaryStage) {
ListView<String> listView = new ListView<>();
for (int i = 0; i < 26; i++) {
listView.getItems().add(Character.toString((char) ('a'+i)));
}
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
MenuButton menuButton = new MenuButton();
Button btn = new Button("Modify");
btn.setOnAction((ActionEvent event) -> {
// create menu items from selection
menuButton.getItems().clear();
for (String s : listView.getSelectionModel().getSelectedItems()) {
menuButton.getItems().add(new MenuItem(s));
}
});
Scene scene = new Scene(new VBox(listView, menuButton, btn));
primaryStage.setScene(scene);
primaryStage.show();
}
As with any List there are multiple ways to remove and add elements to the list, like add, remove, clear, ect.
Adding duplicates can be prevented by using a Set, e.g.
Set<String> items = new shelfBtn.getItems().stream()
.map(MenuItem::getText)
.collect(Collectors.toCollection(HashSet::new));
for(String shelf : shelfs){
if (items.add(shelf)) {
...
}
}

Categories

Resources