I want my JavaFX program to be able to dynamically change the accelerator for menu items. However, I can't seem to get it to work in JavaFX 2.2. In the code below, the first menu item has an accelerator "Ctrl+M" and it works fine. "Ctrl+M" is printed every time the menu item is selected using a mouse or with the keyboard accelerator Ctrl+M. The second menu item attempts to change the first men item's accelerator to "Ctrl+L". After choosing the second menu item, I can click on the first menu item again and it shows that its new accelerator is "Ctrl+L", but I cannot use the keyboard accelerator Ctrl+L to activate the first menu item. Instead, I still need to use Ctrl+M to activate the first menu item. Any suggestions on what is going wrong here?
/*
* from code at <http://java-buddy.blogspot.com/2012/02/javafx-20-set-accelerator.html>
*/
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class AcceleratorExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Accelerator example");
Group root = new Group();
Scene scene = new Scene(root, 400, 300, Color.WHITE);
MenuBar menuBar = new MenuBar();
Menu FileMenu = new Menu("File");
final MenuItem item1 = new MenuItem("Select me");
item1.setAccelerator(KeyCombination.keyCombination("Ctrl+M"));
item1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println(item1.getAccelerator());
}
});
final MenuItem item2 = new MenuItem("New accel");
item2.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
item1.setAccelerator(KeyCombination.keyCombination("Ctrl+L"));
}
});
FileMenu.getItems().add(item1);
FileMenu.getItems().add(item2);
menuBar.getMenus().add(FileMenu);
menuBar.prefWidthProperty().bind(primaryStage.widthProperty());
root.getChildren().add(menuBar);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Dynamically changing the accelerator has iffy behaviour at present, but this is a bug which has been fixed for 8u20:
https://javafx-jira.kenai.com/browse/RT-35182
Related
i'd like to have a StackPane as the root node, it makes overlay effects easy.
But by using a stackpane as the root, inner controls can move out of the window.
In the following example you can see the controls moving out of the window ,
if you shrink the window small enough, e.g. both the menubar and the listview go out to the left. I want to prevent this, how can i do that?
package test;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("controls move out of window :(");
Menu menu = new Menu("Menu");
MenuBar menubar = new MenuBar(menu);
ListView<String> listView = new ListView<>(FXCollections.observableArrayList("Args, it moves away."));
BorderPane borderPane = new BorderPane();
borderPane.setTop(menubar);
borderPane.setLeft(listView);
StackPane rootStackPane = new StackPane(borderPane);
Scene scene = new Scene(rootStackPane);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Update:
setting the alignment for the BorderPane inside the StackPane seems to help:
public void start(Stage stage) throws Exception {
stage.setTitle("controls stay in window :)");
Menu menu = new Menu("Menu");
MenuBar menubar = new MenuBar(menu);
ListView<String> listView = new ListView<>(FXCollections.observableArrayList("it stays!"));
BorderPane borderPane = new BorderPane();
borderPane.setTop(menubar);
borderPane.setLeft(listView);
StackPane rootStackPane = new StackPane(borderPane);
StackPane.setAlignment(borderPane, Pos.TOP_LEFT);
Scene scene = new Scene(rootStackPane);
stage.setScene(scene);
stage.show();
}
Place your stack in a Group and bind the stack's preferred size to the scene's preferred size.
As you can see from the second image, the overlay is not centered on the visible screen, it is centered on the StackPane. The StackPane's minimum size will be the minimum size of it's largest component (even if it overflows the screen), so the overlay is centered on that. To find out minimum sizes of things, you could use either design the UI in SceneBuilder or debug the UI using ScenicView.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("controls stay in window :)");
Menu menu = new Menu("Menu");
MenuBar menubar = new MenuBar();
menubar.getMenus().add(menu);
ListView<String> listView = new ListView<>(FXCollections.observableArrayList("Args, it moves away."));
BorderPane borderPane = new BorderPane();
borderPane.setTop(menubar);
borderPane.setLeft(listView);
borderPane.setStyle("-fx-background-color: palegreen;");
Node overlayContent = new Label("centered");
StackPane stack = new StackPane(borderPane, overlayContent);
Scene scene = new Scene(new Group(stack));
stage.setScene(scene);
stage.show();
stack.prefWidthProperty().bind(scene.widthProperty());
stack.prefHeightProperty().bind(scene.heightProperty());
}
public static void main(String[] args) {
launch(args);
}
}
I have a modal dialog that I pop up, which contains text boxes. If the user has the mouse over the text box (so the TEXT cursor shows) and then hits Enter to close the dialog, the cursor gets left in the TEXT state. So far I have found no way programmatically to change it back to the DEFAULT state. If I try some other cursor on the main scene (like HAND or MOVE) it works. But DEFAULT does not.
I'm using JavaFx / JDK 1.7 that was "fresh" when I downloaded within the last couple of months. I'm on Windows 8.1.
I've searched the web a fair bit and haven't found any mention of it. Is this a known bug? Can anybody suggest a workaround or explain what is going on?
Code sample follows. Simply click on the text edit in the dialog, keep the mouse over the textbox so the cursor is TEXT, and hit Enter.
package jfxtest;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Jfxtest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override public void start(final Stage primaryStage) {
Button btn = new Button();
btn.setText("Open Dialog");
btn.setOnAction(
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
final Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.initOwner(primaryStage);
final TextField txt = new TextField("Hit Enter while I have focus and the mouse is over me!");
txt.setMinWidth(280);
Button btnOk = new Button("OK");
btnOk.setDefaultButton(true);
btnOk.setOnAction(
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
// these don't work
txt.setCursor(Cursor.DEFAULT);
dialog.getScene().setCursor(Cursor.DEFAULT);
primaryStage.getScene().setCursor(Cursor.DEFAULT);
dialog.close();
// and these don't work
txt.setCursor(Cursor.DEFAULT);
dialog.getScene().setCursor(Cursor.DEFAULT);
primaryStage.getScene().setCursor(Cursor.DEFAULT);
}
} );
VBox dialogVbox = new VBox(20);
dialogVbox.getChildren().add(txt);
dialogVbox.getChildren().add(btnOk);
Scene dialogScene = new Scene(dialogVbox, 300, 200);
dialog.setScene(dialogScene);
dialog.showAndWait();
// and these doesn't work
dialogScene.setCursor(Cursor.DEFAULT);
primaryStage.getScene().setCursor(Cursor.DEFAULT);
// but this works as expected (!), although it doesn't solve my problem
// primaryStage.getScene().setCursor(Cursor.HAND);
}
} );
VBox vbox = new VBox(20);
vbox.getChildren().add(btn);
Scene scene = new Scene (vbox, 400, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I need to show a Panel with an extra options in the same Scene when I click on the Button, but I've no idea how to achieve this behaviour. The problem that the Stage not resizing when I add panel to root VBox.
I've written simple code to demonstrate the problem.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) throws Exception {
final VBox root = new VBox();
Button button = new Button("add label");
root.getChildren().add(button);
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
root.getChildren().add(new Label("hello"));
}
});
stage.setScene(new Scene(root));
stage.show();
}
}
I suppose I need to call some method to notify root container to do layout, but all methods I try haven't brought me desired results.
Program Works
Your program is working almost as you expect I think (when you click the "add label" button, a new label is added to the scene).
Why you can't see it working
You can't see the newly added label as a stage is sized by default to fit the initial content of the scene. When you add more area to the scene, the stage won't be automatically resized to encompass the new area.
What to do to see it work
Manually resize the stage window after adding a label.
OR
Set an initial size for the scene so that you can see the newly added labels.
stage.setScene(new Scene(root, 200, 300));
OR
After you add each new label, size the stage to the scene.
stage.sizeToScene();
just change the code
button.setOnAction(new EventHandler<ActionEvent>()
{
public void handle(ActionEvent event)
{
root.getChildren().add(new Label("hello"));
stage.sizeToScene();
}
});
I want to create several work spaces which can be switched with toggle buttons. I created this example
package menunavigation;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MenuNavigation extends Application
{
#Override
public void start(Stage primaryStage)
{
final VBox vbox = new VBox();
MenuBar menuBar = new MenuBar();
// --- Menu File
Menu menuFile = new Menu("File");
// --- Menu Edit
Menu menuEdit = new Menu("Edit");
// --- Menu View
Menu menuView = new Menu("View");
menuBar.getMenus().addAll(menuFile, menuEdit, menuView);
// buttons
String pillButtonCss = MenuNavigation.class.getResource("PillButton.css").toExternalForm();
// create 3 toggle buttons and a toogle group for them
ToggleButton tb1 = new ToggleButton("Left Button");
tb1.setId("pill-left");
ToggleButton tb2 = new ToggleButton("Center Button");
tb2.setId("pill-center");
ToggleButton tb3 = new ToggleButton("Right Button");
tb3.setId("pill-right");
final ToggleGroup group = new ToggleGroup();
tb1.setToggleGroup(group);
tb2.setToggleGroup(group);
tb3.setToggleGroup(group);
// select the first button to start with
group.selectToggle(tb1);
final Rectangle rect1 = new Rectangle(300, 300);
rect1.setFill(Color.ALICEBLUE);
final Rectangle rect2 = new Rectangle(300, 300);
rect2.setFill(Color.AQUA);
final Rectangle rect3 = new Rectangle(300, 300);
rect3.setFill(Color.AZURE);
tb1.setUserData(rect1);
tb2.setUserData(rect2);
tb3.setUserData(rect3);
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>()
{
#Override
public void changed(ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle)
{
if (new_toggle == null)
{
//rect.setFill(Color.WHITE);
}
else
{
//rect.setFill((Color) group.getSelectedToggle().getUserData());
vbox.getChildren().setAll((Node) group.getSelectedToggle().getUserData());
}
}
});
HBox hBox = new HBox();
hBox.getChildren().addAll(tb1, tb2, tb3);
hBox.setPadding(new Insets(10, 10, 10, 10));
hBox.getStylesheets().add(pillButtonCss);
// end buttons
vbox.getChildren().addAll(menuBar, hBox, new Text("Test Space"));
StackPane root = new StackPane();
root.getChildren().add(vbox);
Scene scene = new Scene(root, 800, 850);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
This is the visual result:
When I press the second button I get this:
I want to use the toggle buttons to create different work spaces on which I want to place one main BorderPane or GridPane in which I want to insert many other components. Can you help me to get this result.
Is there a reason to not use a TabPane?
Edit:
Replace:
vbox.getChildren().setAll((Node) group.getSelectedToggle().getUserData());
With:
vbox.getChildren().set(2,(Node) group.getSelectedToggle().getUserData());
to show the rectangle instead of the Text.
This replaces the node with index 2 (the text) of your vbox with the node from ToggleButtons UserData (instead of replacing all elements in your current code).
I want to implement this example: http://docs.oracle.com/javafx/2/ui_controls/toggle-button.htm#
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class test extends Application
{
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setScene(new Scene(root));
String pillButtonCss = DX57DC.class.getResource("PillButton.css").toExternalForm();
// create 3 toggle buttons and a toogle group for them
ToggleButton tb1 = new ToggleButton("Left Button");
tb1.setId("pill-left");
ToggleButton tb2 = new ToggleButton("Center Button");
tb2.setId("pill-center");
ToggleButton tb3 = new ToggleButton("Right Button");
tb3.setId("pill-right");
final ToggleGroup group = new ToggleGroup();
tb1.setToggleGroup(group);
tb2.setToggleGroup(group);
tb3.setToggleGroup(group);
// select the first button to start with
group.selectToggle(tb1);
//////////////////////////////////////////
tb1.setUserData(Color.LIGHTGREEN);
tb2.setUserData(Color.LIGHTBLUE);
tb3.setUserData(Color.SALMON);
final Rectangle rect = new Rectangle(300, 300);
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>()
{
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle toggle, Toggle new_toggle)
{
if (new_toggle == null)
{
rect.setFill(Color.WHITE);
}
else
{
rect.setFill(
(Color) group.getSelectedToggle().getUserData());
}
}
});
///////////////////////////////////////////
rect.setArcHeight(10);
rect.setArcWidth(10);
HBox hBox = new HBox();
hBox.getChildren().addAll(tb1, tb2, tb3);
hBox.setPadding(new Insets(20, 20, 260, 20));
hBox.getStylesheets().add(pillButtonCss);
VBox vbox = new VBox();
vbox.getChildren().add(hBox);
vbox.getChildren().add(rect);
root.getChildren().add(vbox);
}
#Override
public void start(Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
But for some reason I can see the buttons but the rectangle is not displayed when I try to switch the buttons. Can you help me to find where is my mistake? And also how I can implement this example with several rectangles holding different content?
You have to add rect to your scene graph :
root.getChildren().addAll(hBox, rect);
Also, think of using an appropriate layout for your root, BorderPane instead of Group for example.