Drag Image outside the window with JavaFX - java

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);
}
}

Related

Transparent JavaFX stage capture scrolling events if there is another window behind

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

Javafx show virtual keyboard

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;
}
}

Java change Label image

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.

JavaFX changing Scenes on fullscreen mode

I have problem with JavaFX. I created two scenes and switch button.
When I click that button I'm changing scene. But earlier i set fullscreen on true and after I pressed the button, windows taskbar shows for a moment. Is there any way to change scenes without having this taskbar visible?
There is the code:
----Main class----
import java.io.IOException;
public class Main {
public static void main(String[] args) throws InterruptedException, IOException {
DesktopApplication.launch(DesktopApplication.class);
}
}
----DesktopApplication class----
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class DesktopApplication extends Application implements Runnable {
Scene firstScene;
Scene secondScene;
Scene scene;
public static Stage primaryStagePublic;
public DesktopApplication() {
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Title");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.initStyle(StageStyle.UNDECORATED);
int width = (int) Screen.getPrimary().getBounds().getWidth();
int height = (int) Screen.getPrimary().getBounds().getHeight();
HBox mainLayout = new HBox();
mainLayout.getChildren().add(new Text("hello!"));
MyLayout myLayout = new MyLayout(this);
firstScene = new Scene(myLayout,width,height);
secondScene = new Scene(mainLayout, width, height);
scene = firstScene;
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.show();
primaryStagePublic = primaryStage;
}
#Override
public void run() {
Thread thread = new Thread() {
public void run() {
launch(DesktopApplication.class);
}
};
thread.start();
while (true) {
}
}
public void swapScenes(Stage primaryStage){
primaryStage.setScene(secondScene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
}
}
----MyLayout class----
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class MyLayout extends HBox{
private DesktopApplication desktopApplication;
public MyLayout(DesktopApplication desktopApplication) {
this.desktopApplication = desktopApplication;
init();
}
private void init(){
this.setStyle("-fx-background-color: #f8ff7d;");
Label text = new Label("testing");
Button button = new Button("Button");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
desktopApplication.swapScenes(DesktopApplication.primaryStagePublic);
}
});
this.getChildren().addAll(text, button);
}
}
I had a similar issue and solved it like #James_D suggested: Do not replace the scene as a whole, but only the root element:
public void swapScenes(Parent newContent){
stage.getScene().setRoot(newContent);
}
This requires changing the rest of the initialisation code a bit:
public class DesktopApplication extends Application implements Runnable {
Parent myLayout;
Parent mainLayout;
Scene scene;
public static Stage stage; // if possible make this private and non static
public DesktopApplication() {
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Title");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.initStyle(StageStyle.UNDECORATED);
int width = (int) Screen.getPrimary().getBounds().getWidth();
int height = (int) Screen.getPrimary().getBounds().getHeight();
mainLayout = new HBox();
mainLayout.getChildren().add(new Text("hello!"));
myLayout = new MyLayout(this);
scene = new Scene(myLayout,width,height);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.show();
primaryStagePublic = primaryStage;
}
...
I personally solved it (After a few months of looking) by, instead of doing primaryStage.setFullScreen(true), which glitches or something, doing primaryStage.setMaximized(true) along with primaryStage.setWidth(var1) and primaryStage.setHeight(var2). My hypothesis on why setFullScreen deosn't work is a bug in full screen exclusive mode. Or, there just isn't enough permissions or something and it bugs out.

resizing a substraction shape

I would like to resize the below created shape. but cannot get it.
The project is to create a transparent rectangle to show only a part of the desktop, and hide the rest. The transparent zone is the result of a substraction, and I need to make it resizable by the user.
I tryed several ways, such as adapting from this : https://gist.github.com/jewelsea/1441960
But couldn't get it.
Here is my code :
#Override
public void start(Stage stage) {
Group group = new Group();
Rectangle rect = new Rectangle(0, 0, 350, 300);
Rectangle clip = new Rectangle(20, 20, 200, 200);
clip.setArcHeight(15);
clip.setArcWidth(15);
Shape shape = Shape.subtract(rect, clip);
shape.setFill(Color.GRAY);
group.getChildren().add(shape);
Scene scene = new Scene(group);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
Any link or help would be appreciated.
If you create a Shape by Shape.subtract(...), you don't have any mechanism to change the properties of it afterwards (in the sense of changing the bounds of the shapes that were used to create it). You would have to remove the shape from its parent, recompute the rect and clip, recompute the shape, and add the new shape back into the scene.
It might be better to use a Path here so that you can manipulate the coordinates without creating a new shape every time. Traverse one way (say clockwise) around the outside (filled portion), and then the other way (anti-clockwise) around the inner (transparent portion). The resulting shape will be the same as a subtraction of the inner portion from the outer portion. The initial setup will potentially require considerably more code, but you can then manipulate the coordinates as you need to.
I'm not sure exactly what functionality you were looking for, but the following allows you to drag the inner portion around by clicking and dragging on it, and allows you to move the whole window by clicking and dragging on the outer portion. It should be enough for you to figure out what you need. I didn't include the nice rounded corners you had in your example, but you can fairly easily implement those using ArcTo path elements.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentRectangle extends Application {
#Override
public void start(Stage stage) {
Pane root = new Pane();
PathElement start = new MoveTo(0, 0);
PathElement outerTopRight = createBoundLineTo(root.widthProperty(), 0);
PathElement outerBottomRight = createBoundLineTo(root.widthProperty(), root.heightProperty());
PathElement outerBottomLeft = createBoundLineTo(0, root.heightProperty());
PathElement outerTopLeft = new LineTo(0, 0);
DoubleProperty innerLeft = new SimpleDoubleProperty(20);
DoubleProperty innerTop = new SimpleDoubleProperty(20);
DoubleBinding innerRight = innerLeft.add(180);
DoubleBinding innerBottom = innerTop.add(180);
PathElement innerTopLeft = createBoundLineTo(innerLeft, innerTop);
PathElement innerTopRight = createBoundLineTo(innerRight, innerTop);
PathElement innerBottomRight = createBoundLineTo(innerRight, innerBottom);
PathElement innerBottomLeft = createBoundLineTo(innerLeft, innerBottom);
Path path = new Path(
start, outerTopRight,
outerBottomRight, outerBottomLeft,
outerTopLeft,
innerTopLeft, innerBottomLeft,
innerBottomRight, innerTopRight,
innerTopLeft, new ClosePath()
);
path.setFill(Color.GRAY);
path.setStroke(Color.TRANSPARENT);
root.getChildren().add(path);
class Wrapper<T> { T value ; }
Wrapper<Point2D> mouseLocation = new Wrapper<>();
// Drag on gray portion of path - move entire window:
path.setOnDragDetected(event -> {
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
});
path.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
stage.setX(stage.getX() + event.getScreenX() - mouseLocation.value.getX());
stage.setY(stage.getY() + event.getScreenY() - mouseLocation.value.getY());
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
}
});
path.setOnMouseReleased(event -> mouseLocation.value = null);
// Drag on scene (i.e not on path, i.e. on transparent part) - move transparent part
root.setOnDragDetected(event -> {
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
});
root.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
innerLeft.set(innerLeft.get() + event.getScreenX() - mouseLocation.value.getX());
innerTop.set(innerTop.get() + event.getScreenY() - mouseLocation.value.getY());
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
}
});
root.setOnMouseReleased(event -> mouseLocation.value = null);
// No close button on a transparent window, so exit on double click:
root.setOnMouseClicked(event -> {
if (event.getClickCount() == 2) Platform.exit();
event.consume();
});
Scene scene = new Scene(root, 800, 600);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
private PathElement createBoundLineTo(ObservableDoubleValue x, ObservableDoubleValue y) {
LineTo lineTo = new LineTo();
lineTo.xProperty().bind(x);
lineTo.yProperty().bind(y);
return lineTo ;
}
private PathElement createBoundLineTo(double fixedX, ObservableDoubleValue y) {
LineTo lineTo = new LineTo();
lineTo.setX(fixedX);
lineTo.yProperty().bind(y);
return lineTo ;
}
private PathElement createBoundLineTo(ObservableDoubleValue x, double fixedY) {
LineTo lineTo = new LineTo();
lineTo.setY(fixedY);
lineTo.xProperty().bind(x);
return lineTo ;
}
public static void main(String[] args) {
launch(args);
}
}

Categories