Scenebuilder rejects Webview in custom control - javafx

I'm creating a rich text control that uses a WebView for display and HTMLEditor for editing. When I tried to add the control to Scenebuilder's custom controls an exception was thrown:
Exception for: com/spindotta/jfx/control/text/RichTextArea.class
javafx.fxml.LoadException:
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
...
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = LibraryFolderWatcher([library folder location])
...
at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216)
...
It seems that Scenebuilder tries to instantiate a WebView, which in turn tries to instantiate a WebEngine which seems to require an FX application thread, which the custom control doesn't generate.
Would be grateful for any ideas/workaround.
Thanks!

SceneBuilder has a built in mechanism that checks all the jars in the user library looking for custom controls.
For any given jar, that implies checking all its classes, looking for possible controls: concrete classes that are assignable from Node. Then it creates an instance for these and try to load it on the class path. If it works, it will be added to the library panel.
In case of your custom control, that includes a WebView node, this means that when your control is being checked by SceneBuilder, a new instance of WebView will be done. At this point you'll get the exception you mentioned:
Caused by: java.lang.IllegalStateException: Not on FX application thread; currentThread = LibraryFolderWatcher()
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:856)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:845)
at javafx.scene.web.WebView.<init>(WebView.java:271)
So the easy solution for this will be allowing the custom control to be loaded in the JavaFX application thread when being loaded in design mode by SceneBuilder.
This small custom control works for me:
public class WebControl extends StackPane {
private WebView webView;
public WebControl() {
if (Platform.isFxApplicationThread()) {
init();
} else {
// Intended for SceneBuilder
Platform.runLater(this::init);
}
}
private void init() {
webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.load("http://www.google.com");
getChildren().add(webView);
}
}

Related

Xamarin Forms: WebView loading time is very high

I am using WebView for loading websites in my project. It is loading websites but very slow. It takes around 10 to 15 seconds to load this site.
I try a solution from this thread. I have added android:hardwareAccelerated="true" on the manifest under application level, but no luck.
For iOS and Windows the solution is below: But I don't know how to create the custom render.
In iOS, try to use WKWebView instead of UIWebView.
For the UWP, use Windows.UI.Xaml.Controls.WebViewcontrol instead of using the built-in Xamarin WebView control.
Can I get the sample custom renderer for iOS and Windows-like above?
It is loading websites but very slow
The speed of loading website will up to lots of causes . For example, it will consume a lot of time if the web contains many remote css/js file . And it will also up to network performance .
The link that you provided loads slow even if on browser on my side . It contains lots of remote css if we check the source code.
For Android , We can create an custom renderer webview to accelerate it.
Set Render Priority to hight.
Enable the dom storeage.
When Loading the html, we can disable the image showing, when
loading finished, load the image by
Control.Settings.BlockNetworkImage.
Enable the Cache, if you load it at the nexttime, you can load it
quickly.
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(MyWebviewRender))]
namespace MyWebviewDemo.Droid
{
public class MyWebviewRender : WebViewRenderer
{
public MyWebviewRender(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
Control.Settings.JavaScriptEnabled = true;
Control.Settings.SetAppCacheEnabled(true);
Control.Settings.CacheMode = Android.Webkit.CacheModes.Normal;
Control.Settings.SetRenderPriority(RenderPriority.High);
Control.Settings.DomStorageEnabled = true;
Control.Settings.BlockNetworkImage = true;
Control.SetWebChromeClient(new MyWebChromeClient());
}
}
internal class MyWebChromeClient : WebChromeClient
{
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
base.OnProgressChanged(view, newProgress);
if (newProgress == 100)
{
// webview load success
// // loadDialog.dismiss();
view.Settings.BlockNetworkImage=false;
}
else
{
// webview loading
// loadDialog.show();
}
}
}
}
In iOS, try to use WKWebView instead of UIWebView. For the UWP, use Windows.UI.Xaml.Controls.WebViewcontrol instead of using the built-in Xamarin WebView control.
In your case it will only have less improve even if using above solution . You could add an ActivityIndicator during the loading . If you can access the website , it would be better to download the css file to local and loading them in ContentPage .

How to create new skin for `DatePicker`, javafx

I want to create a new skin class for DatePicker. Following different instructions about how to do it, e.g guigarage the right way is to extend SkinBase and load new skin class by setting -fx-skin property to the skin class name, so i did, but I got this Error:
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: sample.myDatePickerSkin cannot be cast to com.sun.javafx.scene.control.skin.DatePickerSkin
at com.sun.javafx.scene.control.behavior.DatePickerBehavior.onAutoHide(DatePickerBehavior.java:103)
and if you look at DatePickerBehavior.java:103:
#Override public void onAutoHide() {
// when we click on some non-interactive part of the
// calendar - we do not want to hide.
DatePicker datePicker = (DatePicker)getControl();
DatePickerSkin cpSkin = (DatePickerSkin)datePicker.getSkin();
cpSkin.syncWithAutoUpdate();
// if the DatePicker is no longer showing, then invoke the super method
// to keep its show/hide state in sync.
if (!datePicker.isShowing()) super.onAutoHide();
}
So did I do anything wrong? or I should also write myDatePickerBehavior?
The official way to implement a Skin is to either
extend javafx.scene.control.SkinBase (since JavaFX 8.0)
or
implement Skin directly.
com.sun.javafx.scene‌​.control.skin.BaseSki‌​n is not a published API.
Please note that with Java SE 9 and Jigsaw, DatePickerSkin as well as the other default skins will become published APIs, AFAIK. Then it should be safe to extend them if it meets your requirements.
I should extend DatePickerSkin :
package sample;
import com.sun.javafx.scene.control.skin.DatePickerSkin;
import javafx.scene.control.DatePicker;
public class customDatePickerSkin extends DatePickerSkin {
public customDatePickerSkin(DatePicker datePicker) {
super(datePicker);
}
}

JavaFX - Extends Stage, but inherited method is undefined

I'm working with JavaFX using Java 8 and I have a class where Stage is extended. I got this code from another student and it works fine for everyone else, but for me I get an error on setAlwaysOnTop. It says the method is undefined, but it is inherited from Stage. It seems to be only that method which is giving this error.
It would be easier with an image, but I can't post images yet so I had to just copy-paste the relevant code:
public class NotificationWindow extends Stage {
public void showNotification(){
Rectangle2D screenBounds=Screen.getPrimary().getVisualBounds();
this.setAlwaysOnTop(true); //Error here
this.setX(screenBounds.getMaxX()-scene.getWidth());
this.setY(screenBounds.getMaxY()-scene.getHeight());
this.show();
}
}
The alwaysOnTop property was introduced in JavaFX 8 update 20. So you are probably using an earlier version. Update to the latest JavaFX version and it should recognize the method.

Access elements of loaded FXML in Parent Controller

I currently have the following situation:
I have created a FXML file backed up by a FXML Controller. The screen consists of a sidebar and a child holder. When I click on an element in the sidebar, I load an additional FXML file in the child holder, like this:
childHolder.getChildren().addAll(FXMLLoader.load(getClass().getResource("SidebarItem1.fxml")));
This works fine. But I want to access some elements of the loaded FXML in the Controller of the Parent Controller. I just initialized the elements of the child FXML after I loaded it.
I already looked at this question: JAVAFX - FXML - Access Loaded FXML Controls from Parent Controller, but when I do it like that, I still get an error:
FXMLLoader loader = new FXMLLoader(getClass().getResource("SidebarItem1.fxml"));
loader.setController(this);
childHolder.getChildren().addAll(loader.load());
someChildTextField.setText("Some text");
I have given someChildTextField a fx:id and I have placed it on top of the initialize like this:
#FXML public TextField someChildTextField;
Still, I get a NullpointerException, so I assume it can still not find the control.
Does anyone know how to access elements of a loaded FXML in the Parent Controller?
I can't see any reason your approach doesn't work; I mocked something similar up and it worked just fine. This is an unusual approach, though, as you are using the same object as a controller for two separate FXML files.
I would recommend using one controller for each FXML. So you could do something like
public class SidebarItem1Controller {
#FXML
private TextField someTextField ;
public void setText(String text) {
someTextField.setText(text);
}
}
and then from your parent controller just do
FXMLLoader loader = new FXMLLoader(getClass().getResource("SidebarItem1.fxml"));
childHolder.getChildren().add(loader.load());
SideBarItem1Controller childController = loader.getController();
childController.setText("Some Text");
Set the controller in your SideBarItem1.fxml file the usual way, with fx:controller = com.example.SidebarItem1Controller"

Changing QWidget with QGlWidget in Qt 4.8.0

I am working on an application that currently uses QWidgets for rendering moving images, and I am trying to move from using QWidgets to QGlWidgets, but my application keeps crashing.
The only modifications I've done are:
from this:
class MyClass : public QWidget
{
MyClass(QWidget *parent):QWidget(parent)
{
...
}
}
to this:
class MyClass : public QGlWidget
{
MyClass(QWidget *parent):QGlWidget(QGLFormat(QGL::SampleBuffers),parent)
{
...
}
}
Do I have to modify my code more, if so what other modifications are needed?
I am currently using Visual studio 2010, and qt 4.8.0
MyClass(QWidget *parent):QGlWidget(QGLFormat(QGL::SampleBuffers),parent)
{
...
}
It looks like you're creating a temporary QGLFormat object, which is being passed by reference to QGLWidget, and when that temporary object goes out of scope, you'll have an invalid reference.
I would use a different form of the constructor.
I found what the problem was.
In my QWidget class I had a member object QPainter, that I would reuse in the paint event. I kept searching and found that this is a bad practice, so now I declared a QPainter object at the beginning of the paint event and my problem is solved.

Resources