0

Problem : So basically the problem is that when I do change background color at 12pt font size and then I increase the font size to let's say 36pt then the background color is not properly rendered in HTMLEditor. Here is the video on action : Video showing how I generate the issue.

Here is the sample fxml file :

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

 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.Tab?>
 <?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.web.HTMLEditor?>

<TabPane xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" 
fx:controller="AppController">
 <tabs>
  <Tab text="Editor">
     <content>
        <GridPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0">
          <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          </columnConstraints>
          <rowConstraints>
            <RowConstraints minHeight="10.0" prefHeight="366.0" vgrow="ALWAYS" />
          </rowConstraints>
           <children>
              <HTMLEditor fx:id="htmleditor" htmlText="&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body contenteditable=&quot;true&quot;&gt;&lt;/body&gt;&lt;/html&gt;" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS" />
           </children>
        </GridPane>
     </content>
  </Tab>
  <Tab text="Raw">
     <content>
        <GridPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0">
           <columnConstraints>
              <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
           </columnConstraints>
           <rowConstraints>
              <RowConstraints maxHeight="195.0" minHeight="10.0" prefHeight="34.0" vgrow="NEVER" />
              <RowConstraints minHeight="10.0" prefHeight="366.0" vgrow="ALWAYS" />
           </rowConstraints>
           <children>
              <ToolBar prefHeight="40.0" prefWidth="200.0">
                 <items>
                    <Button mnemonicParsing="false" onAction="#onRawHTMLButtonClick" text="Raw HTML" />
                    <Button mnemonicParsing="false" onAction="#onUpdateButtonClick" text="Update" />
                 </items>
              </ToolBar>
              <TextArea fx:id="rawHTMLViewer" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1" />
           </children>
        </GridPane>
     </content>
  </Tab>
</tabs>
</TabPane>

Here is the code for running the gui:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {

public static void main(String[] args) {
    launch();
}

@Override
public void start(Stage primaryStage) throws Exception {
    FXMLLoader fxmlLoader = new FXMLLoader();
    fxmlLoader.setLocation(App.class.getResource("/View/App.fxml"));
    Scene scene = new Scene(fxmlLoader.load(), 900, 600);
    primaryStage.setTitle("Basic HtmlEditor");
    primaryStage.setScene(scene);
    primaryStage.show();
}

}

What I have tried to solve the problem : So basically I tried to retrive webengine reference and from there I have extracted the webpage. Webpage has a method to refresh and it also has a method to force to repaint. But still it doesn't solve the problem. Here is the code of that : @FXML private HTMLEditor htmleditor;

@FXML
private TextArea rawHTMLViewer;
private WebEngine engine;
@Override
public void initialize(URL location, ResourceBundle resources) {
    Platform.runLater(() -> {
        engine = HTMLEditorUtility.findWebView(htmleditor).getEngine();
        int backgroundColorNodeIndex = 16;
        ColorPicker colorPicker = (ColorPicker) HTMLEditorUtility.findNthNodeOfTopToolBar(htmleditor, backgroundColorNodeIndex);
        colorPicker.valueProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("Color has been changed");
            repaint();
        });

        ComboBox fontSizeComboBox = (ComboBox) HTMLEditorUtility.findNthNodeOfBottomToolBar(htmleditor, 2);
        fontSizeComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("Font size has been changed");
            repaint();
        });
    });
}

And here is the repaint method code :

private void repaint() {
    WebPage webPage = Accessor.getPageFor(engine);
    webPage.refresh(webPage.getMainFrame());
    webPage.forceRepaint();
}

Also I have read in this post that we may overcome the screen refreshing problem by changing scene size. I have tried that also but it doesn't solve the problem. Please if anyone know any solution to this.

Here is the HTMLEditorUtility.java code :

import javafx.scene.Node;
import javafx.scene.control.ToolBar;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import lombok.NonNull;

import java.util.function.Function;

 /**
 * @author : Md. Ismail Hosen
 * @version : 1.0
 */

public class HTMLEditorUtility {

static final String TOP_TOOLBAR_SELECTOR = ".top-toolbar";
static final String BOTTOM_TOOLBAR_SELECTOR = ".bottom-toolbar";
static final String WEB_VIEW_SELECTOR = "WebView";

/**
 * @param htmlEditor from which top toolbar will be founded.
 * @return this return the top toolbar.
 */
public static ToolBar findTopToolBar(HTMLEditor htmlEditor) {
    return findToolBar(htmlEditor, TOP_TOOLBAR_SELECTOR);
}

/**
 * @param htmlEditor from which bottom toolbar will be founded.
 * @return this return the bottom toolbar.
 */
public static ToolBar findBottomToolBar(HTMLEditor htmlEditor) {
    return findToolBar(htmlEditor, BOTTOM_TOOLBAR_SELECTOR);
}

/**
 * @param htmlEditor from which Webview will be founded.
 * @return this return the webview of the HTMLEditor
 */
public static WebView findWebView(@NonNull HTMLEditor htmlEditor) {
    return (WebView) htmlEditor.lookup(WEB_VIEW_SELECTOR);
}

/**
 * @param htmlEditor      from which we need to find the toolbar
 * @param toolbarSelector this is the selector for finding the node
 * @return it returns the toolbar based on the selector
 */
private static ToolBar findToolBar(@NonNull HTMLEditor htmlEditor, String toolbarSelector) {
    Node node = htmlEditor.lookup(toolbarSelector);
    if (node instanceof ToolBar) {
        return (ToolBar) node;
    }
    return null;
}

/**
 * @param htmlEditor from which we need to find the node
 * @param N          node index(0 based)
 * @return This will return top tollbar node based on the index(N)
 */

public static Node findNthNodeOfTopToolBar(HTMLEditor htmlEditor, int N) {
    return findNthNodeOfToolBar(htmlEditor, N, HTMLEditorUtility :: findTopToolBar);
}

/**
 * @param htmlEditor from which we need to find the node
 * @param N          node index(0 based)
 * @return This will return bottom toolbar node based on the index(N)
 */

public static Node findNthNodeOfBottomToolBar(HTMLEditor htmlEditor, int N) {
    return findNthNodeOfToolBar(htmlEditor, N, HTMLEditorUtility :: findBottomToolBar);
}

/**
 * @param htmlEditor from which we need to find the node
 * @param N          node index(0 based)
 * @param nodeFinder (functional intrface to run the code based on the finding section)
 * @return This will return the node based on the nodeFinder method and index(N)
 */

private static Node findNthNodeOfToolBar(@NonNull HTMLEditor htmlEditor, int N,
                                         Function<HTMLEditor, ToolBar> nodeFinder) {
    ToolBar toolBar = nodeFinder.apply(htmlEditor);
    return toolBar.getItems().get(N);
}

/**
 * @param htmlEditor for which top tollbar details will be printed.
 */

public static void printTopToolbarNode(@NonNull HTMLEditor htmlEditor) {
    printToolBarNode(htmlEditor, HTMLEditorUtility :: findTopToolBar);
}

/**
 * @param htmlEditor for which bottom toolbar details will be printed.
 */
public static void printBottomToolbarNode(@NonNull HTMLEditor htmlEditor) {
    printToolBarNode(htmlEditor, HTMLEditorUtility :: findBottomToolBar);
}

private static void printToolBarNode(HTMLEditor htmlEditor, Function<HTMLEditor, ToolBar> nodeFinder) {
    ToolBar topToolBar = nodeFinder.apply(htmlEditor);
    final int[] positionIndex = {0};
    topToolBar.getItems().forEach((node -> {
        System.out.println("position Index : " + positionIndex[0]);
        positionIndex[0] += 1;
        System.out.println("Style Class : " + node.getStyleClass());
        System.out.println("Class Name : " + node.getClass().getName());
        System.out.println("Type Selector: " + node.getTypeSelector());
        System.out.println("");
    }));
}

/**
 * @param htmlEditor from which we need to find the webengine
 * @return Webengine of that given html editor
 */
public static WebEngine findWebEngine(@NonNull HTMLEditor htmlEditor) {
    WebView webView = HTMLEditorUtility.findWebView(htmlEditor);
    return webView.getEngine();
}

}
MD Ismail Hosen
  • 118
  • 1
  • 7
  • 1
    Looks like it is not solved yet bug in javafx https://bugs.openjdk.java.net/browse/JDK-8087686 – Alex Mar 13 '22 at 07:54
  • Thank you. I have found a patch in there but they are changing the HTMLEditorSkin.java code for that. I guess I can't do that on my code(as it is in jar). – MD Ismail Hosen Mar 13 '22 at 08:05
  • It is skin, so you can probably copy and paste the existing skin, create a new name for it, put your patch in it and set the skin of the control to your new skin. No guarantee that that would work or would be worth a try, and it wouldn't be stable across different JavaFX versions, but something for you to consider. – jewelsea Mar 13 '22 at 12:44
  • Will try. I was thinking to customize HTMLEditorSkin but wanted to try without it. But I think it's time to do that. – MD Ismail Hosen Mar 13 '22 at 16:41
  • If the html editor otherwise provides the functionality you want, patching it might work OK. But if some other JavaScript editor works as well (or better) for you, you could try that. You can run a JavaScript editor in a WebView and communicate with it through the Java<->JavaScript bridge. The API isn't as nice as the Java API for HTMLEditor, because you have to deal with the bridge, but it is doable. Just another option to consider. – jewelsea Mar 14 '22 at 10:53
  • Actually I am building editor . I have added Table insertion,modification, hyperlink insertion,image paste option, changed font size dropdown list to like typeable. I can try Javascript based editor but it must need to work in offline. In our company we already have web based editor. Any recommendation for offline based editor? – MD Ismail Hosen Mar 16 '22 at 10:00

0 Answers0