0

Below is main UI where I dynamically want to append the Pane that contains Name, Author, Description and Button.

Here, it expands to available width very nicely.

Root Scene

This is my FXML code for above UI

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>


<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ResultsUI">
   <children>
      <AnchorPane prefWidth="600.0" style="-fx-background-color: white;" VBox.vgrow="NEVER">
         <children>
            <TextField id="queryOldTF" fx:id="queryOldTF" layoutX="14.0" layoutY="14.0" prefHeight="40.0" prefWidth="518.0" style="-fx-min-height: 40; -fx-border-radius: 50%; -fx-border-color: #007BFF; -fx-background-color: white;" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="68.0" AnchorPane.topAnchor="14.0" />
            <Button id="serachAgainBtn" fx:id="serachAgainBtn" layoutX="532.0" layoutY="14.0" mnemonicParsing="false" onAction="#searchAgain" style="-fx-background-color: #007BFF; -fx-min-height: 40; -fx-min-width: 40; -fx-text-fill: white; -fx-border-radius: 50%; -fx-background-radius: 50%;" text="GO" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0" />
         </children>
         <VBox.margin>
            <Insets />
         </VBox.margin>
      </AnchorPane>
      <AnchorPane prefHeight="700.0" prefWidth="600.0" style="-fx-background-color: white;" VBox.vgrow="ALWAYS">
         <children>
            <ScrollPane id="scrollPane" fx:id="scrollPane" fitToWidth="true" layoutX="1.0" prefHeight="339.0" prefWidth="574.0" style="-fx-background-color: white;" vbarPolicy="ALWAYS" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="1.0" AnchorPane.rightAnchor="1.0" AnchorPane.topAnchor="0.0">
              <content>
                  <AnchorPane prefHeight="206.0" style="-fx-background-color: white;">
                     <children>
                        <TextArea id="descTl" fx:id="descTl" editable="false" layoutY="46.0" prefHeight="111.0" prefRowCount="4" style="-fx-background-color: white;" wrapText="true" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
                           <font>
                              <Font name="Calibri Light" size="12.0" />
                           </font>
                        </TextArea>
                        <Label id="nameTl" fx:id="nameTl" prefHeight="27.0" style="-fx-font-size: 18;" text="Name" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="1.0" AnchorPane.topAnchor="1.0" />
                        <Label id="authorTl" fx:id="authorTl" layoutX="4.0" layoutY="27.0" prefHeight="17.0" style="-fx-font-style: italic;" text="Authors" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
                        <Button id="openBtn" fx:id="openBtn" layoutY="164.0" mnemonicParsing="false" style="-fx-background-color: white; -fx-border-color: #007BFF; -fx-border-radius: 25%;" text="Open" AnchorPane.bottomAnchor="14.800000000000011" AnchorPane.leftAnchor="0.0" />
                     </children>
                  </AnchorPane>
              </content>
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
            </ScrollPane>
         </children>
      </AnchorPane>
   </children>
</VBox>

And this is my sub-portion of FXML which I dynamically inflate and add during runtime

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<fx:root xmlns:fx="http://javafx.com/fxml" type="javafx.scene.layout.Pane">
    <Pane id="resPane" fx:id="resPane" prefHeight="200.0" prefWidth="200.0">
        <children>
            <Label id="descTl" fx:id="descTl" layoutX="14.0" layoutY="75.0" text="Description" />
            <Label id="nameTl" fx:id="nameTl" layoutX="14.0" layoutY="14.0" style="-fx-font-size: 18;" text="Name" />
            <Label id="authorTl" fx:id="authorTl" layoutX="14.0" layoutY="49.0" text="Authors" />
            <Button id="openBtn" fx:id="openBtn" layoutX="12.0" layoutY="161.0" mnemonicParsing="false" text="Open" />
        </children>
    </Pane>
</fx:root>

But after adding the pane dynamically, this is what my UI looks: after adding panes

This is what I want to achieve:

1) Make the dynamically inflated panes to fill width dynamically

2) Remove the scrollbar from textarea (which doesn't actually removes even after setting preferred row property) and append ellipse on text overflow

3) Remove the gray background from UI (I can't seem to find which portion of UI is causing gray spots in UI despite setting the property of all views to be white background)

Below is my Main controller

public class ResultsUI { @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private TextField queryOldTF;

    @FXML
    private Button serachAgainBtn;

    @FXML
    private VBox resContainer;

    @FXML
    private ScrollPane scrollPane;

    @FXML
    void searchAgain(ActionEvent event) {
        String query = queryOldTF.getText().toString();
        queryOldTF.setText(query);
        getFromOwn = true;
        initialize();
    }

    private boolean getFromOwn = false;

    @FXML
    void initialize() {
        assert queryOldTF != null : "fx:id=\"queryOldTF\" was not injected: check your FXML file 'Second.fxml'.";
        assert serachAgainBtn != null : "fx:id=\"serachAgainBtn\" was not injected: check your FXML file 'Second.fxml'.";
        assert resContainer != null : "fx:id=\"resContainer\" was not injected: check your FXML file 'Second.fxml'.";
        assert scrollPane != null : "fx:id=\"scrollPane\" was not injected: check your FXML file 'Second.fxml'.";

        System.out.println(MainUI.query);
        if (!getFromOwn)
            queryOldTF.setText(MainUI.query);

        HashMap<Integer, Double> documents = ExtendedBooleanModel.returnRankedDocuments(queryOldTF.getText().toString().trim());
        Map<Integer, Paper> papers = ObjectHandler.getPapersCollection();

        List<MyPane> panes = new ArrayList<>();
        for (int index:
             documents.keySet()) {

            MyPane docPane = new MyPane(papers.get(index).getContent(), papers.get(index).getTitle(), papers.get(index).getAuthor(), papers.get(index).getName());
            panes.add(docPane);
        }

        VBox pane = new VBox();
        pane.getChildren().addAll(panes);

        scrollPane.setContent(pane);
    }
}

And below is the controller for dynamically inflating panes:

public class MyPane extends Pane {

    @FXML
    public TextArea descTl;

    @FXML
    public Label nameTl;

    @FXML
    public Label authorTl;

    public MyPane(String desc, String name, String author, String doc) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("subFXML.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        try {
            loader.load();

            descTl.setText(desc);
            nameTl.setText(name);
            authorTl.setText(author);
            openBtn.setOnAction(event -> {
                File myFile = new File("pdfs/" + doc.replace(".txt", ".pdf"));
                try {
                    Desktop.getDesktop().open(myFile);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @FXML
    private Button openBtn;

    public void setOnAction(EventHandler<ActionEvent> handler) {
        System.out.println("test");
        openBtn.setOnAction(handler);
    }

    public EventHandler<ActionEvent> getOnAction() {
        return openBtn.getOnAction();
    }
}

Any help would be greatly appriciated...

HQuser
  • 640
  • 10
  • 26
  • You should probably be using a `ListView`. – SedJ601 Jan 15 '19 at 16:51
  • @Sedrick can I embed TextFields, TextAreas and customized onclick button listener in ListView? – HQuser Jan 15 '19 at 17:02
  • https://stackoverflow.com/questions/19588029/customize-listview-in-javafx-with-fxml – SedJ601 Jan 15 '19 at 17:04
  • I was going to create an answer but since slaw posted a very good answer I will not. [Here](https://github.com/sedj601/ListViewCustomCellFactoryExample) is a starting point for you. – SedJ601 Jan 15 '19 at 20:17

1 Answers1

2

As I mentioned in a comment to your previous question, I strongly suggest you don't use AnchorPane. You will have a better time using layouts that actually layout their children ("automatically"). For instance, your main FXML file could be simplified to something like:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" 
      fx:controller="ResultsUI" prefWidth="600" prefHeight="500">

    <HBox spacing="10">
        <padding>
            <Insets topRightBottomLeft="10"/>
        </padding>
        <TextField fx:id="queryOldTF" HBox.hgrow="ALWAYS"/>
        <Button fx:id="searchAgainBtn" text="GO" onAction="#searchAgain"/>
    </HBox>

    <ListView fx:id="resultsListView" VBox.vgrow="ALWAYS"/>

</VBox>

With this setup you don't have to worry about absolute positioning (e.g. layoutX, ArchivePane.rightAnchor, etc...). This also uses a ListView as suggested by Sedrick; since ListView is a virtual control it will maintain performance even when you have many results to show.

In the controller you'd customize the cellFactory of the ListView to properly display your items (see this question). You could also simplify your "sub-portion" FXML file (the one you'd likely use as the cell graphic) to something like:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.VBox?>

<fx:root xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
         type="javafx.scene.layout.VBox">

    <Label fx:id="titleLabel" text="Title"/>
    <Label fx:id="authorsLabel" text="Authors"/>
    <!-- The parent VBox will grow the TextArea horizontally -->
    <TextArea fx:id="descArea" editable="false" wrapText="true" prefRowCount="6"/>
    <Button text="Open" onAction="#openFile"/>

</fx:root>

In both FXML files above I omitted the styles for brevity but it might be easier/cleaner/more-maintainable to separate the styles into one or more CSS files.


As for removing the scroll bars in the TextArea see this question.


The gray box things you're seeing is likely the background of the ScrollPane, though it could be some other ancestor node or even the Scene. Since your nodes are not growing to fill the space as you want, the backgrounds of ancestor nodes are "peeking" through. Some of this can be fixed by fixing the layout so the nodes are covered up. However, you may need to set the background colors of some of the nodes. This will be easiest using CSS; see the JavaFX CSS Reference Guide.

Using the CSS Analyzer of Scene Builder can be a helpful tool here. To display it go to View > Show CSS Analyzer or press Ctrl+6 (Windows).

Slaw
  • 37,820
  • 8
  • 53
  • 80