scrollbar and alignment issue with JavaFX web view - javafx

I am using Javafx webview to embed a webpage inside a standalone Java application. The application has a small frame size of 415 by 180. I would like the top left hand corner of the webpage to appear in the top left corner of the jfxPanel. For some reason the webpage LHS appears further to the left of my jfxPanel LHS. I could tweak the page with css to indent the page but was wondering if there is an easy way to solve this in my java code?
Also, the scroll bars have disappeared. I only need the vertical scroll bar. Any ideas what I need to change so that I can have a vertical scroll bar?
In my main java program I have:
JPanel jPanel = new JPanel();
browser browser = new browser(jPanel);
browser.loadURL("http://www.url.com");
jtp.addTab("tab", jPanel);
The bowser class is:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javax.swing.*;
import java.awt.*;
import java.net.MalformedURLException;
import java.net.URL;
public class browser extends JFrame {
private final JFXPanel jfxPanel = new JFXPanel();
private WebEngine engine;
private JPanel panel;
public browser(JPanel panel) {
super();
this.panel = panel;
initComponents();
}
private void initComponents() {
createScene();
panel.add(jfxPanel, BorderLayout.CENTER);
}
private void createScene() {
Platform.runLater(new Runnable() {
#Override
public void run() {
WebView view = new WebView();
engine = view.getEngine();
jfxPanel.setScene(new Scene(view));
}
});
}
public void loadURL(final String url) {
Platform.runLater(new Runnable() {
#Override
public void run() {
String tmp = toURL(url);
if (tmp == null) {
tmp = toURL("http://" + url);
}
engine.load(tmp);
}
});
}
private static String toURL(String str) {
try {
return new URL(str).toExternalForm();
} catch (MalformedURLException exception) {
return null;
}
}
}

Related

How to refresh JavaFX screen twice after sleep?

I am using JavaFX to show images on the screen. My FxScreenController code concept is like:
package my.image.concept;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import org.springframework.stereotype.Component;
#Component
public class FxScreenController {
private ImageSenderClient imageClient;
#FXML
private ImageView imageView;
private Image image;
public FxScreenController(Image image) {
this.image = image;
}
#FXML
public void initialize(){
final ImageSubscriber imageSubscriber = new ImageSubscriber(image);
imageClient.GetImageFrom().subscribe(imageSubscriber);
}
private static class ImageSubscriber implements Consumer<Image> {
private ImageView imageFile;
public ImageSubscriber(Image newImage) {
this.imageFile.setImage(newImage);
}
#Override
public void accept(Image newImage) {
Platform.runLater(() -> imageFile.setImage(newImage));
}
}
}
I am displaying an image that comes from the Subscriber method.
In order to show image on JavaFX Screen, I am calling Platform.runLater(). The image is shown properly.
I would like to show my previous image back after a 2-seconds pause like this:
#Override
public void accept(Image newImage) {
Platform.runLater(() -> imageFile.setImage(newImage));
try {
Thread.sleep(2000); // Wait for 2 sec before updating back to prev image
} catch (InterruptedException ignored) {
//
}
Platform.runLater(() -> imageFile.setImage(prewImage));
}
However, here it doesn't show my newImage because it doesn't refresh the screen.
How can I can refresh the FX screen before calling Thread.sleep, so that I can show my newImage for 2 seconds on the FX screen?
Creating new Thread solved my problem.
#Override
public void accept(Image newImage) {
Thread timerThread = new Thread(() -> displayImage(newImage));
timerThread.start();
}
private void displayImage(Image newImage) {
Platform.runLater(() ->
imageBg.setImage(newImage)
);
try {
Thread.sleep(2000); // Wait for 2 sec before updating back to main
}
catch (InterruptedException ignored) {
}
imageBg.setImage(prevImage);
}

Sound stops working after pressing a key 53 times

I'm creating a game in JavaFX (something like Space Invaders) and I'm having problems with the shooting sound, particularly when I press a key quite a few times not only the sound stops being played but other sounds also stop working.
I've done some small research and it seems that this kind of problem is fairly popular and it involves releasing the MediaPlayer object/instance but I can't call that method(?).
I've tried using dispose() method but it disables the shot sound completely.
I have two classes, GameApp:
primaryStage.getScene().setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE && playable) {
Audio.playPlayerShotSound();
}
}
});
and Audio:
public class Audio {
private static Media shot = new Media(new File("resources/playerShot.wav").toURI().toString());
public static void playPlayerShotSound() {
MediaPlayer shotSound = new MediaPlayer(shot);
shotSound.setVolume(0.2);
shotSound.play();
}
I've created another class using JavaFX and the sound stops after pressing Space 64 times.
package examples;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.scene.layout.Pane;
import java.io.File;
public class GameInst extends Application {
Pane root;
private AnimationTimer timer;
private static Media shot = new Media(new File("resources/playerShot.wav").toURI().toString());
int count = 0;
private Parent createContent() {
root = new Pane();
root.setPrefSize(500, 500);
timer = new AnimationTimer() {
#Override
public void handle(long now) {
onUpdate();
}
};
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
onUpdate();
}
};
timer.start();
return root;
}
private void onUpdate() {
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Pane();
primaryStage.setTitle("Space Invaders");
primaryStage.setScene(new Scene(createContent()));
primaryStage.getScene().setOnKeyPressed(event -> {
switch (event.getCode()) {
case SPACE:
MediaPlayer shotSound = new MediaPlayer(shot);
shotSound.setVolume(0.1);
shotSound.play();
count++;
System.out.println(count);
}
});
primaryStage.show();
}
public static void main (String[] args){
launch(args);
}
}
For what you are trying to do the AudioClip class is probably much better suited as the full blown MediaPlayer. It also probably not a good idea to initialize a new player every time you get an event.

JavaFX How to position the Mouse

I am trying to make a little game and in the most games the mouse gets locked in the center of the screen. So, is it possible to lock the mouse in the center of the screen or set the position of the mouse in JavaFX? I know that it is possible to do, and I also know some samples written in LWJGL or just with the AWT/SWING package.
Thanks for help.
Update 11/27/2019
From now you can use also JavaFX Robot API:
https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/robot/Robot.html
Here is the code you need:
import java.awt.AWTException;
import java.awt.Robot;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class MoveCursor extends Application {
Scene scene;
VBox container;
Button moveMouse;
Button showHideCursor;
public static int screenWidth = (int) Screen.getPrimary().getBounds().getWidth();
public static int screenHeight = (int) Screen.getPrimary().getBounds().getHeight();
#Override
public void start(Stage stage) throws Exception {
// MoveMouse Button
moveMouse = new Button("Move Cursor to the center of Screen");
moveMouse.setOnAction(m -> {
moveCursor(screenWidth/2, screenHeight/2);
});
// ShowHide Cursor
showHideCursor = new Button("Show/Hide Cursor");
showHideCursor.setCursor(Cursor.HAND);
showHideCursor.setOnAction(m -> {
if (scene.getCursor() != Cursor.NONE)
scene.setCursor(Cursor.NONE);
else
scene.setCursor(Cursor.DEFAULT);
});
// Container
container = new VBox();
container.getChildren().addAll(moveMouse, showHideCursor);
// Scene
scene = new Scene(container, 500, 500);
stage.setScene(scene);
stage.show();
}
/**
* Move the mouse to the specific screen position
*
* #param x
* #param y
*/
public void moveCursor(int screenX, int screenY) {
Platform.runLater(() -> {
try {
Robot robot = new Robot();
robot.mouseMove(screenX, screenY);
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
public static void main(String[] args) {
launch(args);
}
}

Textarea scrollpane hold in text append

It is my test code of textarea append text,
public class TextAreaScrollHold extends Application {
TextArea area = new TextArea();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(area);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
addTextInTextArea();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public void addTextInTextArea() {
for (int i = 0; i < 15; i++) {
area.appendText("Hello World " + i + "\n");
}
Task<Void> task = new Task() {
#Override
protected Void call() throws Exception {
for (int i = 15; i < 100; i++) {
area.appendText("Hello World " + i + "\n");
Thread.sleep(1000);
}
return null;
}
};
new Thread(task).start();
}
}
It my code data will update in thread. i need how to hold in scroll bar when data update in textarea. I have ref JavaFX TextArea and autoscroll and Access to TextArea's Scroll Pane or Scroll Bars but how solve this problems.
I need
When data update in textarea, i will scroll the text area scrollbar the bar will hold.
textArea.scrollTopProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
textArea.setScrollTop(100);
}
});
I have used this code but scroll bar in not moved bar will fixed in pixel 100 positions
You can use getCaretPostion and postionCaret (yes, that setter's method name is awkward for Java).
I quickly drafted up some code for you, use the scroll lock button to enable/disable scrolling:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ConsoleDemo extends Application {
Console console = new Console();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(console);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Console Demo");
primaryStage.setScene(scene);
primaryStage.show();
addTextInTextArea();
}
/**
* #param args
* the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public void addTextInTextArea() {
for (int i = 0; i < 15; i++) {
console.log("Hello World " + i);
}
Task<Void> task = new Task() {
#Override
protected Void call() throws Exception {
for (int i = 15; i < 100; i++) {
console.log("Hello World " + i);
Thread.sleep(1000);
}
return null;
}
};
new Thread(task).start();
}
public class Console extends BorderPane {
TextArea textArea = new TextArea();
int scrollLockPos = -1;
public Console() {
HBox toolbar = new HBox();
ToggleButton scrollLockButton = new ToggleButton("Scroll Lock");
scrollLockButton.setOnAction(e -> {
if (scrollLockButton.isSelected()) {
scrollLockPos = textArea.getCaretPosition();
} else {
scrollLockPos = -1;
}
});
HBox.setMargin(scrollLockButton, new Insets(5, 5, 5, 5));
toolbar.getChildren().add(scrollLockButton);
setCenter(textArea);
setTop(toolbar);
}
public void log(String text) {
textArea.appendText(text + "\n");
if (scrollLockPos != -1) {
textArea.positionCaret(scrollLockPos);
}
}
}
}
Not the nicest solution, but unless you want to use selection in the textarea while it's scrolling is locked, this one works. For a proper solution you'd need access to the skin / scrollpane / scrollbars and with the upcoming Java 9 version and its modularization you don't know what you will have access to since access to them is currently flagged as "restricted".
Edit:
Here's an alternate solution which uses the Range, console component only. With this version you can select text and keep the selection while the Scroll Lock button is down:
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.IndexRange;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
/**
* Console which provides a mechanism to lock scrolling. Selecting text and copying it works while scrolling is locked.
*/
public class Console extends BorderPane {
TextArea textArea = new TextArea();
ToggleButton scrollLockButton;
IndexRange range;
public Console() {
initComponents();
}
private void initComponents() {
// toolbar
HBox toolbar = new HBox();
toolbar.setAlignment(Pos.CENTER_RIGHT);
// clear
Button clearButton = new Button("Clear");
clearButton.setOnAction(e -> {
textArea.clear();
});
// scroll lock
scrollLockButton = new ToggleButton("Scroll Lock");
// button positions & layout
Insets insets = new Insets(5, 5, 5, 5);
HBox.setMargin(clearButton, insets);
HBox.setMargin(scrollLockButton, insets);
toolbar.getChildren().addAll(clearButton,scrollLockButton);
// component layout
setCenter(textArea);
setTop(toolbar);
}
public void log(String text) {
if (scrollLockButton.isSelected()) {
range = textArea.getSelection();
}
textArea.appendText(text + "\n");
if (scrollLockButton.isSelected()) {
textArea.selectRange(range.getStart(), range.getEnd());
}
}
}

Collapse menu at the bottom of the stage

Based on this example
https://gist.github.com/SaiPradeepDandem/95accfa4d8de8b9b3310
I created this simple example code:
import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MainApp extends Application
{
BorderPane bp = new BorderPane();
public static void main(String[] args) throws Exception
{
launch(args);
}
#Override
public void start(final Stage stage) throws Exception
{
stage.setTitle("Slide out YouTube demo");
// create a WebView to show to the right of the SideBar.
bp.setStyle("-fx-background-color: #2f4f4f;");
bp.setPrefSize(800, 600);
// create a sidebar with some content in it.
final Pane lyricPane = createSidebarContent();
SideBar sidebar = new SideBar(250, lyricPane);
VBox.setVgrow(lyricPane, Priority.ALWAYS);
// layout the scene.
final BorderPane layout = new BorderPane();
StackPane st = new StackPane();
st.getChildren().addAll(bp, sidebar.getControlButton());
st.setAlignment(Pos.TOP_LEFT);
VBox vb = new VBox(10);
vb.getChildren().addAll(st);
layout.setLeft(sidebar);
layout.setCenter(vb);
// show the scene.
Scene scene = new Scene(layout);
scene.getStylesheets().add(getClass().getResource("/styles/slideout.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private BorderPane createSidebarContent()
{
// create some content to put in the sidebar.
final Button changeLyric = new Button("New Song");
changeLyric.getStyleClass().add("change-lyric");
changeLyric.setMaxWidth(Double.MAX_VALUE);
changeLyric.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
System.out.println("Some action");
}
});
changeLyric.fire();
final BorderPane lyricPane = new BorderPane();
lyricPane.setTop(changeLyric);
return lyricPane;
}
/**
* Animates a node on and off screen to the left.
*/
class SideBar extends VBox
{
/**
* #return a control button to hide and show the sidebar
*/
public Button getControlButton()
{
return controlButton;
}
private final Button controlButton;
/**
* creates a sidebar containing a vertical alignment of the given nodes
*/
SideBar(final double expandedWidth, Node... nodes)
{
getStyleClass().add("sidebar");
this.setPrefWidth(expandedWidth);
this.setMinWidth(0);
// create a bar to hide and show.
setAlignment(Pos.CENTER);
getChildren().addAll(nodes);
// create a button to hide and show the sidebar.
controlButton = new Button("Collapse");
controlButton.getStyleClass().add("hide-left");
// apply the animations when the button is pressed.
controlButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
// create an animation to hide sidebar.
final Animation hideSidebar = new Transition()
{
{
setCycleDuration(Duration.millis(250));
}
#Override
protected void interpolate(double frac)
{
final double curWidth = expandedWidth * (1.0 - frac);
setPrefWidth(curWidth);
setTranslateX(-expandedWidth + curWidth);
}
};
hideSidebar.onFinishedProperty().set(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
setVisible(false);
controlButton.setText("Show");
controlButton.getStyleClass().remove("hide-left");
controlButton.getStyleClass().add("show-right");
}
});
// create an animation to show a sidebar.
final Animation showSidebar = new Transition()
{
{
setCycleDuration(Duration.millis(250));
}
#Override
protected void interpolate(double frac)
{
final double curWidth = expandedWidth * frac;
setPrefWidth(curWidth);
setTranslateX(-expandedWidth + curWidth);
}
};
showSidebar.onFinishedProperty().set(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
controlButton.setText("Collapse");
controlButton.getStyleClass().add("hide-left");
controlButton.getStyleClass().remove("show-right");
}
});
if (showSidebar.statusProperty().get() == Animation.Status.STOPPED && hideSidebar.statusProperty().get() == Animation.Status.STOPPED)
{
if (isVisible())
{
hideSidebar.play();
}
else
{
setVisible(true);
showSidebar.play();
}
}
}
});
}
}
}
How I can display the collapse menu at the bottom of the stage?
Also is there a way to display the collapsed panel closed when the stage is loaded.
Using your code:
import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MainApp extends Application
{
BorderPane bp = new BorderPane();
public static void main(String[] args) throws Exception
{
launch(args);
}
#Override
public void start(final Stage stage) throws Exception
{
stage.setTitle("Slide out YouTube demo");
// create a WebView to show to the right of the SideBar.
bp.setStyle("-fx-background-color: #2f4f4f;");
bp.setPrefSize(800, 600);
// create a sidebar with some content in it.
final Pane lyricPane = createSidebarContent();
SideBar sidebar = new SideBar(250, lyricPane);
VBox.setVgrow(lyricPane, Priority.ALWAYS);
// layout the scene.
final BorderPane layout = new BorderPane();
StackPane st = new StackPane();
st.getChildren().addAll(bp, sidebar.getControlButton());
st.setAlignment(Pos.TOP_LEFT);
VBox vb = new VBox(10);
vb.getChildren().addAll(st);
layout.setBottom(sidebar);
layout.setCenter(vb);
// show the scene.
Scene scene = new Scene(layout);
//scene.getStylesheets().add(getClass().getResource("/styles/slideout.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private BorderPane createSidebarContent()
{
// create some content to put in the sidebar.
final Button changeLyric = new Button("New Song");
changeLyric.getStyleClass().add("change-lyric");
changeLyric.setMaxWidth(Double.MAX_VALUE);
changeLyric.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
System.out.println("Some action");
}
});
changeLyric.fire();
final BorderPane lyricPane = new BorderPane();
lyricPane.setTop(changeLyric);
return lyricPane;
}
/**
* Animates a node on and off screen to the left.
*/
class SideBar extends VBox
{
/**
* #return a control button to hide and show the sidebar
*/
public Button getControlButton()
{
return controlButton;
}
private final Button controlButton;
/**
* creates a sidebar containing a vertical alignment of the given nodes
*/
SideBar(final double expandedWidth, Node... nodes)
{
getStyleClass().add("sidebar");
this.setPrefWidth(expandedWidth);
this.setMinWidth(0);
// create a bar to hide and show.
setAlignment(Pos.CENTER);
getChildren().addAll(nodes);
// create a button to hide and show the sidebar.
controlButton = new Button("Collapse");
controlButton.getStyleClass().add("hide-left");
// apply the animations when the button is pressed.
controlButton.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
// create an animation to hide sidebar.
final Animation hideSidebar = new Transition()
{
{
setCycleDuration(Duration.millis(250));
}
#Override
protected void interpolate(double frac)
{
final double curWidth = expandedWidth * (1.0 - frac);
setPrefHeight(curWidth);
setTranslateY(-expandedWidth + curWidth);
}
};
hideSidebar.onFinishedProperty().set(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
setVisible(false);
controlButton.setText("Show");
controlButton.getStyleClass().remove("hide-left");
controlButton.getStyleClass().add("show-right");
}
});
// create an animation to show a sidebar.
final Animation showSidebar = new Transition()
{
{
setCycleDuration(Duration.millis(250));
}
#Override
protected void interpolate(double frac)
{
final double curWidth = expandedWidth * frac;
setPrefHeight(curWidth);
setTranslateY(-expandedWidth + curWidth);
}
};
showSidebar.onFinishedProperty().set(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent actionEvent)
{
controlButton.setText("Collapse");
controlButton.getStyleClass().add("hide-left");
controlButton.getStyleClass().remove("show-right");
}
});
if (showSidebar.statusProperty().get() == Animation.Status.STOPPED && hideSidebar.statusProperty().get() == Animation.Status.STOPPED)
{
if (isVisible())
{
hideSidebar.play();
}
else
{
setVisible(true);
showSidebar.play();
}
}
}
});
}
}
}
This makes it expand 'up' when the button is clicked. It's a little jumpy on the first click, I'm at work so I can't debug that bit of it very extensively, but essentially you just set the custom sidebar object at the bottom of the border pane, and then you switch all the .width and .X methods to be .height and .Y methods instead. With some tweaking I'm sure you could also get it to expand 'down' instead.

Resources