Skip to content

Previews

Giorgio Garofalo edited this page Feb 9, 2021 · 20 revisions

NOTE! You may need a basic knowledge of JavaFX to fully understand this guide.


Creating custom previews from JavaScript is pretty easy. A preview menu can be simply instantiated by calling the constructor new PreviewMenu(title, image, controls, initFlow, flowsAmount)

  • title is a string representing the title on the top of the menu
  • image represents the background of the preview. It can be either a relative string path or a File instance
  • controls is an array of JavaFX text controls, such as text fields, text areas, spinners, etc
  • initFlow is a function(flow, index) called every time the preview needs to be updated
  • flowsAmount defines the initial amount of text flows. If not specified, it will be equals to the amount of controls.

Example:

const textfield1 = new TextField(); // javafx.scene.control.TextField
const textfield2 = new TextField();
const previewMenu = new PreviewMenu('My preview', 'image.jpg', [textfield1, textfield2], (flow, index) => {
   // ...
}, 2 /* in this case, this could be omitted */);
previewMenu.show();

Now we can work on the initFlow function. The first parameter, flow, is an instance of a JavaFX TextFlow, which can be considered as a succession of Texts.

(flow, index) => {
    flow.setTextAlignment(TextAlignment.CENTER); // We center the flow
    flow.setLayoutY(50 + index * 30);            // We set the Y coordinate to 50 plus 30 for each index value
}

We are almost done but, if you type anything, the preview won't update. It's because we haven't set bindings.

PreviewMenu#bind has two overload functions:

  • bind(property, index) - Takes a JavaFX property (i.e textfield.textProperty()) and flow index
  • bind(control, index) - Takes a JavaFX text control and flow index. Calls the previous method with control.textProperty() parameter

For more complex bindings, check the listen(property, action) top-level function (see here).

const textfield1 = new TextField();
const textfield2 = new TextField();
const previewMenu = new PreviewMenu('My preview', 'image.jpg', [textfield1, textfield2], (flow, index) => {
    flow.setTextAlignment(TextAlignment.CENTER);
    flow.setLayoutY(50 + index * 30);
});
previewMenu.bind(textfield1, 0);
previewMenu.bind(textfield2, 1);
previewMenu.show();

Result 1

Finally, let the user open the preview from a drop-menu button:

function onDropMenuOpen(type, menu) {
    if (type == 'previews') {
        menu.addButton('My preview', (area, x, y) => {
            const textfield1 = new TextField();
            const textfield2 = new TextField();
            const previewMenu = new PreviewMenu('My preview', 'image.jpg', [textfield1, textfield2], (flow, index) => {
                flow.setTextAlignment(TextAlignment.CENTER);
                flow.setLayoutY(50 + index * 30);
            }, 2);
            previewMenu.bind(textfield1, 0);
            previewMenu.bind(textfield2, 1)
            previewMenu.setLayoutX(x);
            previewMenu.setLayoutY(y);
            previewMenu.show();
        })
    }
}

You may want to automatically insert selected text into one of the text fields.
Just set the value of the text field to getSelectedText() inside of a runLater block:

runLater(() => {
    textfield1.setText(getSelectedText());
})

NOTE! getSelectedText() is a built-in function that gets text inside selection (if there is one). Otherwhise, it will return the string value hovered by the caret. Otherwhise, an empty string.

Let's play with text flows:

(flow, index) => {
    flow.setTextAlignment(TextAlignment.CENTER);
    flow.setLayoutY(previewMenu.getImage().getBackground().getHeight() / 2 + 25 * (index - 1))
    flow.getStyleClass().add('my-text-flow');
    flow.getStyleClass().add('my-text-flow-' + index);
}

Remember to load stylesheets if needed:

.my-text-flow .label {
    -fx-font-family: "Minecraft";
    -fx-font-size: 25;
}

.my-text-flow-0 {
    -fx-effect: dropshadow(gaussian, red, 10, 0, 0, 0);
}

.my-text-flow-1 {
    -fx-effect: dropshadow(gaussian, green, 10, 0, 0, 0);
}

Result 2

IMPORTANT! For text areas that generate a flow for each line, take a look at generateFlowList(textArea, menu) which returns a list of TextFlows.