I want never to hide the virtual keyboard like Programmatically show/hide virtual keyboard
OR (if above point is not possible then)
I want to remove/hide/disable below (highlighted green left bottom) button from virtual keyboard.
I am using Java 8 and Javafx.
Update 1
I have integrated José Pereda's code. The hide button (.hide) is hidden successfully. Now I am having hard time to keep displaying the keyboard for all time.
Problem:
Whenever there is focus out from the textarea the keyboard hides. So when user clicks on Convert Now! button the keyboard hides. I tried to stay focused on textarea by using textarea.requestFocus();, but there is a blinking of keyboard (hide then show).
My Goal
To never hide the keyboard at any case unless the program is not terminated.
Code: You can check my code on github too.
package com.binaryname.view;
import java.util.Iterator;
import com.sun.javafx.print.PrintHelper;
import com.sun.javafx.print.Units;
import com.sun.javafx.scene.control.skin.FXVK;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.transform.Scale;
import javafx.stage.PopupWindow;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
public class Main extends Application {
private PopupWindow keyboard;
private final Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
private final Rectangle2D bounds = Screen.getPrimary().getBounds();
private final double taskbarHeight = bounds.getHeight() - visualBounds.getHeight();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Binary Name");
Label helloLbl = new Label("Hello");
helloLbl.setAlignment(Pos.CENTER);
helloLbl.setFont(Font.font("Comic Sans MS", FontWeight.BOLD, 68));
helloLbl.setStyle("-fx-background-color: red;padding: 20px;");
helloLbl.setTextFill(Color.web("#ffffff"));
Label myNameLbl = new Label("my name is");
myNameLbl.setAlignment(Pos.CENTER);
myNameLbl.setFont(Font.font("Comic Sans MS", 48));
myNameLbl.setStyle("-fx-background-color: red;padding: 20px;");
myNameLbl.setTextFill(Color.web("#ffffff"));
TextArea nameTxtArea = new TextArea();
nameTxtArea.setWrapText(Boolean.TRUE);
nameTxtArea.getStyleClass().add("center-text-area");
nameTxtArea.setFont(Font.font("Comic Sans MS", 28));
nameTxtArea.setStyle("padding: 20px;");
Button printBtn = new Button("PRINT");
printBtn.setId("ipad-grey");
printBtn.setDisable(Boolean.TRUE);
Button convertBtn = new Button("Convert Now!");
convertBtn.setId("ipad-grey");
convertBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
nameTxtArea.requestFocus();
convertBtn.setDisable(Boolean.TRUE);
printBtn.setDisable(Boolean.FALSE);
}
});
HBox hBox = new HBox(100);
hBox.setAlignment(Pos.CENTER);
hBox.getChildren().addAll(convertBtn, printBtn);
VBox vBox = new VBox(10);
vBox.setAlignment(Pos.TOP_CENTER);
vBox.getChildren().addAll(helloLbl, myNameLbl, nameTxtArea, hBox);
vBox.setStyle("-fx-background-color: red;margin: 20px;");
printBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
nameTxtArea.requestFocus();
// Start printing
print(vBox, nameTxtArea.getText());
convertBtn.setDisable(Boolean.FALSE);
printBtn.setDisable(Boolean.TRUE);
nameTxtArea.setText("");
}
});
Scene scene = new Scene(vBox);
scene.getStylesheets().add(Main.class.getResource("/style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(scene);
primaryStage.setX(visualBounds.getMinX());
primaryStage.setY(visualBounds.getMinY());
primaryStage.setWidth(visualBounds.getWidth());
primaryStage.setHeight(visualBounds.getHeight());
adjustTextAreaLayout(nameTxtArea);
primaryStage.show();
// attach keyboard to first node on scene:
Node first = scene.getRoot().getChildrenUnmodifiable().get(0);
if (first != null) {
FXVK.init(first);
FXVK.attach(first);
keyboard = getPopupWindow();
}
nameTxtArea.focusedProperty().addListener((ob, b, b1) -> {
if (keyboard == null) {
keyboard = getPopupWindow();
}
keyboard.setHideOnEscape(Boolean.FALSE);
keyboard.setAutoHide(Boolean.FALSE);
keyboard.centerOnScreen();
keyboard.requestFocus();
keyboard.yProperty().addListener(obs -> {
Platform.runLater(() -> {
Double y = bounds.getHeight() - taskbarHeight - keyboard.getY();
nameTxtArea.setMaxHeight((bounds.getHeight() - y) * 0.4);
nameTxtArea.setMinHeight((bounds.getHeight() - y) * 0.4);
});
});
});
}
public static void main(String[] args) {
launch(args);
}
private void print(Node node1, String text) {
// Create a printer job for the default printer
Printer printer = Printer.getDefaultPrinter();
Paper label = PrintHelper.createPaper("2.5x3.5", 2.5, 3.5, Units.INCH);
PageLayout pageLayout = printer.createPageLayout(label, PageOrientation.LANDSCAPE, Printer.MarginType.EQUAL);
PrinterJob job = PrinterJob.createPrinterJob();
Node node = createFullNode(text);
double scaleX = pageLayout.getPrintableWidth() / node1.getBoundsInParent().getWidth();
double scaleY = pageLayout.getPrintableHeight() / node1.getBoundsInParent().getHeight();
node.getTransforms().add(new Scale(scaleX, scaleY));
if (job != null) {
// Print the node
boolean printed = job.printPage(node);
if (printed) {
// End the printer job
job.endJob();
} else {
// Write Error Message
System.out.println("Printing failed.");
}
} else {
// Write Error Message
System.out.println("Could not create a printer job.");
}
node.getTransforms().remove(node.getTransforms().size() - 1);
}
private PopupWindow getPopupWindow() {
#SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
if (window.getScene() != null && window.getScene().getRoot() != null) {
Parent root = window.getScene().getRoot();
if (root.getChildrenUnmodifiable().size() > 0) {
Node popup = root.getChildrenUnmodifiable().get(0);
if (popup.lookup(".fxvk") != null) {
FXVK vk = (FXVK) popup.lookup(".fxvk");
// hide the key:
vk.lookup(".hide").setVisible(false);
return (PopupWindow) window;
}
}
}
return null;
}
}
return null;
}
private Node createFullNode(String text) {
Label helloLbl = new Label("Hello");
helloLbl.setAlignment(Pos.CENTER);
helloLbl.setFont(Font.font("Comic Sans MS", FontWeight.BOLD, 68));
helloLbl.setStyle("-fx-background-color: red;padding: 20px;");
helloLbl.setTextFill(Color.web("#ffffff"));
Label myNameLbl = new Label("my name is");
myNameLbl.setAlignment(Pos.CENTER);
myNameLbl.setFont(Font.font("Comic Sans MS", 48));
myNameLbl.setStyle("-fx-background-color: red;padding: 20px;");
myNameLbl.setTextFill(Color.web("#ffffff"));
TextArea nameTxtArea = new TextArea();
nameTxtArea.setWrapText(Boolean.TRUE);
nameTxtArea.setFont(Font.font("Comic Sans MS", 28));
nameTxtArea.setStyle("padding: 20px;");
nameTxtArea.setText(text);
nameTxtArea.getStyleClass().add("center-text-area");
HBox hBox = new HBox(1000);
hBox.setAlignment(Pos.CENTER);
VBox vBox = new VBox(10);
vBox.setAlignment(Pos.CENTER);
vBox.getChildren().addAll(helloLbl, myNameLbl, nameTxtArea, hBox);
vBox.setStyle("-fx-background-color: red;margin: 20px;");
vBox.getStylesheets().add(Main.class.getResource("/style.css").toExternalForm());
return vBox;
}
private void adjustTextAreaLayout(TextArea textArea) {
textArea.applyCss();
textArea.layout();
ScrollPane textAreaScroller = (ScrollPane) textArea.lookup(".scroll-pane");
Text text = (Text) textArea.lookup(".text");
ChangeListener<? super Bounds> listener =
(obs, oldBounds, newBounds) -> centerTextIfNecessary(textAreaScroller, text);
textAreaScroller.viewportBoundsProperty().addListener(listener);
text.boundsInLocalProperty().addListener(listener);
}
private void centerTextIfNecessary(ScrollPane textAreaScroller, Text text) {
double textHeight = text.getBoundsInLocal().getHeight();
double viewportHeight = textAreaScroller.getViewportBounds().getHeight();
double offset = Math.max(0, (viewportHeight - textHeight) / 2 );
text.setTranslateY(offset);
Parent content = (Parent)textAreaScroller.getContent();
for (Node n : content.getChildrenUnmodifiable()) {
if (n instanceof Path) { // caret
n.setTranslateY(offset);
}
}
}
}
You can show and hide the virtual keyboard on demand.
You just need to provide a node that the keyboard will be attached to, and it doesn't need to be a TextField.
Obviously, you show the virtual keyboard for being able to type in your input control (TextField, TextArea,...), but I'll leave this part to you.
Without knowing your scene hierarchy, I'll just pick the first node on it:
#Override
public void start(Stage stage) {
...
stage.setScene(scene);
stage.show();
// attach keyboard to first node on scene:
Node first = scene.getRoot().getChildrenUnmodifiable().get(0);
if (first != null) {
FXVK.init(first);
FXVK.attach(first);
}
}
That will show the keyboard right after showing the stage.
If you want to programmatically close it at any point, you will just call:
FXVK.detach();
About hiding one of the keys of the keyboard, this is a little bit more tricky, since the keyboard is placed in a popup, so first of all you need to get a handle of it. But you can find already solutions for this, like this one.
The following snippet will look for the popup window. As for the deprecated method, on JavaFX 9 it will be a public method (javafx.stage.Windows.getWindows()).
private PopupWindow getPopupWindow() {
#SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
if (window.getScene() != null && window.getScene().getRoot() != null) {
Parent root = window.getScene().getRoot();
if (root.getChildrenUnmodifiable().size() > 0) {
Node popup = root.getChildrenUnmodifiable().get(0);
if (popup.lookup(".fxvk") != null) {
FXVK vk = (FXVK) popup.lookup(".fxvk");
return (PopupWindow) window;
}
}
}
return null;
}
}
return null;
}
Once you have the virtual keyboard instance, you will be able to find any of its nodes with lookups, based on their style classes. Luckily, all the keys have assigned the styleClass key, so you can easily interact with all of them. As for the particular key to hide the keyboard, this one also has special and hide style classes:
// hide the key:
vk.lookup(".hide").setVisible(false);
Full sample code:
public class FXVKAlwaysOn extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 1280, 600);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
Node first = scene.getRoot().getChildrenUnmodifiable().get(0);
if (first != null) {
FXVK.init(first);
FXVK.attach(first);
getPopupWindow();
}
}
private PopupWindow getPopupWindow() {
#SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
if (window.getScene() != null && window.getScene().getRoot() != null) {
Parent root = window.getScene().getRoot();
if (root.getChildrenUnmodifiable().size() > 0) {
Node popup = root.getChildrenUnmodifiable().get(0);
if (popup.lookup(".fxvk") != null) {
FXVK vk = (FXVK) popup.lookup(".fxvk");
// hide the key:
vk.lookup(".hide").setVisible(false);
return (PopupWindow) window;
}
}
}
return null;
}
}
return null;
}
}
Related
I'm new to JavaFX. I try to program a simple GUI but I face those problem whom might be related.
I set files with a File Chooser and want to do pretty basic operations:
save the last folder used
write the name of the selected file in the VBox
Here's my code (which compiles):
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static Stage primaryStageS;
public static Scene mainScene;
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene((new Test(primaryStage).getScene()));
primaryStageS = primaryStage;
primaryStage.setTitle("Parcel Manager Main Page");
primaryStage.initStyle(StageStyle.DECORATED);
VBox main = new VBox(new Label("Test program"));
mainScene = new Scene(main, 800, 600);
primaryStage.setScene((new Test(primaryStage)).getScene());
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public class Object1 {
String name;
public Object1(File f) throws IOException {
name = f.getName();
}
public String getName() {
return name;
}
}
public class Test {
Object1 collec;
String collecName;
File lastFolder;
Pane rootGroup;
public Test(Stage stage) {
setButtons(stage);
}
public void setButtons(Stage stageGoal) {
VBox vbox = new VBox();
Button b = getButton(stageGoal);
vbox.getChildren().addAll(b, new Label(getCollecName() == null ? "no name" : collecName));
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(vbox, 0, 0);
inputGridPane.getChildren().addAll(vbox);
rootGroup = new VBox(12);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12, 12, 12, 12));
}
public Button getButton(Stage stage) {
FileChooser fileChooserParcel = new FileChooser();
fileChooserParcel.setInitialDirectory(getLastFolder());
Button button = new Button("Select a File");
button.setOnAction(e -> {
File f = fileChooserParcel.showOpenDialog(stage);
if (f != null) {
try {
collec = new Object1(f);
} catch (IOException e1) {
e1.printStackTrace();
}
setLastFolder(f.getParentFile());
setCollecName(collec);
setButtons(stage); // tried to reload every buttons - doesn't work
stage.setWidth(stage.getWidth() + 0.0001); // found this dirty hack but doesn't work
}
});
return button;
}
public void setCollecName(Object1 o1) {
collecName = o1.getName();
}
public String getCollecName() {
return collecName;
}
public File getLastFolder() {
return lastFolder;
}
public void setLastFolder(File folder) {
System.out.println("set last folder: " + folder);
lastFolder = folder;
}
private Scene getScene() {
return new Scene(rootGroup, 800, 600);
}
}
}
I cannot refresh the Nodes, either to set a current Initial Directory or display the collecName on the VBox. I tried to regenerate them with reloading of objects or resizing the window, but nothing works. When I print the variables on console, I see that they changes. But haven't found any refresh method for any of my objects.
I bet it's a design program issue, but I have been moving things around for the last week and doesn't know how to fix this.
Thanks !
You are only setting the initial directory once. I guess you want to set it every time you click the button. So move that line of code to inside the handler.
Compare the below getButton() method with yours.
public Button getButton(Stage stage) {
FileChooser fileChooserParcel = new FileChooser();
Button button = new Button("Select a File");
button.setOnAction(e -> {
fileChooserParcel.setInitialDirectory(getLastFolder()); // CHANGE HERE.
File f = fileChooserParcel.showOpenDialog(stage);
if (f != null) {
try {
collec = new Object1(f);
} catch (IOException e1) {
e1.printStackTrace();
}
setLastFolder(f.getParentFile());
setCollecName(collec);
setButtons(stage); // tried to reload every buttons - doesn't work
stage.setWidth(stage.getWidth() + 0.0001); // found this dirty hack but doesn't work
}
});
return button;
}
Mouse events and scroll events behave in different ways
Mouse Events:
The event is captured by mainStage
The event is captured by mainStage
The event is not captured
Scroll Events:
The event is captured by mainStage
The event is captured by secondStage
The event is not captured
Is there any way that transparent secondStage does not capture scroll events?
My code:
Pane mainPane = new Pane(new Label("Main Stage"));
mainPane.setPrefSize(300, 300);
mainStage.setScene(new Scene(mainPane));
Stage secondStage = new Stage();
Pane secondPane = new Pane(new Label("Second Stage"));
secondPane.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)));
secondPane.setBorder(new Border(
new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))));
secondPane.setPrefSize(300, 300);
secondStage.setScene(new Scene(secondPane, Color.TRANSPARENT));
secondStage.initStyle(StageStyle.TRANSPARENT);
mainStage.getScene().setOnScroll(event -> System.out.println("Scroll in main stage"));
secondStage.getScene().setOnScroll(event -> System.out.println("Scroll in second stage"));
mainStage.getScene().setOnMouseClicked(event -> System.out.println("Click in main stage"));
secondStage.getScene().setOnMouseClicked(event -> System.out.println("Click in second stage"));
mainStage.show();
secondStage.show();
Java version: 1.8.0_201 (64 bits), Windows 10
edit:
The example is a simplification with only two windows. Fire the event programmatically implies discovering which stage is immediately lower and that is another problem in itself.
It might be a great coincidence, that we also came with the same solution of transparent window because of not having the feature of managing z-index of stages. And We encountered the exact same issue as yours. ie, scroll events not propagating to underlying Stages. We used the below approach, not sure whether this can help you:
Firstly, We constructed a Singleton class that keeps a reference of Node that is currently hovered on.
Then, when we create any normal stage, we include the below handlers to the scene of that new stage. The key thing here is that, the mouse events are still able to pass through the transparent stage to the underlying window, keep track of node which sits under the mouse.
scene.addEventFilter(MouseEvent.MOUSE_EXITED_TARGET, e -> {
hoverNode.set(null);
});
scene.addEventFilter(MouseEvent.MOUSE_MOVED, e -> {
hoverNode.set(e.getTarget());
});
In the scene of the transparent window, we included the below handlers to delegate the scroll events to the underlying node.
scene.addEventFilter(ScrollEvent.SCROLL, e -> {
if (hoverNode.get() != null) {
Event.fireEvent(hoverNode.get(), e);
}
});
scene.addEventHandler(ScrollEvent.SCROLL, e -> {
if (hoverNode.get() != null) {
Event.fireEvent(hoverNode.get(), e);
}
});
I am pretty sure this is not the most desired way. But this addressed our issue. :)
Below is the quick demo code of what I mean.
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.Event;
import javafx.event.EventTarget;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.stream.IntStream;
public class ScrollThroughTransparentStage_Demo extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Main Window");
VBox root = new VBox(buildScrollPane());
root.setStyle("-fx-background-color:#888888;");
root.setSpacing(10);
root.setPadding(new Insets(10));
Button normalStageBtn = new Button("Normal Stage");
normalStageBtn.setOnAction(e -> {
Stage normalStage = new Stage();
normalStage.initOwner(stage);
Scene normalScene = new Scene(buildScrollPane(), 300, 300);
addHandlers(normalScene);
normalStage.setScene(normalScene);
normalStage.show();
});
CheckBox allowScrollThrough = new CheckBox("Allow scroll through transparency");
allowScrollThrough.setSelected(true);
HBox buttons = new HBox(normalStageBtn);
buttons.setSpacing(20);
root.getChildren().addAll(allowScrollThrough,buttons);
Scene scene = new Scene(root, 600, 600);
addHandlers(scene);
stage.setScene(scene);
stage.show();
/* Transparent Stage */
Stage transparentStage = new Stage();
transparentStage.initOwner(stage);
transparentStage.initStyle(StageStyle.TRANSPARENT);
Pane mainRoot = new Pane();
Pane transparentRoot = new Pane(mainRoot);
transparentRoot.setStyle("-fx-background-color:transparent;");
Scene transparentScene = new Scene(transparentRoot, Color.TRANSPARENT);
transparentStage.setScene(transparentScene);
transparentScene.addEventFilter(ScrollEvent.SCROLL, e -> {
if (allowScrollThrough.isSelected() && HoverNodeSingleton.getInstance().getHoverNode() != null) {
Event.fireEvent(HoverNodeSingleton.getInstance().getHoverNode(), e);
}
});
transparentScene.addEventHandler(ScrollEvent.SCROLL, e -> {
if (allowScrollThrough.isSelected() && HoverNodeSingleton.getInstance().getHoverNode() != null) {
Event.fireEvent(HoverNodeSingleton.getInstance().getHoverNode(), e);
}
});
determineStageSize(transparentStage, mainRoot);
transparentStage.show();
Button transparentStageBtn = new Button("Transparent Stage");
transparentStageBtn.setOnAction(e -> {
MiniStage miniStage = new MiniStage(mainRoot);
ScrollPane scrollPane = buildScrollPane();
scrollPane.setPrefSize(300, 300);
miniStage.setContent(scrollPane);
miniStage.show();
});
buttons.getChildren().add(transparentStageBtn);
}
private static void determineStageSize(Stage stage, Node root) {
DoubleProperty width = new SimpleDoubleProperty();
DoubleProperty height = new SimpleDoubleProperty();
DoubleProperty shift = new SimpleDoubleProperty();
Screen.getScreens().forEach(screen -> {
Rectangle2D bounds = screen.getVisualBounds();
width.set(width.get() + bounds.getWidth());
if (bounds.getHeight() > height.get()) {
height.set(bounds.getHeight());
}
if (bounds.getMinX() < shift.get()) {
shift.set(bounds.getMinX());
}
});
stage.setX(shift.get());
stage.setY(0);
stage.setWidth(width.get());
stage.setHeight(height.get());
root.setTranslateX(-1 * shift.get());
}
private void addHandlers(Scene scene) {
scene.addEventFilter(MouseEvent.MOUSE_EXITED_TARGET, e -> {
HoverNodeSingleton.getInstance().setHoverNode(null);
});
scene.addEventFilter(MouseEvent.MOUSE_MOVED, e -> {
HoverNodeSingleton.getInstance().setHoverNode(e.getTarget());
});
}
private ScrollPane buildScrollPane() {
VBox vb = new VBox();
vb.setSpacing(10);
vb.setPadding(new Insets(15));
IntStream.rangeClosed(1, 100).forEach(i -> vb.getChildren().add(new Label(i + "")));
ScrollPane scrollPane = new ScrollPane(vb);
return scrollPane;
}
class MiniStage extends Group {
private Pane parent;
double sceneX, sceneY, layoutX, layoutY;
protected BorderPane windowPane;
private BorderPane windowTitleBar;
private Label labelTitle;
private Button buttonClose;
public MiniStage(Pane parent) {
this.parent = parent;
buildRootNode();
getChildren().add(windowPane);
addEventHandler(MouseEvent.MOUSE_PRESSED, e -> toFront());
}
#Override
public void toFront() {
parent.getChildren().remove(this);
parent.getChildren().add(this);
}
public void setContent(Node content) {
// Computing the bounds of the content before rendering
Group grp = new Group(content);
new Scene(grp);
grp.applyCss();
grp.requestLayout();
double width = grp.getLayoutBounds().getWidth();
double height = grp.getLayoutBounds().getHeight() + 30; // 30 title bar height
grp.getChildren().clear();
windowPane.setCenter(content);
// Centering the stage
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
setX(screenBounds.getWidth() / 2 - width / 2);
setY(screenBounds.getHeight() / 2 - height / 2);
}
public Node getContent() {
return windowPane.getCenter();
}
public void setX(double x) {
setLayoutX(x);
}
public void setY(double y) {
setLayoutY(y);
}
public void show() {
if (!parent.getChildren().contains(this)) {
parent.getChildren().add(this);
}
}
public void hide() {
parent.getChildren().remove(this);
}
private void buildRootNode() {
windowPane = new BorderPane();
windowPane.setStyle("-fx-border-width:2px;-fx-border-color:#444444;");
labelTitle = new Label("Mini Stage");
labelTitle.setStyle("-fx-font-weight:bold;");
labelTitle.setMaxHeight(Double.MAX_VALUE);
buttonClose = new Button("X");
buttonClose.setFocusTraversable(false);
buttonClose.setStyle("-fx-background-color:red;-fx-background-radius:0;-fx-background-insets:0;");
buttonClose.setOnMouseClicked(evt -> hide());
windowTitleBar = new BorderPane();
windowTitleBar.setStyle("-fx-border-width: 0 0 2px 0;-fx-border-color:#444444;-fx-background-color:#BBBBBB");
windowTitleBar.setLeft(labelTitle);
windowTitleBar.setRight(buttonClose);
windowTitleBar.setPadding(new Insets(0, 0, 0, 10));
windowTitleBar.getStyleClass().add("nonfocus-title-bar");
windowPane.setTop(windowTitleBar);
assignTitleBarEvents();
}
private void assignTitleBarEvents() {
windowTitleBar.setOnMousePressed(this::recordWindowLocation);
windowTitleBar.setOnMouseDragged(this::moveWindow);
windowTitleBar.setOnMouseReleased(this::resetMousePointer);
}
private final void recordWindowLocation(final MouseEvent event) {
sceneX = event.getSceneX();
sceneY = event.getSceneY();
layoutX = getLayoutX();
layoutY = getLayoutY();
getScene().setCursor(Cursor.MOVE);
}
private final void resetMousePointer(final MouseEvent event) {
// Updating the new layout positions
setLayoutX(layoutX + getTranslateX());
setLayoutY(layoutY + getTranslateY());
// Resetting the translate positions
setTranslateX(0);
setTranslateY(0);
getScene().setCursor(Cursor.DEFAULT);
}
private final void moveWindow(final MouseEvent event) {
double offsetX = event.getSceneX() - sceneX;
double offsetY = event.getSceneY() - sceneY;
setTranslateX(offsetX);
setTranslateY(offsetY);
event.consume();
}
}
}
/**
* Singleton class.
*/
class HoverNodeSingleton {
private static HoverNodeSingleton INSTANCE = new HoverNodeSingleton();
private EventTarget hoverNode;
private HoverNodeSingleton() {
}
public static HoverNodeSingleton getInstance() {
return INSTANCE;
}
public EventTarget getHoverNode() {
return hoverNode;
}
public void setHoverNode(EventTarget hoverNode) {
this.hoverNode = hoverNode;
}
}
I don't know that's right or not, but you can bind properties:
secondStage.getScene().onScrollProperty().bind(mainStage.getScene().onScrollProperty());
You can create a custom event dispatcher that will ignore events you don't want:
public class CustomEventDispatcher extends BasicEventDispatcher {
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if(event instanceof ScrollEvent) {
return null;
} else {
return super.dispatchEvent(event, tail);
}
}
}
Then set that on your stage:
secondStage.setEventDispatcher(new CustomEventDispatcher());
I don't know how this works in the context of stages but for simple shapes it makes a difference whether you set the fill color to Color.TRANSPARENT or just null. Using any Color catches events, whereas null does not.
You can do so by ignoring the event on the second stage using event dispatcher using this answer by #Slaw you can understand everything about EventDispatcher
https://stackoverflow.com/a/51015783/5303683
Then you can fire your own event using this answer by DVarga
https://stackoverflow.com/a/40042513/5303683
Sorry I don't have time to try and make a full example of it
everybody,
I want to insert a graphic into my application that can be dragged and dropped out of the application. As soon as the graphic is released outside the window, an Undecoraded / Transparent window should be opened, where only this graphic is displayed.
Stage newStage = new Stage();
StackPane stack = new StackPane();
ImageView imageView = new ImageView(new Image(this.getClass().getResourceAsStream(InBoxEnum.Graphic.INBOXLOGO.getFilename())));
stack.getChildren().add(imageView);
imageView.setOnDragDetected(event -> {
Dragboard dragboard = imageView.startDragAndDrop(TransferMode.MOVE);
dragboard.setDragView(imageView.snapshot(null, null));
ClipboardContent content = new ClipboardContent();
content.put(DRAGGABLE_INBOX_TYPE, "dontcare");
dragboard.setContent(content);
event.consume();
});
imageView.setOnDragDone(event -> {
System.out.println(event.getScreenX());
System.out.println(event.getScreenY());
});
Scene scene = new Scene(stack, 500, 500);
newStage.setScene(scene);
newStage.show();
The DragDetected event works so far also without problems.
The problem is that inside the dragDone event the position of the mouse is always 0 and I can't tell if the mouse is inside or outside my application. If the mouse is released inside the application, nothing should happen.
I also tried with Robot Class, but I always get a static strange x/y position.
I am using JAVA 11 (Adopt JDK).
Thanks for your help
Using Robot works fine for me (Oracle JDK 11 + JavaFX 12). Since you don't actually want to drag&drop any image data, you could simply work around this issue by creating a new stage immediately and use the MOUSE_DRAGGED of the ImageView to update the position of the window:
Stage newStage = new Stage();
StackPane stack = new StackPane();
ImageView imageView = new ImageView(new Image(...));
stack.getChildren().add(imageView);
class DragHandler implements EventHandler<MouseEvent> {
Stage dragTarget;
#Override
public void handle(MouseEvent event) {
if (dragTarget != null) {
// move stage
dragTarget.setX(event.getScreenX());
dragTarget.setY(event.getScreenY());
event.consume();
}
}
}
final DragHandler dragHandler = new DragHandler();
imageView.setOnDragDetected(event -> {
// init stage at half transparency
Group root = new Group(new ImageView(imageView.getImage()));
root.setOpacity(0.5);
Scene displayScene = new Scene(root);
displayScene.setFill(null);
Stage displayStage = new Stage();
displayStage.initStyle(StageStyle.TRANSPARENT);
displayStage.setScene(displayScene);
displayStage.setX(event.getScreenX());
displayStage.setY(event.getScreenY());
displayStage.show();
dragHandler.dragTarget = displayStage;
event.consume();
});
imageView.setOnMouseDragged(dragHandler);
imageView.setOnMouseReleased(event -> {
if (dragHandler.dragTarget != null) {
if (stack.contains(event.getX(), event.getY())) { // check, if drop happened inside the bounds of the scene root
dragHandler.dragTarget.hide();
} else {
// make stage fully opaque & cleanup
dragHandler.dragTarget.getScene().getRoot().setOpacity(1);
imageView.setImage(null);
}
dragHandler.dragTarget = null;
event.consume();
}
});
Scene scene = new Scene(stack, 500, 500);
newStage.setScene(scene);
newStage.show();
The proposed solution is based on fabian's answer.
The main change is encapsulated in withinBounds method which is used to define if the drag ended within the window bounds. This method is based on this answer:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
public class DragOut extends Application {
#Override
public void start(Stage primaryStage) {
ImageView imageView = new ImageView("https://findicons.com/files/icons/345/summer/128/cake.png");
StackPane stack = new StackPane(imageView);
DragHandler dragHandler = new DragHandler();
imageView.setOnDragDetected(event -> {
Stage displayStage = makeNewStage(imageView.getImage());
displayStage.setX(event.getScreenX());
displayStage.setY(event.getScreenY());
dragHandler.setStage(displayStage);
displayStage.show();
event.consume();
});
imageView.setOnMouseDragged(dragHandler);
imageView.setOnMouseReleased(event -> {
if (! withinBounds(event, imageView.getScene().getWindow()) && dragHandler.dragTarget != null) {
// make stage fully opaque & cleanup
dragHandler.showStage();
imageView.setImage(null);
event.consume();
}else{
dragHandler.closeStage();
}
dragHandler.setStage(null);
});
Scene scene = new Scene(stack, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
private Stage makeNewStage(Image image) {
Group root = new Group(new ImageView(image));
root.setOpacity(0.5); // init stage at half transparency
Stage displayStage = new Stage(StageStyle.TRANSPARENT);
displayStage.setScene(new Scene(root, null));
return displayStage;
}
private boolean withinBounds(MouseEvent event, Window window) {
Point2D mouseLoc = new Point2D(event.getScreenX(), event.getScreenY());
Rectangle2D windowBounds = new Rectangle2D(window.getX(), window.getY(),
window.getWidth(), window.getHeight());
return windowBounds.contains(mouseLoc);
}
class DragHandler implements EventHandler<MouseEvent> {
private Stage dragTarget;
#Override
public void handle(MouseEvent event) {
if (dragTarget != null) {
// move stage
dragTarget.setX(event.getScreenX());
dragTarget.setY(event.getScreenY());
event.consume();
}
}
void setStage(Stage stage){
dragTarget = stage;
}
void showStage(){
if (dragTarget != null) {
dragTarget.getScene().getRoot().setOpacity(1);
}
}
void closeStage(){
if (dragTarget != null) {
dragTarget.close();
}
}
}
public static void main(final String[] args) {
launch(args);
}
}
I can listen for scroll events:
tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
new EventHandler<javafx.scene.input.ScrollEvent>() {
#Override
public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {
System.out.println("Scrolled.");
}
});
But How can I be notified if the bottom/ top of the table is reached?
A simple way of doing this is to retrieve the ScrollBar using a lookup:
ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");
You can then add a listener to check if it's reached the bottom (the valueProperty of the ScrollBar is represented by the percentage it's been scrolled, so 0.0 is the top and 1.0 is the bottom):
tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
if ((Double) newValue == 1.0) {
System.out.println("Bottom!");
}
});
Below is a simple MCVE that demonstrates how to put it all together:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ScrollBarNotify extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Simple TableView to demonstrate
TableView<String> tableView = new TableView<>();
TableColumn<String, String> column = new TableColumn<>("Text");
column.setCellValueFactory(f -> new SimpleStringProperty(f.getValue()));
tableView.getColumns().add(column);
// Add some sample items to our TableView
for (int i = 0; i < 100; i++) {
tableView.getItems().add("Item #" + i);
}
// Now, let's add a listener to the TableView's scrollbar. We can only access the ScrollBar after the Scene is
// rendered, so we need to do schedule this to run later.
Platform.runLater(() -> {
ScrollBar tvScrollBar = (ScrollBar) tableView.lookup(".scroll-bar:vertical");
tvScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {
if ((Double) newValue == 1.0) {
System.out.println("Bottom!");
}
});
});
// Finally, add the TableViewto our layout
root.getChildren().add(tableView);
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
If you're using Java 10+ you can subclass TableViewSkin and get access to the VirtualFlow. The latter class has the position property which you can use to know if the top or bottom has been reached.
Here's an example using custom events:
MyEvent.java
import javafx.event.Event;
import javafx.event.EventType;
public class MyEvent extends Event {
public static final EventType<MyEvent> ANY = new EventType<>(Event.ANY, "MY_EVENT");
public static final EventType<MyEvent> TOP_REACHED = new EventType<>(ANY, "TOP_REACHED");
public static final EventType<MyEvent> BOTTOM_REACHED = new EventType<>(ANY, "BOTTOM_REACHED");
public MyEvent(EventType<? extends MyEvent> eventType) {
super(eventType);
}
}
MyTableViewSkin.java
import javafx.scene.control.TableView;
import javafx.scene.control.skin.TableViewSkin;
public class MyTableViewSkin<T> extends TableViewSkin<T> {
public MyTableViewSkin(TableView<T> control) {
super(control);
getVirtualFlow().positionProperty().addListener((obs, oldVal, newVal) -> {
if (newVal.doubleValue() == 0.0) {
control.fireEvent(new MyEvent(MyEvent.TOP_REACHED));
} else if (newVal.doubleValue() == 1.0) {
control.fireEvent(new MyEvent(MyEvent.BOTTOM_REACHED));
}
});
}
}
App.java
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
var table = new TableView<Integer>();
for (int i = 0; i < 250; i++) {
table.getItems().add(i);
}
var column = new TableColumn<Integer, Number>("Value");
column.setCellValueFactory(features -> new SimpleIntegerProperty(features.getValue()));
table.getColumns().add(column);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setSkin(new MyTableViewSkin<>(table));
table.addEventHandler(MyEvent.ANY, event -> System.out.printf("%s%n", event.getEventType()));
primaryStage.setScene(new Scene(table, 500, 300));
primaryStage.setTitle("Example");
primaryStage.show();
}
}
In this example I manually call table.setSkin. Another option is to subclass TableView and override createDefaultSkin which returns the skin you want to use.
tableView.addEventFilter(javafx.scene.input.ScrollEvent.SCROLL,
new EventHandler<javafx.scene.input.ScrollEvent>() {
#Override
public void handle(final javafx.scene.input.ScrollEvent scrollEvent) {
Object virtualFlow = ((javafx.scene.control.SkinBase<?>) tableView.getSkin()).getChildren().get(1);
double position = -1;
try {
position = (double) virtualFlow.getClass().getMethod("getPosition").invoke(virtualFlow);
} catch (Exception ignored) { }
if(position == 0.0) {
System.out.println("scrolled to top!");
}
else if(position == 1.0) {
System.out.println("scrolled to bottom!");
}
}
});
I have two different StackPanes with HBoxes I want to change the image of a label in the second StackPane with a Label(MouseListener) in the first StackPane I think the problem is that the Label doesn't gets repainted or reloaded
First StackPane:
Label label= new Label("",new ImageView(ClearSpace));
label.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
HotBar hb = new HotBar();
if(hb.getX1() == 0){
hb.setImageX1(5);
}
event.consume();
});
Second StackPane(HotBar):
public Label x1;
Image image= new Image(getClass().getResourceAsStream("/resources/images/Test.png"));
...
Label x1 = new Label("",new ImageView(image));
...
public void setImage(int i){
if(i == 5){
x1.setGraphic(new ImageView(image2));
}
}
I think these are the importantst parts of hte code
setImage() is definetly working if you use it below Label x1 = ... it works
In your EventHandler you create a new instance of HotBar on which you do changes, but this instance is not linked to the scene.
Instead you should pass the instance of HotBar into the other class and use that in your event handler.
package helloworld;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
/**
* Created by matt on 3/22/16.
*/
public class SwapLabel extends Application {
int i = 0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Image img1 = new Image("http://www.logotemplater.com/freelogostemplates/voope-free-logo-template.png", true);
Image img2 = new Image("http://www.logotemplater.com/freelogostemplates/zoozz-vector-logo-template-sample.png", true);
Label l = new Label("", new ImageView(img1));
l.addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
i = (i+1)%2;
if(i==0){
l.setGraphic(new ImageView(img1));
}else{
l.setGraphic(new ImageView(img2));
}
});
root.getChildren().add(l);
primaryStage.setScene(new Scene(root, 200, 200));
primaryStage.show();
}
}
This appears to be what you are trying to do, but this works. So most likely something else is happening.