WebView - Adding a label to display status - java

I am using the following code with java 8 using javaFx.
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.Stage;
#SuppressWarnings("all")
public class Highlighter extends Application {
private boolean marked;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final WebView webView = new WebView();
final WebEngine engine = webView.getEngine();
engine.load("http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html");
final TextField searchField = new TextField("light");
searchField.setPromptText("Enter the text you would like to highlight and press ENTER to highlight");
searchField.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
if (engine.getDocument() != null) {
highlight(
engine,
searchField.getText()
);
}
}
});
final Button highlightButton = new Button("Highlight");
highlightButton.setDefaultButton(true);
highlightButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
searchField.fireEvent(new ActionEvent());
}
});
final Button markedButton = new Button("Mark it");
markedButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
marked = true;
}
});
markedButton.setCancelButton(true);
HBox controls = new HBox(10);
controls.getChildren().setAll(
highlightButton,
markedButton
);
VBox layout = new VBox(10);
layout.getChildren().setAll(searchField, controls, webView);
searchField.setMinHeight(Control.USE_PREF_SIZE);
controls.setMinHeight(Control.USE_PREF_SIZE);
controls.disableProperty().bind(webView.getEngine().getLoadWorker().runningProperty());
searchField.disableProperty().bind(webView.getEngine().getLoadWorker().runningProperty());
primaryStage.setScene(new Scene(layout));
primaryStage.show();
webView.requestFocus();
}
private void highlight(WebEngine engine, String text) {
engine.executeScript("$('body').removeHighlight().highlight('" + text + "')");
}
}
My problem is I want to add a label which displays the marked status of a page.
I tried simply adding a Label label = new Label("Marked: " + marked) to the controls, but this does not work.
Any recommendations how I could add a label to my code to display the marked status?
I appreciate your replies!

If you add a Label to controls with your actual code:
private boolean marked;
Label label = new Label("Marked: " + marked)
controls.getChildren().setAll(
highlightButton,
markedButton,
label
);
it will always show Marked: false, no matter if you change marked afterwards.
If you want that your control responds to changes, JavaFX has observable properties, as you can read here.
So you can replace the boolean primitive with this property that wraps the boolean value:
private final BooleanProperty marked=new SimpleBooleanProperty();
Create the label:
Label label=new Label("Marked: "+marked.get());
HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER_LEFT);
controls.getChildren().setAll(
highlightButton,
markedButton,
label
);
Change the event for markedButton:
markedButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
marked.set(true);
}
});
(this will work just once, since for now you don't have implemented a way to reset marked to false again)
And finally, add a listener for any change inmarked property:
marked.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
label.setText("Marked: "+newValue);
}
});
Instead of the listener, you can also use Bindings:
Label label=new Label();
label.textProperty().bind(Bindings.concat("Marked: ").concat(marked));

Related

JavaFX - Why does changing the header text of a dialog make the button bar invisible?

Here is a small sample from my custom dialog, which is meant to display the progress of a running javafx.concurrent.Task.
DialogPane pane = this.getDialogPane()
pane.getButtonTypes().addAll(ButtonType.CANCEL);
pane.headerTextProperty().bind(task.titleProperty());
pane.contentTextProperty().bind(task.messageProperty());
For some reason, the buttons in the button bar disappeared, but only after some text updated. After further investigation, I found that binding the header text of a dialog seems to remove all the buttons in the button bar. Why would this happen, and what would I do to stop the buttons from being hidden?
EDIT: Here's an MCVE demonstrating the problem.
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MCVE extends Application {
public static class CustomDialog extends Dialog<ButtonType> {
public CustomDialog(Task<?> task) {
this.initModality(Modality.APPLICATION_MODAL);
DialogPane pane = this.getDialogPane();
{
pane.getButtonTypes().addAll(ButtonType.CANCEL);
pane.headerTextProperty().bind(task.titleProperty());
}
setOnCloseRequest(event -> {
if (task.isRunning()) event.consume();
});
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
StackPane root = new StackPane();
Button starter = new Button("Showcase");
starter.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
root.getChildren().add(starter);
Scene scene = new Scene(root, 500, 500);
stage.setScene(scene);
stage.show();
starter.setOnAction(event -> {
Task<Void> task = new Task<>() {
#Override
protected Void call() throws Exception {
updateTitle("Before loop");
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
if (isCancelled()) return null;
}
for (int i = 1; i <= 20; i++) {
updateTitle("loop " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
if (isCancelled()) return null;
}
}
return null;
}
};
Thread worker = new Thread(task);
worker.start();
new CustomDialog(task).showAndWait();
});
}
}
It appears that the issue needs fixing on JavaFX's end. In fact, the button bar is simply pushed out of view. There is a relatively decent workaround though. Dialog internally uses a styled GridPane to display header text and graphics, so I simply replicated that using an external GridPane and instead bound to the textProperty() of a Label.
public static class CustomDialog extends Dialog<ButtonType> {
public CustomDialog(Task<?> task) {
DialogPane pane = this.getDialogPane(); {
pane.getButtonTypes().addAll(ButtonType.CANCEL);
//construct custom header
GridPane headerRoot = new GridPane(); {
Label headerText = new Label();
//headerText is the label containing the header text
headerText.textProperty().bind(task.titleProperty());
headerRoot.add(headerText, 0, 0);
}
headerRoot.getStyleClass().addAll("header-panel");
pane.setHeader(headerRoot);
pane.setContentText("Placeholder content");
pane.getScene().getWindow().setOnCloseRequest(event -> {
if (!task.isDone()) {
event.consume();
}
});
}
}
It looks exactly the same as a default dialog, without the issue mentioned in the question above.

Javafx - Update progress indicator in UI from java class

The following are the changes I made in fxml
Changes in the java file , here my code :
private ProgressIndicator pi;
void handlebuildButtonAction(ActionEvent event) throws IOException, GeneralSecurityException {
if ((entServer.isSelected()==true || compasServer.isSelected()==true)) {
if(!fileList.isEmpty()){
ProgressIndicator pi = new ProgressIndicator();
pi.setProgress(10);
}
}
The progress indicator is not updated when I run the application. I'm not sure how to sync the changes to UI. Assist me on this. Thanks in advance.
output
For example: if you set 0.1 - progress will be 10%, 0.2 - 20% and so on, so when you set the progress => 1 you will always have "done".
Here, this an example with a button, when you click the button, your progress indicator will be updated(one click + 10%):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
public class Test extends Application {
private ProgressIndicator pi;
private double counter = 0;
public void start(Stage stage)
{
ProgressIndicator pi = new ProgressIndicator();
Button button = new Button("Press");
TilePane root = new TilePane();
// action event
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
public void handle(ActionEvent e)
{
counter += 0.1;
pi.setProgress(counter);
}
};
button.setOnAction(event);
root.getChildren().add(button);
root.getChildren().add(pi);
// create a scene
Scene scene = new Scene(root, 200, 200);
// set the scene
stage.setScene(scene);
stage.show();
}
public static void main(String args[])
{
// launch the application
launch(args);
}
}
Just change this code for your case:
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
public void handle(ActionEvent e)
{
if ((entServer.isSelected()==true || compasServer.isSelected()==true)) {
if (!fileList.isEmpty()) {
counter += 0.1;
pi.setProgress(counter);
}
}
}
};
Hope that helps you!

Synchronize scrollbars of two JavaFx WebViews

I'm using two WebViews to display two versions of HTML formatted text for comparison. The two display the same amount of text (same number of lines and corresponding lines have always the same length).
When the displayed text exceeds the size of the node, the WebView gets scroll bars. Of course I want these scroll bars to scroll synchronously so that always the corresponding text is displayed.
In order to supply a minimal, complete and verifiable example, I trimmed the code down to this:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.GridPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class SynchronizedWebViewsTest extends Application {
protected class DifferencePanel extends GridPane {
private WebView actualPane;
private WebView expectedPane;
public DifferencePanel() {
setPadding(new Insets(20, 20, 20, 20));
actualPane = new WebView();
expectedPane = new WebView();
setResultPanes();
addRow(0, actualPane, expectedPane);
}
public void setHtml(WebView webView) {
Platform.runLater(() -> {
webView.getEngine().loadContent(createHtml());
});
}
public void synchronizeScrolls() {
final ScrollBar actualScrollBarV = (ScrollBar)actualPane.lookup(".scroll-bar:vertical");
final ScrollBar expectedScrollBarV = (ScrollBar)expectedPane.lookup(".scroll-bar:vertical");
actualScrollBarV.valueProperty().bindBidirectional(expectedScrollBarV.valueProperty());
final ScrollBar actualScrollBarH = (ScrollBar)actualPane.lookup(".scroll-bar:horizontal");
final ScrollBar expectedScrollBarH = (ScrollBar)expectedPane.lookup(".scroll-bar:horizontal");
actualScrollBarH.valueProperty().bindBidirectional(expectedScrollBarH.valueProperty());
}
private String createHtml() {
StringBuilder sb = new StringBuilder(1000000);
for (int i = 0; i < 100; i++) {
sb.append(String.format("<nobr>%03d %2$s%2$s%2$s%2$s%2$s%2$s%2$s%2$s</nobr><br/>\n",
Integer.valueOf(i), "Lorem ipsum dolor sit amet "));
}
return sb.toString();
}
private void setResultPanes() {
setHtml(actualPane);
setHtml(expectedPane);
}
} // ---------------------------- end of DifferencePanel ----------------------------
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage dummy) throws Exception {
Stage stage = new Stage();
stage.setTitle(this.getClass().getSimpleName());
DifferencePanel differencePanel = new DifferencePanel();
Scene scene = new Scene(differencePanel);
stage.setScene(scene);
differencePanel.synchronizeScrolls();
stage.showAndWait();
}
}
I tried using adding a listener:
actualScrollBarV.onScrollFinishedProperty().addListener(event -> {
System.out.println(event);
});
But the listener is never invoked.
I'm using Java version 1.8.0_92, but with version 9.0.4 I get the same result.
Can anybody tell me, what I'm missing here?
I would post a comment, but sadly I did not have enough reputation.
Did you tried the following solution? Create listeners on value changed event, instead of binding. Synchronizing two scroll bars JavaFX
I could not get the ScrollBar approach working. It turned out that the listeners were actually invoked (breakpoints in lambdas are not always working?). Setting the scroll bar value of the other WebView did not get it inclined change the scroll bar or the view port. :-(
There is something strange going on with events in WebView; that might be because there is a native library involved...
However, the approach using the event handler of WebView works. The event handler of each WebView simply mirrors all events to the other WebView, using a synchronizing field Boolean scrolling to avoid recursion.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class SynchronizedWebViewsTest extends Application {
protected class DifferencePanel extends GridPane {
private Boolean scrolling = Boolean.FALSE;
private WebView actualPane;
private WebView expectedPane;
public DifferencePanel() {
setPadding(new Insets(20, 20, 20, 20));
actualPane = new WebView();
expectedPane = new WebView();
setResultPanes();
addRow(0, actualPane, expectedPane);
}
public void setHtml(WebView webView) {
Platform.runLater(() -> {
webView.getEngine().loadContent(createHtml());
});
}
public void synchronizeScrolls() {
wireViews(actualPane, expectedPane);
wireViews(expectedPane, actualPane);
}
private void wireViews(WebView webView, WebView otherWebView) {
webView.addEventHandler(Event.ANY, event -> {
if (!scrolling.booleanValue()) {
synchronized (scrolling) {
scrolling = Boolean.TRUE;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
Point2D origin = webView.localToScreen(0, 0);
Point2D otherOrigin = otherWebView.localToScreen(0, 0);
double offsetX = otherOrigin.getX() - origin.getX();
double offsetY = otherOrigin.getY() - origin.getY();
double x = mouseEvent.getX();
double y = mouseEvent.getY();
double screenX = mouseEvent.getScreenX() + offsetX;
double screenY = mouseEvent.getScreenY() + offsetY;
MouseButton button = mouseEvent.getButton();
int clickCount = mouseEvent.getClickCount();
boolean shiftDown = mouseEvent.isShiftDown();
boolean controlDown = mouseEvent.isControlDown();
boolean altDown = mouseEvent.isAltDown();
boolean metaDown = mouseEvent.isMetaDown();
boolean primaryButtonDown = mouseEvent.isPrimaryButtonDown();
boolean middleButtonDown = mouseEvent.isMiddleButtonDown();
boolean secondaryButtonDown = mouseEvent.isSecondaryButtonDown();
boolean synthesized = mouseEvent.isSynthesized();
boolean popupTrigger = mouseEvent.isPopupTrigger();
boolean stillSincePress = mouseEvent.isStillSincePress();
MouseEvent otherMouseEvent =
new MouseEvent(otherWebView, otherWebView, mouseEvent.getEventType(), x, y, screenX,
screenY, button, clickCount, shiftDown, controlDown, altDown, metaDown,
primaryButtonDown, middleButtonDown, secondaryButtonDown, synthesized,
popupTrigger, stillSincePress, null);
otherWebView.fireEvent(otherMouseEvent);
}
else {
otherWebView.fireEvent(event.copyFor(otherWebView, otherWebView));
}
scrolling = Boolean.FALSE;
}
}
});
}
private String createHtml() {
StringBuilder sb = new StringBuilder(1000000);
for (int i = 0; i < 100; i++) {
sb.append(String.format("<nobr>%03d %2$s%2$s%2$s%2$s%2$s%2$s%2$s%2$s</nobr><br/>\n",
Integer.valueOf(i), "Lorem ipsum dolor sit amet "));
}
return sb.toString();
}
private void setResultPanes() {
setHtml(actualPane);
setHtml(expectedPane);
}
}
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage dummy) throws Exception {
Stage stage = new Stage();
stage.setTitle(this.getClass().getSimpleName());
DifferencePanel differencePanel = new DifferencePanel();
Scene scene = new Scene(differencePanel);
stage.setScene(scene);
differencePanel.synchronizeScrolls();
stage.showAndWait();
}
}
This works for all input methods I'm interested in:
Keyboard: PageUp, PageDown, all 4 arrow keys, "space bar" (same as PageDown) and shift-"space bar" (same as PageUp), Home and End
Mouse wheel: RollDown and RollUp as well as shift-RollUp (scroll left) and shift-RollDown (scroll right)
Using the mouse to click or to drag the scroll bar.
Using the mouse to select text outside of the current view port.
Mirroring the mouse events has the added benefit that text gets selected in both WebViews.

Java fx editable combobox with objects

I am just starting to learn Java Fx.
I have a combo box filled with objects. I dealt with toString() method, and I can see that name I wanted to display on the screen. But now I would like to make it editable, that user will enter its own text, and ComboBox will create a new object and put that text into the correct field. I know that problem is in converter FromString or ToString, but I cannot deal with it.
package mnet;
import javafx.application.Application;
import javafx.scene.control.ComboBox;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class sample extends Application {
Stage window;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Sample");
GridPane grid = new GridPane();
User usr1 = new User("Witold", "ciastko");
User usr2 = new User("Michał", "styk");
User usr3 = new User("Maciej", "masloo");
ComboBox<User> combo1 = new ComboBox<User>();
combo1.getItems().addAll(usr1, usr2, usr3);
combo1.setConverter(new StringConverter<User>() {
#Override
public String toString(User usr) {
return usr.getName();
}
#Override
public User fromString(String s) {
User usr = new User(s, "haslo");
combo1.getItems().add(usr);
return usr;
}
});
combo1.setEditable(true);
combo1.valueProperty().addListener((v, oldValue, newValue) -> {
System.out.println(newValue);
});
GridPane.setConstraints(combo1, 2, 1);
grid.getChildren().addAll(combo1);
Scene scene = new Scene(grid, 400, 200);
window.setScene(scene);
window.show();
}
}
package mnet;
public class User {
String user;
String password;
public User() {
this.user="";
this.password="";
}
public User(String user, String password){
this.user=user;
this.password=password;
}
public String getName(){
return this.user;
}
}
If I get rid of StringConverter it works correctly, but instead of name of user I only see address of Object like this "mnet.User#1f3b971"
EDIT: Added appropriately working code
You have a null pointer exception in you stringconverter since you can get a null User.
Your string converter should only convert User to/from String without modifying items since you don't know how many time it will be called.
To add a user I add an on event handler (when you type enter) on the combo that add a new user.
Note that thanks to the string converter you can call getValue on the combobox and get a user with the entered name
You should add a plus button to commit the user instead of my on event handler
here my working example :
public class Main extends Application {
Stage window;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Sample");
GridPane grid = new GridPane();
User usr1 = new User("Witold", "ciastko");
User usr2 = new User("Michał", "styk");
User usr3 = new User("Maciej", "masloo");
ComboBox<User> combo1 = new ComboBox<User>();
combo1.getItems().addAll(usr1, usr2, usr3);
combo1.setConverter(new StringConverter<User>() {
#Override
public String toString(User usr) {
return usr == null ? "" : usr.getName();
}
#Override
public User fromString(String s) {
User usr = new User(s, "haslo");
return usr;
}
});
combo1.setEditable(true);
combo1.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.ENTER) {
combo1.getItems().add(combo1.getValue());
}
});
GridPane.setConstraints(combo1, 2, 1);
grid.getChildren().addAll(combo1);
Scene scene = new Scene(grid, 400, 200);
window.setScene(scene);
window.show();
}

JavaFx Spreadsheet Cell Right Click Open Dialog

I am new to SpreadSheet functionality of ControlsFx Api. I would like to open Dialog on right click of Spreadsheetcell of SpreadsheetView in Javafx. Any help is greatly appreciated.
this is code where you can off the standard ContextMenu and implements own handler with Dialog, in this example TextInputDialog:
SpreadsheetView spreadsheetView = new SpreadsheetView();
//off the standard ContextMenu
spreadsheetView.setContextMenu(null);
//set own handler for right click with Dialog
spreadsheetView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override public void handle(ContextMenuEvent event) {
CellView cellView = (CellView) event.getTarget();
TextInputDialog dialog = new TextInputDialog(cellView.getText());
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
System.out.println(cellView.getText());
}
}
});
I don't know very good this library, but it works good.
Example how it works:
My program:
public class MainController extends Application {
public static void main(String[] args) {
launch(args);
}
#Override public void start(Stage primaryStage) throws Exception {
SpreadsheetView spreadsheetView = new SpreadsheetView();
//off the standard ContextMenu
spreadsheetView.setContextMenu(null);
//set own handler for right click with Dialog
spreadsheetView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override public void handle(ContextMenuEvent event) {
CellView cellView = (CellView) event.getTarget();
TextInputDialog dialog = new TextInputDialog(cellView.getText());
Optional<String> result = dialog.showAndWait();
if (result.isPresent()) {
System.out.println(cellView.getText());
}
}
});
HBox hBox = new HBox();
hBox.getChildren().add(spreadsheetView);
Scene scene = new Scene(hBox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
It is using mouse handler on the table view which checks when mouse is clicked and on clicking it fires a new dialogue in fx and then accepts the input and updates the fx table view.
table.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 1) {
Call dialogue method of java fx
}
}
});
Or if you want right click you can create cell
Eg
FirstNameCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> col) {
final TableCell<Person, String> cell = new TableCell<>();
cell.textProperty().bind(cell.itemProperty()); // in general might need to subclass TableCell and override updateItem(...) here
cell.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton == MouseButton.SECONDARY) {
// handle right click on cell...
// access cell data with cell.getItem();
// access row data with (Person)cell.getTableRow().getItem();
}
}
});
return cell ;
}
});

Categories

Resources