Currently im doing my thesis work, and I'm making a component library with javafx.
But im dealing a problem that I wish I could change the icon that puts the tool scene builder imported .jar.
In Scene Builder 2.0 icons for custom controls from JAR files are set by private Collection<LibraryItem> makeLibraryItems(JarReport jarReport) in the private LibraryItem makeLibraryItem(Path path) function. Currently the source for that function is:
private Collection<LibraryItem> makeLibraryItems(JarReport jarReport) throws IOException {
final List<LibraryItem> result = new ArrayList<>();
final URL iconURL = ImageUtils.getNodeIconURL(null);
final List<String> excludedItems = library.getFilter();
for (JarReportEntry e : jarReport.getEntries()) {
if ((e.getStatus() == JarReportEntry.Status.OK) && e.isNode()) {
// We filter out items listed in the excluded list, based on canonical name of the class.
final String canonicalName = e.getKlass().getCanonicalName();
if (! excludedItems.contains(canonicalName)) {
final String name = e.getKlass().getSimpleName();
final String fxmlText = JarExplorer.makeFxmlText(e.getKlass());
result.add(new LibraryItem(name, UserLibrary.TAG_USER_DEFINED, fxmlText, iconURL, library));
}
}
}
return result;
}
As you can see the iconURL is not based on anything supplied by your control's JAR so it is currently not possible to supply an icon. This would require a change to Scene Builder (this could be done after all it's open source).
I know this question is a bit old, but hopefully this helps somebody.
Related
This is the background for my question:
I have a GUI with an accordion with many TitledPanes, and each Titledpane contains a spreadsheetView from the controlsFX package.
There is a search-function in the code, where a Titledpane is opened and a specific cell in the spreadsheetView is opened for text input using the edit method of the spreadsheetcell type.
If the TitledPane is already open, this works fine, but if it must open first then the call of the edit-method fails. (The program is actually written in scalafx, but I don't think that matters here because scalafx is just a wrapper of javaFX and calls all the javaFX methods.)
Someone from the scalafx user group found out, that when I put in a wait time of 350ms (The animation time of the TitledPane is 300ms) then the call of 'edit' on the cell succeeds. He thought that the call fails, when the rendering of the content of the TitledPane is not complete.
This is also true when I turn the animation for the TitledPane off. In this case, it is sufficient to wait for 50ms, which does not work when animation is on.
Anyway - I am concerned about just waiting 350ms and hoping that this will always work. Which brings me back to the question: How can I tell that the rendering inside the TitledPane (or the spreadsheetView?) is complete so that I can safely call my edit method on the spreadsheetView?
Astonishingly, that doesn't seem to be supported.
The property that changes during the expand/collapse phase is the content's height: so a hack around might be to listen to it and start editing when fully expanded (which is a bit hacky in itself, could change due to layout constraints as well).
The example below simply initializes the fully expanded height after showing, listens to content's height property and starts editing when it reaches the fully expanded height.
The code:
public class TitledPaneEndOfExpansion extends Application {
private DoubleProperty expandedHeight = new SimpleDoubleProperty();
private TitledPane titled = new TitledPane();
private Parent createContent() {
titled.setText("Titled");
ListView<String> list = new ListView<>(FXCollections.observableArrayList("some", "content"));
list.setEditable(true);
list.setCellFactory(TextFieldListCell.forListView());
titled.setContent(list);
list.heightProperty().addListener((src, ov, nv) -> {
if (nv.doubleValue() == expandedHeight.get()) {
list.edit(0);
}
});
BorderPane content = new BorderPane(titled);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setTitle(FXUtils.version());
stage.show();
expandedHeight.set(((Region) titled.getContent()).getHeight());
}
public static void main(String[] args) {
launch(args);
}
}
Basically I like kleopatras idea, but unfortunately I can't figure out if this works for me or not.
At first I had some problems reading the code - only because my java knowledge is very limited. So I transferred it to scala. When I run it there, the call to edit works only sometimes (after startup it does not, when i clicked into a cell to edit it does). So I added a button that also calls edit - and it had the same behavior. So calling edit in general seems to have a problem in scalafx. But I learned something interesting here. I will now wait a few more days to see if anyone can think of anything else. If not then I will accept kleopatras solution.
For my own reference I add my not working scala-code here:
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane
object TitledPaneEndOfExpansion extends JFXApp {
val expandedHeight = new DoubleProperty()
val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")
stage = new JFXApp.PrimaryStage {
title = "JavaFX: edit after rendering test"
val list: ListView[String] = new ListView[String](data) {
editable = true
cellFactory = TextFieldListCell.forListView()
height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
println("old height is: " + oldValue.doubleValue() + " new height is: " + newValue.doubleValue())
if (newValue.doubleValue() == expandedHeight.value) {
edit(1)
}
}
}
val titled: TitledPane = new TitledPane {
text = "titled"
content = list
}
scene = new Scene {
root = new BorderPane {
center = titled
bottom = new Button() {
text = "edit cell 1"
onAction = { _: ActionEvent => list.edit(1) }
}
}
}
expandedHeight.value = 400
list.edit(1)
}
}
I have a project I am working on. I am trying to make a dictionary. For that, I have a .csv file with about 55000 words.I am using the trie data structure which has a startsWith() method which checks whether there is a word in the .csv file which matches the given prefix. I had managed to get it to work to find all words that match the given prefix and display them. Now, I have to develop this into a JavaFX app.
So, I thought of using a ComboBox which has its editable attribute set to true so that I could type into it and then the handler associated with the textProperty() of its editor would display all the words starting with given prefix in the listview of the combobox.
Now, the problem I have is that whenever I click the arrow button of the combobox the application stops responding (I think it's because the list view tries to resize itself to fit the items which are 55000).
So, what I want to know is how to disable the arrow button entirely. I have tried to set its background-color to transparent but even then it can still be clicked I want to make it so that it is disabled and transparent basically the combobox ends up looking like a text field.
If there are better, more efficient ways of implementing a dictionary I would appreciate it if you could guide me.
The ListView is a virtual control that only shows a certain number of cells at a time, it doesn't need to "resize itself to the number of items" in any way that would lock up your GUI.
Does this demo program do what you want?
public class Main extends Application {
public static void main(String[] args) throws IOException, URISyntaxException {
launch(args);
}
#Override
public void start(Stage stage) {
List<String> rawWords = Collections.emptyList();
try {
URI wordURI = new URI("https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(wordURI.toURL().openStream(), StandardCharsets.UTF_8));
rawWords = reader.lines().collect(Collectors.toCollection(() -> new ArrayList<>(6000)));
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
// make the list at least as big as in the question
while(rawWords.size() < 55000) {
ArrayList<String> nextWords = new ArrayList<>(rawWords.size() * 2);
nextWords.addAll(rawWords);
nextWords.addAll(rawWords);
rawWords = nextWords;
}
Collections.sort(rawWords);
ObservableList<String> wordList = FXCollections.observableArrayList(rawWords);
FilteredList<String> filteredList = new FilteredList<>(wordList);
ComboBox<String> combo = new ComboBox<>(filteredList);
combo.setEditable(true);
combo.getEditor().textProperty().addListener((obs, oldVal, newVal) -> {
filteredList.setPredicate(s -> newVal == null || newVal.isEmpty() || s.startsWith(newVal));
});
VBox vbox = new VBox(8,new Label("Dictionary ComboBox"),
combo,
new Label("\n\n\n\nThis space intentionally left blank.\n\n\n\n"));
vbox.setPadding(new Insets(8));
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Demo - Filtered Combobox List");
stage.setScene(scene);
stage.show();
}
}
I am building an application using JavaFX. What I am trying to do is generate a message according to the user input values. So there are one text-field and one combo-box and one check-box per row and there are many rows like the following.
Let's say I will generate three different messages according to the user values. So I need to check whether those fields are empty or not and check each field's value to generate a specific message. Checking fields are okay for just three rows like the above. But I have 10 fields. So I have to check each and generate or append my own message. And also if the user checked the check-box need to group all checked row values. So what I am asking is there any good way (best practice) to achieve what I need or an easy one also? I have tried with HashMap and ArrayList. But those are not working for this.
Really appreciate it if anybody can help me. Thanks in advance.
I would probably recommend a custom node that you create on your own like below. This example is not supposed to have the same functionality as your application but just to show how to create and use custom nodes. I kept your idea in mind when creating this example it has your textfield combobox and checkbox and they are a little easier to manage. Give it a run and let me know if you have any questions
public class Main extends Application {
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
ArrayList<String> itemList = new ArrayList<>(Arrays.asList("Dog", "Cat", "Turkey"));
ArrayList<HBoxRow> hBoxRowArrayList = new ArrayList<>();
for (int i = 0; i<3; i++) {
HBoxRow hBoxRow = new HBoxRow();
hBoxRow.setComboBoxValues(FXCollections.observableList(itemList));
hBoxRowArrayList.add(hBoxRow);
vBox.getChildren().add(hBoxRow.gethBox());
}
Button printTextfieldsButton = new Button("Print Textfields");
printTextfieldsButton.setOnAction(event -> {
for (HBoxRow hBoxRow : hBoxRowArrayList) {
System.out.println("hBoxRow.getTextFieldInput() = " + hBoxRow.getTextFieldInput());
}
});
vBox.getChildren().add(printTextfieldsButton);
stage.setScene(new Scene(vBox));
stage.show();
}
//Below is the custom Node
public class HBoxRow {
HBox hBox = new HBox();
ComboBox<String> comboBox = new ComboBox<>();
TextField textField = new TextField();
CheckBox checkBox = new CheckBox();
public HBoxRow(){
hBox.setAlignment(Pos.CENTER);
textField.setPrefWidth(150);
comboBox.setPrefWidth(150);
checkBox.setOnAction(event -> {
textField.setDisable(!textField.isDisabled());
comboBox.setDisable(!comboBox.isDisabled());
});
hBox.getChildren().addAll(checkBox, textField, comboBox);
}
public void setComboBoxValues(ObservableList observableList) {
comboBox.setItems(observableList);
}
public HBox gethBox(){
return hBox;
}
public String getTextFieldInput(){
return textField.getText();
}
}
}
We are reliant on Node.impl_isTreeVisible() because isVisible does not work properly (or at least the way we want it to).
/**
* #treatAsPrivate implementation detail
* #deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
#Deprecated
public final boolean impl_isTreeVisible() {
return impl_treeVisibleProperty().get();
}
We have a custom Node which contains a Plot. This gets continuous data. We want to avoid to update the plot if it is not visible (still managed/rendered, but hidden).
If the node is placed on a tab which is not selected, hence it is not visible in the window, then using isVisible still returns true. This causes the Node on the selected tab to be rendred every time the plot is updated.
This will evaluate to true even though the node is not visible in the application window.
if (isVisible()) {
updatePlot()
}
So we have been using the following which works as we want it.
if (impl_isTreeVisible()) {
updatePlot()
}
However this will no longer work in Java 9 as such methods are removed. Is there a new approach to this in Java 9?
Update:
Looking at Java 9 source code for javafx.scene.Node I have found the method isTreeVisible(), which looks like a replacement for impl_isTreeVisible. However looking at the Javadoc I cannot find this isTreeVisible().
http://download.java.net/java/jdk9/docs/api/javafx/scene/Node.html
Trying with an example using isTreeVisible() will not compile with Java 9
Java9AppTest.java:50: error: cannot find symbol
if (text1.isTreeVisible()) {
^
symbol: method isTreeVisible()
location: variable text1 of type Text
Update2: Failed to see at first that isTreeVisible() is package private.
Update3: Taken another look at Node source code, I started to check out NodeHelper if could use it to get isTreeVisible(), however the package NodeHelper is not visible. Though using --add-exports for com.sun.javafx.scene to get access to NodeHelper works.
--add-exports javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED
Then I can read the state of isTreeVisible() of a Node.
final boolean isTreeVisible = NodeHelper.isTreeVisible(node);
Code Example
Contains two Tab, each with its own Text.
Has a Task that updates each Text.
Using isVisible() will update each text on both tabs.
Using impl_isTreeVisible() will only update the text that is truely visible.
It makes sense that Text should be updated, even if it is not visible. This is just to illustrate the problem. Replace Text with background process that does alot more CPU heavy work.
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Java9AppTest extends Application {
private Text text1, text2;
public static void main(String[] args) {
Java9AppTest.launch(args);
}
public void start(Stage stage) throws Exception {
TabPane root = new TabPane();
VBox box1 = new VBox();
text1 = new Text();
text1.setText("Hello World!");
text1.textProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("text1 changed from " + oldValue + " to " + newValue);
});
box1.getChildren().addAll(text1);
Tab tab1 = new Tab("Tab 1");
tab1.setContent(box1);
VBox box2 = new VBox();
text2 = new Text();
text2.setText("Another Hello World!");
text2.textProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("text2 changed from " + oldValue + " to " + newValue);
});
box2.getChildren().add(text2);
Tab tab2 = new Tab("Tab 2");
tab2.setContent(box2);
root.getTabs().addAll(tab1, tab2);
Task<Void> task = new Task<Void>() {
/* (non-Javadoc)
* #see javafx.concurrent.Task#call()
*/
#Override
protected Void call() throws Exception {
final String oldText = "Hello World!";
final String newText = "New Hello World!";
while (true) {
if (text1.isVisible()) {
if (text1.getText().equals(oldText)) {
text1.setText(newText);
} else {
text1.setText(oldText);
}
}
if (text2.isVisible()) {
if (text2.getText().equals(oldText)) {
text2.setText(newText);
} else {
text2.setText(oldText);
}
}
Thread.sleep(2000);
}
}
};
stage.setScene(new Scene(root));
stage.setWidth(200);
stage.setHeight(200);
stage.setTitle("JavaFX 9 Application");
stage.show();
Thread thread = new Thread(task, "Task");
thread.start();
}
}
I suggest adding a property to your node, that controls if you want to update the plot. So instead of if (impl_isTreeVisible()) { just have if (shouldUpdate) {. Upon tab selection changes, just toggle the property. So in essence your TabPane would control if the plot is updated.
Alternatively you could pass the TabPane to your node and query the selected tab: tabPane.getSelectionModel().getSelectedIndex(). This, however means that your node must know on which tab it resides.
A Tab has a property selected, bind that property to an update property of your plot, which determines if you redraw your plot.
In your control (or its skin) add a listener to the update property of the plot, where you pause or resume listening to your input source, or pause or resume the timer that gets the data.
This solution does not add additional dependencies to the object graph, the type of container it should be in and enables you to create more complex bindings if necessary (like a pause button), and eases testing as this property is controllable in a standalone manner.
Depending on the data source implementation this solution can also pause your data source if it determines that there are no listeners processing your data actively.
This is actually my first JavaFx desktop application. Within my application I want to display every event as a log in textarea. I have different log types , error,warning etc. I want to append all these logs inside a textarea with different colors. I tried like this(this is just a sample),
// Method call is like this
logText("Enter Ip address First");
// The method
public void logText(String log){
log = ">> "+ log +"\n";
Text t = new Text();
t.setStyle("-fx-background-color: #DFF2BF;-fx-text-fill: #4F8A10;-fx-font-weight:bold;");
t.setText(log);
txtConsole.appendText(t.toString());
}
With above code I did not get any error but my output is like :
Text[text=">> Enter Ip address First
", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=12.0], fontSmoothingType=GRAY, fill=0x000000ff]
How can I solve this ? I tried various methods mentioned here in stackoverflow (this is one of them).
*** Please note that this application is for corporate use so I need to be strict to the licenses.
Thanks in advance.
You need to have replace t.toString() with t.getText() in txtConsole.appendText(t.toString())
You can't have colored text in TextArea. Try using TextFlow instead.
You have colored text which you can add to the TextFlow:
public class TextFlowSample extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
TextFlow flow = new TextFlow();
String log = ">> Sample passed \n";
Text t1 = new Text();
t1.setStyle("-fx-fill: #4F8A10;-fx-font-weight:bold;");
t1.setText(log);
Text t2 = new Text();
t2.setStyle("-fx-fill: RED;-fx-font-weight:normal;");
t2.setText(log);
flow.getChildren().addAll(t1, t2);
stage.setScene(new Scene(new StackPane(flow), 300, 250));
stage.show();
}
}
If you have the flexibility to use external library, have a look at RichTextFX