I added the following functions to the previous sample application.File Tree View Sample - tomoTakaの日記
1. The context menu to expand tree item, delete directory or add new directory(Figure 1)
2. The context menu to delete file and the dialog to confirm it.(Figure 2, 3)
3. Editing the tree item(Renaming files or directories name) (Figure 4)
This code shows you how to use NIO2 to deal with above operations as well.
(see Using JavaFX UI Controls: Tree View | JavaFX 2 Tutorials and Documentation for more details)
Instantiate the Expand Menuitem and set the action.
MenuItem expandMenu = new MenuItem("Expand");
expandMenu.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
getTreeItem().setExpanded(true);
}
});
Instantiate the Expand All Menuitem and set the action.
MenuItem expandAllMenu = new MenuItem("Expand All");
expandAllMenu.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
expandTreeItem(getTreeItem());
}
private void expandTreeItem(TreeItem<PathItem> item) {
if (item.isLeaf()){
return;
}
item.setExpanded(true);
ObservableList<TreeItem<PathItem>> children = item.getChildren();
for (TreeItem<PathItem> child : children) {
if (!child.isLeaf()) {
expandTreeItem(child);
}
}
}
});
Instantiate the Add Directory Menuitem and set the action.
Creating new directory named newDirectory1 by Files#createDirectory method. Then create node with it and add the node to the tree item as a child.
MenuItem addMenu = new MenuItem("Add Directory");
addMenu.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
Path newDir = createNewDirectory();
if (newDir != null) {
TreeItem<PathItem> addItem = PathTreeItem.createNode(new PathItem(newDir));
getTreeItem().getChildren().add(addItem);
}
}
private Path createNewDirectory() {
Path newDir = null;
while (true) {
Path path = getTreeItem().getValue().getPath();
newDir = Paths.get(path.toAbsolutePath().toString(), "newDirectory" + String.valueOf(getItem().getCountNewDir()));
try {
Files.createDirectory(newDir);
break;
} catch (FileAlreadyExistsException ex) {
continue;
} catch (IOException ex) {
cancelEdit();
messageProp.setValue(String.format("Creating directory(%s) failed", newDir.getFileName()));
break;
}
}
return newDir;
}
});
Instantiate the Delete Menuitem and set the action.
When you click the delete Menuitem the modal dialog shows up to confirm it. If you chose the [OK] button, the prop's propertyChange event gets fired with the target path.
Then delete the directory or file by Files#walkFileTree method in the prop changed method. I am not sure that using the property for communicating with the dialog is a good way.....
MenuItem deleteMenu =new MenuItem("Delete");
deleteMenu.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
ObjectProperty<TreeItem<PathItem>> prop = new SimpleObjectProperty<>();
new ModalDialog(owner, getTreeItem(), prop);
prop.addListener(new ChangeListener<TreeItem<PathItem>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<PathItem>> ov, TreeItem<PathItem> oldItem, TreeItem<PathItem> newItem) {
try {
Files.walkFileTree(newItem.getValue().getPath(), new VisitorForDelete());
if (getTreeItem().getParent() == null){
// when the root is deleted how to clear the TreeView???
} else {
getTreeItem().getParent().getChildren().remove(newItem);
}
} catch (IOException ex) {
messageProp.setValue(String.format("Deleting %s failed", newItem.getValue().getPath().getFileName()));
}
}
});
}
});
Adding the above Menuitems to the context menu for directory and file.
dirMenu.getItems().addAll(expandMenu, expandAllMenu, deleteMenu, addMenu);
fileMenu.getItems().addAll(deleteMenu);
How to set the context menu
In updateItem method you should add the context menu to directory or file.
@Override
protected void updateItem(PathItem item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
if (!getTreeItem().isLeaf()) {
setContextMenu(dirMenu);
} else {
setContextMenu(fileMenu);
}
}
}
}
Editing the tree item.
When you start editing the tree item startEdit method called. So you should hold the editing item(editingPath in this code).
When you finish editing commitEdit method is called.
When you add the tree item startEdit and commitEdit method is called as well.
If textField is null it means you are adding it then you should create a tree item(see createTextField method)
If editingPath is not null it means you are editing so rename the path by FIles#move method in commitEdit method.
@Override
public void startEdit() {
super.startEdit();
if (textField == null){
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
if (getItem() == null) {
editingPath = null;
} else {
editingPath =getItem().getPath();
}
}
@Override
public void commitEdit(PathItem pathItem) {
// rename the file or directory
if (editingPath != null) {
try {
Files.move(editingPath, pathItem.getPath());
} catch (IOException ex) {
cancelEdit();
messageProp.setValue(String.format("Renaming %s filed", editingPath.getFileName()));
}
}
super.commitEdit(pathItem);
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(getString());
setGraphic(null);
}
private String getString() {
return getItem().toString();
}
private void createTextField() {
textField = new TextField(getString());
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER){
Path path = Paths.get(getItem().getPath().getParent().toAbsolutePath().toString(), textField.getText());
commitEdit(new PathItem(path));
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
}
How to create the modal dialog.
The prop's change event gets fired if you click the [OK] button.
public class ModalDialog { public ModalDialog(Stage owner, final TreeItem<PathItem> treeItem, final ObjectProperty<TreeItem<PathItem>> prop) { final Stage dialog = new Stage(StageStyle.UTILITY); dialog.initOwner(owner); dialog.initModality(Modality.APPLICATION_MODAL); GridPane root = new GridPane(); root.setPadding(new Insets(30)); root.setHgap(5); root.setVgap(10); Label label = new Label("Are you sure?"); Button okButton = new Button("OK"); okButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { prop.set(treeItem); dialog.hide(); } }); Button cancelButton = new Button("Cancel"); cancelButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { dialog.hide(); } }); root.add(label, 0, 0, 2, 1); root.addRow(1, okButton, cancelButton); dialog.setScene(new Scene(root)); dialog.show(); } }
Deleting files and directories.
You should delete all files under the director first.
This code shows you how to delete files or directories. It is easy thanks to the new NIO2.
public class VisitorForDelete extends SimpleFileVisitor<Path>{ @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.deleteIfExists(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.deleteIfExists(dir); return FileVisitResult.CONTINUE; } }
The cell factory is used to change tree item dynamically or add or delete it on demand.
fileTreeView.setEditable(true);
fileTreeView.setCellFactory(new Callback<TreeView<PathItem>, TreeCell<PathItem>>(){
@Override
public TreeCell<PathItem> call(TreeView<PathItem> p) {
return new PathTreeCell(stage, messageProp);
}
});
The whole code is here tomoTaka01/FileTreeViewSample ? GitHub
keep coding....
I just wanted to create an application like kind of file explorer to learn about the following contents.
This code shows how to use Directory Chooser(Figure 2)
final DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle("select Directory");
Image image = new Image(getClass().getResourceAsStream("OpenDirectory.png"));
Button chooserBtn = new Button("", new ImageView(image));
chooser.setTitle("Select Root Directory");
chooserBtn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
File selDir = chooser.showDialog(stage);
if (selDir != null) {
rootDirText.setText(selDir.getAbsolutePath());
}
}
});
This refers to the JavaFX API site[Class ListItem]
But I just wanted to use Path instead of File.
If you use Path for TreeItem it shows absolute paths.(Figure 3)
So I create PathItem class to show file names.(code 2-2)
public class PathTreeItem extends TreeItem<PathItem> { private boolean isLeaf = false; private boolean isFirstTimeChildren = true; private boolean isFirstTimeLeft = true; private PathTreeItem(PathItem pathItem) { super(pathItem); } public static TreeItem<PathItem> createNode(PathItem pathItem) { return new PathTreeItem(pathItem); } @Override public ObservableList<TreeItem<PathItem>> getChildren() { if (isFirstTimeChildren) { isFirstTimeChildren = false; super.getChildren().setAll(buildChildren(this)); } return super.getChildren(); } @Override public boolean isLeaf() { if (isFirstTimeLeft) { isFirstTimeLeft = false; Path path = getValue().getPath(); isLeaf = !Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS); } return isLeaf; } private ObservableList<TreeItem<PathItem>> buildChildren(TreeItem<PathItem> treeItem) { Path path = treeItem.getValue().getPath(); if (path != null && Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) { ObservableList<TreeItem<PathItem>> children = FXCollections.observableArrayList(); try (DirectoryStream<Path> dirs = Files.newDirectoryStream(path)) { for (Path dir : dirs) { PathItem pathItem = new PathItem(dir); children.add(createNode(pathItem)); } } catch (IOException ex) { Logger.getLogger(FileTreeViewSample.class.getName()).log(Level.SEVERE, null, ex); } return children; } return FXCollections.emptyObservableList(); } }
public class PathItem { private Path path; public PathItem(Path path) { this.path = path; } public Path getPath() { return path; } @Override public String toString() { if (path.getFileName() == null) { return path.toString(); } else { return path.getFileName().toString(); // showing file name on the TreeView } } }
This refers to the site Watching a Directory for Changes (The Java™ Tutorials > Essential Classes > Basic I/O)
public class WatchTask extends Task<Void>{ private Path path; private StringBuilder message = new StringBuilder(); public WatchTask(Path path) { this.path = path; } @Override protected Void call() throws Exception { WatchService watcher = FileSystems.getDefault().newWatchService(); path.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); while (true) { WatchKey key; try { key = watcher.take(); } catch (InterruptedException e) { break; } for (WatchEvent<?> event : key.pollEvents()) { if (event.kind() == OVERFLOW) { continue; } Path context = (Path) event.context(); message.append(context.toAbsolutePath()); message.append(getKindToMessage(event.kind())); message.append(System.getProperty("line.separator")); updateMessage(message.toString()); // to bind to the TextArea(see code 5-1) } boolean valid = key.reset(); if (!valid) { break; } } return null; } @Override protected void cancelled() { updateMessage("Watch task was cancelled"); // to bind to the TextArea(see code 5-1) } private String getKindToMessage(WatchEvent.Kind<?> kind) { if (kind == ENTRY_CREATE) { return " is created"; } else if (kind == ENTRY_DELETE) { return " is deleted"; } return " is updated"; } }
This code uses FileVisitor and PathMatcher to search files.
you can also use regular expressions, or regex, syntax as well.
public class SearchTask extends Task<Void> { private Path path; private String pattern; private StringProperty resultString; public SearchTask(Path path, String pattern) { this.path = path; this.pattern = pattern; resultString = new SimpleStringProperty(); } @Override protected Void call() throws Exception { updateProgress(0, 0); updateMessage("searching ..."); final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern); Files.walkFileTree(path, new SimpleFileVisitor<Path>() { int cnt = 0; @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (matcher.matches(file.getFileName())) { cnt++; resultString.setValue(file.toAbsolutePath().toString()); // to bind to the ListItem(see code5-2) updateMessage(String.format("%d files founded", cnt)); // to bind to the Label(see code5-2) updateProgress(cnt, cnt); // this does not work } return FileVisitResult.CONTINUE; } }); return null; } public StringProperty getResultString() { return resultString; } }
When watch checkbox checked WatchTask starts and shows the changes to the TextArea
public class FileTreeViewSample extends Application { ..... watchChkbox.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean oldVal, Boolean newVal) { if (newVal) { watchTask = new WatchTask(rootPath); service.submit(watchTask); watchText.textProperty().bind(watchTask.messageProperty()); // see code-3 } else { if (watchTask != null && watchTask.isRunning()) { watchTask.cancel(); watchText.textProperty().unbind(); } } } }); }
When search button is clicked the searchTask starts and shows the number of the results to the Label.
The label binding works but I do not know how to bind to the ListView.
So I use StringProperty[searchResult] to get the result from searchTask and add it to the ObservableList.
Once searchTask succeed the ObservableList is added to ListItem.
I think this is not the good way to bind but I do not how to implement ListBinding....
public class FileTreeViewSample extends Application { ..... searchBtn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { searchListItem.clear(); searchTask = new SearchTask(rootPath, patternText.getText()); searchCountLabel.textProperty().bind(searchTask.messageProperty()); // showing the number of files(see code-4) searchList.clear(); searchResult.bind(searchTask.getResultString()); searchTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() { @Override public void handle(WorkerStateEvent t) { searchListItem.addAll(searchList); // add all search results at once(see code-4) } }); // this bind does not work??? // searchCountLabel.textProperty().bind( // new StringBinding() { // { // bind(searchTask.progressProperty()); // } // @Override // protected String computeValue() { // return String.format("%1.0f files founded", searchTask.progressProperty().getValue()); // } // }); service.submit(searchTask); } }); // *** binding to search task result one by one *** searchResult.addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String oldVal, String newVal) { if (newVal != null) { searchList.add(newVal); } } }); }
The full code is here tomoTaka01/FileTreeViewSample ? GitHub
just keep coding...
I wanted to make NetBeans plugin showing JavaFX version(like Figure1) by using JavaFX.
But when I read the article NetBeans NetBeans IDE 7.2 Plugin Quick Start I decided to use Swing with JavaFX(like Figure2)
The content that is surrounded with the square of the orange is the JFXPanel.
(FYR:http://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htm#CHDIEEJE )
At first it was not difficult to develop the plugin thanks to the above article.
After the plugin runs, you see the icon on the toolbar(like Figure3)
You can use any icons when you create a plugin action.

The problem is when the I click the icon I see the window without the JFXPanel

I am pretty sure I am doing something wrong.
I hope someone gives me any advice.The whole code is here(tomoTaka01/JavaFXVersionInfo ? GitHub)
I am looking forward to creating the toolbar by JavaFX!!!
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionRegistration; import org.openide.util.NbBundle.Messages; @ActionID( category = "File", id = "org.myorg.javafxversioninfo.VersionListener") @ActionRegistration( iconBase = "org/myorg/javafxversioninfo/version.png", displayName = "#CTL_VersionListener") @ActionReference(path = "Toolbars/File", position = 0) @Messages("CTL_VersionListener=JavaFXVersion") public final class VersionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { SwingVersion swingVersion = new SwingVersion(); swingVersion.main(null); // Application.launch(JavaFXVersionDisplay.class); // this code does not work??? } }
import com.sun.javafx.runtime.VersionInfo; import java.awt.BorderLayout; import java.awt.Container; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.effect.InnerShadow; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class SwingVersion { private static void initAndShowGui() { JFrame jFrame = new JFrame("test"); final JFXPanel jfxPanel = new JFXPanel(); jFrame.add(jfxPanel); jFrame.setSize(500, 200); // JavaFX version by Swing String javaFxVer = VersionInfo.getRuntimeVersion(); JLabel jLabel = new JLabel("JavaFX version(by Swing) is " + javaFxVer); Container contentPane = jFrame.getContentPane(); contentPane.add(jLabel, BorderLayout.PAGE_START); jFrame.setVisible(true); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // this code does not work??? Platform.runLater(new Runnable() { @Override public void run() { initFX(jfxPanel); } }); } private static void initFX(JFXPanel fxpanel){ Scene scene = createScene(); fxpanel.setScene(scene); } private static Scene createScene() { GridPane grid = new GridPane(); grid.setAlignment(Pos.CENTER); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(25)); // *** for row size ColumnConstraints col1 = new ColumnConstraints(250); grid.getColumnConstraints().add(col1); // Title label InnerShadow is = new InnerShadow(); is.setOffsetX(0.1f); is.setOffsetY(0.1f); Text scenetitle = new Text("JavaFX Version Display"); scenetitle.setEffect(is); scenetitle.setFill(Color.AQUA); scenetitle.setFont(Font.font("Tahoma", FontWeight.BOLD, 36)); grid.add(scenetitle, 0, 0, 2, 1); // JavaFX version info Label l1 = new Label("Version from VersionInfo is "); l1.setFont(Font.font(null, FontWeight.NORMAL, 18)); grid.add(l1, 0, 1); final Text t1 = new Text(VersionInfo.getRuntimeVersion()); t1.setFont(Font.font(null, FontWeight.BOLD, 18)); grid.add(t1, 1, 1); // JavaFX version info Label l2 = new Label("Version from property is "); l2.setFont(Font.font(null, FontWeight.NORMAL, 18)); grid.add(l2, 0, 2); final Text t2 = new Text(System.getProperty("javafx.runtime.version")); t2.setFont(Font.font(null, FontWeight.BOLD, 18)); grid.add(t2, 1, 2); // Java version info Label l3 = new Label("Java Version is "); l3.setFont(Font.font(null, FontWeight.NORMAL, 18)); grid.add(l3, 0, 3); final Text t3 = new Text(System.getProperty("java.version")); t3.setFont(Font.font(null, FontWeight.BOLD, 18)); grid.add(t3, 1, 3); return new Scene(grid, Color.ALICEBLUE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { initAndShowGui(); } }); } }
keep coding...
I just want to share two results I have got on My Mac. I transcribed all of this article. Creating Visual Effects in JavaFX: About This Document | JavaFX 2 Tutorials and Documentation Also the article is written by @skrb using InvalidationListener.
The result that I saw was different from what I had expected. The one is blend mode and another is InvalidationListener.
This code shows using 3 type of blend mode. But Figure 1 shows the same result...
It seems like BlendMode.SRC_ATOP does not work on Mac?
(see Creating Visual Effects in JavaFX: Blending Objects | JavaFX 2 Tutorials and Documentation)
public class BlendSample extends Application { @Override public void start(Stage stage) throws Exception { HBox root = new HBox(); root.setPadding(new Insets(10)); root.setSpacing(8); // Blending Node node1 = getBlendMode(BlendMode.MULTIPLY, Color.BLUE); Node node2 = getBlendMode(BlendMode.SRC_ATOP, Color.ORANGE); // This has the different result Node node3 = getBlendMode(BlendMode.SRC_OVER, Color.AQUA); root.getChildren().addAll(node1, node2, node3); stage.setScene(new Scene(root, 500, 300)); stage.show(); } static Node getBlendMode(BlendMode mode, Color color) { Rectangle r = new Rectangle(); r.setX(590); r.setY(50); r.setWidth(50); r.setHeight(50); r.setFill(color); Circle c = new Circle(); c.setFill(Color.rgb(255, 0, 0, 0.5f)); c.setCenterX(590); c.setCenterY(50); c.setRadius(25); Group g = new Group(); g.setBlendMode(mode); g.getChildren().add(r); g.getChildren().add(c); return g; }
InvalidationListener called only once but it should be twice.
package javaapplication22; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.SimpleIntegerProperty; public class BindDemo { public static void main(String[] args) { SimpleIntegerProperty x = new SimpleIntegerProperty(0); SimpleIntegerProperty y = new SimpleIntegerProperty(); y.bind(x); y.addListener(new InvalidationListener() { @Override public void invalidated(Observable o) { System.out.println("Invalidate:" + o); } }); System.out.println(y.get()); // This code works without invalidationListener? x.set(10); x.set(20); System.out.println(y.get()); } }
When I run any JavaFX programs and maximize the screen then quit the application, the error message as follows appears on NetBeans.
Glass detected outstanding Java exception at -[GlassApplication runLoop:]:src/com/sun/mat/ui/GlassApplication.m:566
Exception in thread "JavaFX Application Thread" java.util.concurrent.RejectedExecutionException: Task com.sun.prism.render.RenderJob@343d0d2b rejected from com.sun.javafx.tk.quantum.QuantumRenderer@142b9cd8[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 53]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2013)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:816)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1337)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:110)
at com.sun.javafx.tk.quantum.QuantumRenderer.disposePresentable(QuantumRenderer.java:176)
at com.sun.javafx.tk.quantum.WindowStage.setScene(WindowStage.java:186)
at javafx.stage.Window$10.invalidated(Window.java:751)
at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:127)
at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:161)
at javafx.stage.Window.setShowing(Window.java:782)
at javafx.stage.Window.hide(Window.java:807)
at com.sun.javafx.stage.WindowCloseRequestHandler.dispatchBubblingEvent(WindowCloseRequestHandler.java:45)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
at javafx.event.Event.fireEvent(Event.java:171)
at com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:82)
at com.sun.javafx.tk.quantum.GlassStage.requestClosingAllWindows(GlassStage.java:203)
at com.sun.javafx.tk.quantum.QuantumToolkit$11.handleQuitAction(QuantumToolkit.java:347)
at com.sun.glass.ui.mac.MacApplication$4.action(MacApplication.java:104)
I would appreciate any advice since I am very beginner of JavaFX.
keep coding....
I wanted to use JavaFX for web pages test like Selenium, but I realized I have not had enough knowledge about JavaFX. So I started to learn about WebView and WebEngine.
WebView and 5 Buttons executing JavaScript commands
engine.load(...) starts a asynchronous loading and return immediately
WebView webView = new WebView();
engine = webView.getEngine();
engine.load("http://localhost:8080/HelloSample");
So the buttons should be disabled as long as the web page is loading, except first button
This code uses addListener method but there are some other ways (F.Y.R:JavaFX 2.2)
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
@Override
public void changed(ObservableValue<? extends State> ov, State oldState, State newState) {
if (newState == State.SUCCEEDED && engine.getTitle().equalsIgnoreCase("Hello")) {
btnHello.setDisable(false);
btnMorning.setDisable(false);
btnClear.setDisable(false);
btnNotClear.setDisable(false);
} else {
btnHello.setDisable(true);
btnMorning.setDisable(true);
btnClear.setDisable(true);
btnNotClear.setDisable(true);
}
}
});
After we click the [Greet Hello] button
add the below code to the button's setOnAction:
1. Input the JavaFX to text
2. click the Hello radio botton
3. select the English
4. click the greet button
public void handle(ActionEvent t) {
engine.executeScript("document.getElementById('form1:name').value = 'JavaFX'");
engine.executeScript("document.getElementsByName('form1:greetSel')[0].checked = true");
engine.executeScript("document.getElementById('form1:language').options[0].selected = true");
engine.executeScript("var btn = document.getElementById('form1:greet'); btn.click();");
}
After we click the [Greet おはよう] button
This code is almost the same to the Figure 2
public void handle(ActionEvent t) {
engine.executeScript("document.getElementById('form1:name').value = 'JavaFX'");
engine.executeScript("document.getElementsByName('form1:greetSel')[1].checked = true");
engine.executeScript("document.getElementById('form1:language').options[1].selected = true");
engine.executeScript("var btn = document.getElementById('form1:greet'); btn.click();");
}
NOTE:
After Figure 2, I wanted to click [Greet おはよう] without clicking the [load Hello Screen] button.
But I could not find out the way about how to manage web page loading and control button handle method.
When we uses browsers after click the [Clear Name] button
instead of this we have to use engine.setConfirmHandler method.
In this code shows how to control either ok or cancel button in the confirm window
I use the member variable isConfirmOK to do so, But I think there is another way....
btnClear.setOnAction(getClearNameAction(true)); // 「ClearName(OK)」button setting OK for confirm window
btnNotClear.setOnAction(getClearNameAction(false)); // 「ClearName(cancel)」button setting Cancel for confirm window
...
private EventHandler<ActionEvent> getClearNameAction(final boolean isOK) {
return new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
isConfirmOK = isOK;
engine.executeScript("var btn = document.getElementById('form1:clearButton'); btn.click();");
}
};
}
.....
engine.setConfirmHandler(getConfirmAction());
......
private Callback<String, Boolean> getConfirmAction() {
return new Callback<String, Boolean>() {
@Override
public Boolean call(String p) {
if (isConfirmOK) {
return true;
}
return false;
}
};
}
Please feel free to give me any advice anytime.
package test; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.concurrent.Worker.State; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; import javafx.util.Callback; /** * * @author tomo */ public class HelloSampleTest extends Application{ private WebEngine engine; private boolean isConfirmOK; @Override public void start(Stage stage) throws Exception { WebView webView = new WebView(); engine = webView.getEngine(); engine.load("http://localhost:8080/HelloSample"); VBox box = new VBox(); box.getChildren().add(webView); Button btnLoad = new Button("load Hello Screen"); final Button btnHello = new Button("Greet Hello"); btnHello.setDisable(true); final Button btnMorning = new Button("Greet おはよう"); btnMorning.setDisable(true); final Button btnClear = new Button("clearName(OK)"); btnClear.setDisable(true); final Button btnNotClear = new Button("clearName(cancel)"); btnNotClear.setDisable(true); engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends State> ov, State oldState, State newState) { if (newState == State.SUCCEEDED && engine.getTitle().equalsIgnoreCase("Hello")) { btnHello.setDisable(false); btnMorning.setDisable(false); btnClear.setDisable(false); btnNotClear.setDisable(false); } else { btnHello.setDisable(true); btnMorning.setDisable(true); btnClear.setDisable(true); btnNotClear.setDisable(true); } } }); HBox hbox = new HBox(); hbox.setPadding(new Insets(15,12,15,12)); hbox.setSpacing(10); hbox.setStyle("-fx-background-color: #336699;"); hbox.getChildren().addAll(btnLoad, btnHello, btnMorning, btnClear, btnNotClear); box.getChildren().add(hbox); btnLoad.setOnAction(getLoadAction()); btnHello.setOnAction(getHelloAction()); btnMorning.setOnAction(getおはようAction()); btnClear.setOnAction(getClearNameAction(true)); btnNotClear.setOnAction(getClearNameAction(false)); engine.setConfirmHandler(getConfirmAction()); stage.setScene(new Scene(box, 800, 300)); stage.show(); } private EventHandler<ActionEvent> getLoadAction() { return new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { engine.load("http://localhost:8080/HelloSample"); } }; } private EventHandler<ActionEvent> getHelloAction() { return new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { engine.executeScript("document.getElementById('form1:name').value = 'JavaFX'"); engine.executeScript("document.getElementsByName('form1:greetSel')[0].checked = true"); engine.executeScript("document.getElementById('form1:language').options[0].selected = true"); engine.executeScript("var btn = document.getElementById('form1:greet'); btn.click();"); } }; } private EventHandler<ActionEvent> getおはようAction() { return new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { engine.executeScript("document.getElementById('form1:name').value = 'JavaFX'"); engine.executeScript("document.getElementsByName('form1:greetSel')[1].checked = true"); engine.executeScript("document.getElementById('form1:language').options[1].selected = true"); engine.executeScript("var btn = document.getElementById('form1:greet'); btn.click();"); } }; } private EventHandler<ActionEvent> getClearNameAction(final boolean isOK) { return new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { isConfirmOK = isOK; engine.executeScript("var btn = document.getElementById('form1:clearButton'); btn.click();"); } }; } private Callback<String, Boolean> getConfirmAction() { return new Callback<String, Boolean>() { @Override public Boolean call(String p) { if (isConfirmOK) { return true; } return false; } }; } public static void main(String... args) { launch(args); } }
The code is hosted on gist JavaFX using WebView
So keep coding....