I have a program that allows users to take notes. When they take notes, I use a JavaFX HTMLEditor, which allows for tables, lists, etc. I then need to display these notes to the user, and only way I can find to display HTML in JavaFX is WebEngine (unfortunately).
The problem is WebEngine has different behaviours inside containers than labels, and I need the WebView to behave as a label would.
I have simplified my problem with this example. Here is a VBox with 1 component inside it, a Label:
As you can see, the label (red) takes up no more vertical space than necessary inside the VBox container, and wraps text which is exactly the behaviour I want.
If I now add a WebView and then a second label, the result changes to this:
The webview in the middle (white) is now expanding vertically as much as possible, and the labels are no longer wrapping. So if in my actual program I had two WebViews stacked on top of each other, they would both fight for 50% of the available vertical space, instead of only taking up as much space as necessary, which is what I need.
Furthermore, with Label/WebView/Label like in last image, If I shrink the width of the window like so:
There is another problem where the WebView starts to scroll vertically. This is not what I want, as I want it to be just like a label (expand to take up as much space as necessary and no more at all times).
I have tried setting the vgrow property of the webview to both null and Priority.NEVER, but it seems to have no effect.
Here is the complete code. Thanks for your help.
Main.java
package main;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
public class Main extends Application {
#Override
public void start(Stage _primaryStage) {
// create the view
VBox vBox = new VBox();
vBox.setPrefHeight(600);
vBox.setPrefWidth(600);
String textString = "";
textString += "It's the end of the world as we know it and I feel fine. ";
textString += textString;
textString += textString;
textString += textString;
textString += textString;
Label testLabel = new Label(textString);
testLabel.getStyleClass().add("label");
testLabel.setWrapText(true);
vBox.getChildren().add(testLabel);
WebView testWebView = new WebView();
testWebView.getStyleClass().add("webview");
testWebView.getEngine().loadContent(textString);
vBox.getChildren().add(testWebView);
VBox.setVgrow(testWebView, Priority.NEVER);
Label testLabel2 = new Label(textString);
testLabel2.getStyleClass().add("label");
testLabel2.setWrapText(true);
vBox.getChildren().add(testLabel2);
Scene scene = new Scene(vBox);
scene.getStylesheets().add("/main/styles.css");
_primaryStage.setTitle("Notes Program");
_primaryStage.setScene(scene);
_primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
styles.css
.label {
-fx-background-color: 'red';
}
.webview {
}
Currently (at the time this answer was written), no out of the box, easily implemented or adaptable solution to your problem exists in either JavaFX or available 3rd party libraries.
This is not a concrete answer to the question, but a suggestion of approaches which may be tried to solve problems such as this one.
Fitting a WebView to its content
Your current approach to solving the problem could be summarized as: render HTML content in a resizable JavaFX node the behaves similarly to a Label.
For the most direct assistance in trying to achieve that, review the information in answers to the linked question below, it is not re-iterated in detail here.
This question is related to a similar one which provides some information on ways to address sizing WebView to the displayed content of its HTML page, though the information there is currently not definitive and compete for all cases, so it won't immediately solve your problem:
Correct sizing of Webview embedded in Tabelcell
The suggestion there is to use some JavaScript scripting to determine the size of the document, then set re-size constraints on the WebView node to fit the node to the content, dynamically updating the sizing link when the content changes or is re-rendered or the available space for the WebView changes.
FAQ
Answers to additional and clarifying questions which were included in comments.
I was hoping I could accomplish this with JavaFX layouts or stylesheets to avoid scripts.
I think to accomplish your original solution idea of treating a WebView like a label, it will not be possible to avoiding writing some code in both JavaScript and JavaFX.
The in-built JavaFX layout panes and CSS style sheets will not be
sufficient.
The current WebView doesn’t provide the appropriate
support to work as nicely as desired with JavaFX layout systems.
If somebody has already wrotethe code and scripting to create the behavior and skin for a WebViewLabel control, then you could just use that and customize it with just JavaFX APIs similar to other controls, however that doesn’t exist as far as I know.
Is there another solution that could support lists and tables
I don't think RichTextFX has this support (I haven't looked at it in detail, so I don't know for sure).
Markdown will support lists well. It will also support tables, likely mapped to a GridView in a JavaFX node, not a TableView. But, the syntax for creating tables may be hard on users if the Markdown is just edited with a plain text editor without icon widget support. I suggest you search the web for JavaFX based Markdown viewers and see if they support your required functionality adequately.
My concern though is if i have a page with 100 webviews on it, do you see performance issues? User could only intereact with 1 at a time ofcourse, but there would still be 100 of them loaded into memory potentially. If it was Java Swing i could simply load content into labels but I didn't realise JavaFX doesnt have that
There might be performance issue with such a setup or it might perform OK.
You should create a minimal app to study and benchmark the performance of many WebView nodes.
A single WebView is slow to initialize with detectable lag when it is the first one shown in the app, but that might just because to display the first WebView the first time the JavaFX system may need to run a lot of runtime setup and initialization.
I never tried with many WebView nodes simultaneously displayed in a scene. Perhaps the internal implementation will allow good performance for that, but you have to test it to make sure or you might expend a lot of effort and be disappointed in the result.
I would guess maybe 100 WebViews would be an upper limit and would be surprised if performance didn't tend to drop either a lot earlier with less of them or soon thereafter with a few more of them.
JavaFX isn't really setup to simultaneously display 1000's of nodes, and, usually that isn't required anyway, you only need to display the stuff which is currently visible to the user. No user could immediately take in all of the data if there is too much of it and no output device could render it.
This is why there exist virtualized controls like TableView and ListView, which feature reusable cells created in factories to display interactive views into the underlying data rather then rendering display notes for the complete (perhaps 1000s or millions of lines) of underlying data.
If you find yourself in a situation where you want to create 1000s of nodes, usually that can be better solved by using cell factories and virtualization like ListView.
Creating a new control with these facilities is an advanced topic and difficult to implement well.
What follows is a list of alternate approaches to the problem from what you outlined in your question. These approaches may not be directly applicable to your application but may be applicable to somebody with a different application who has a similar problem to solve.
Alternate Approach: Do more in HTML and less in JavaFX
Where your multiple WebViews are clustered for rendering output, instead just use a single WebView instance and manage more of the layout via HTML and JavaScript.
There are certain layout tasks (like laying out html) that a web engine is better at than JavaFX. Use the best tool for the job. JavaFX includes both tools (an internal JavaFX rendering engine for rendering the scene graph and an internal Web Engine for rendering html). So you can choose one rendering implementation or the other (or a mixture of the two), whichever best suits the current task you are implementing.
Alternate Approach: Use RichTextFX
For some similar applications RichTextFX may be a preferred solution.
RichTextFX provides a memory-efficient text area for JavaFX that allows the developer to style ranges of text, display custom objects in-line (no more HTMLEditor), and override the default behavior only where necessary without overriding any other part of the behavior.
RichTextFX may not have full support for all of the required features, but can still accomplish many tasks, so investigate its functionality to see if it is a good fit before implementing your own solution.
Alternate Approach: Use Markdown
Under this approach:
Use an editor to edit the Markdown.
Use a viewer to view the rendered Markdown.
It is not necessary to provide full Markdown support, only as much as might be very useful for your application users.
There are various JavaFX Markdown editors and viewers available in third party implementations that you can find if you search the web. There are also many HTML based Markdown editors and viewers which you can use from a WebView. So you could choose to integrate or modify one of those, rather than implementing this from scratch.
For a general purpose library or utility it would be best to make these two things custom controls (e.g MarkDownEditor and MarkDownView) which hide their implementation in a JavaFX control skin and expose their public API in a JavaFX control subclass.
Markdown editor
For the editor which creates (not renders) the Markdown, that can be different from the viewer.
The editor implementation, instead of using the built-in JavaFX HTMLEditor, which is a what-you-see-is-what-you-get (wysiwyg) editor the generates HTML output, you would provide either:
A plain text editor to create the markdown OR
A wysiwyg editor that generates Markdown output.
For the implementation you could use either:
100 percent JavaFX solutions OR
HTML embedded in WebView Markdown editor solutions (e.g. similar to the editor used in StackOverflow for editing questions and answers).
A JavaFX based editor could be as simple as a TextArea, or more complex with additional formatting support widgets implemented in JavaFX (again similar to the editing toolbar provided in the StackOverflow Markdown editor JavaScript implementation, just implemented in JavaFX rather than HTML).
Markdown viewer
Again for the viewer you could choose either:
100 percent JavaFX solutions OR
HTML embedded in WebView Markdown editor solutions (e.g. similar to the editor used in StackOverflow for editing questions and answers).
Whereas, for the editor, using a HTML markdown editor embedded in a WebView would likely be fine, for the viewer, you would likely be better off with a 100 percent JavaFX solution if you wanted to view many snippets of documents and display and size them like Label controls.
Essentially the JavaFX solution would be similar to the implementation of a new control called say, MarkDownLabel, which would render a formatted label like thing based on MarkDown text input.
If the view needs to render a large document, it could be equipped with scrolling, scaling and panning functionality, but a simple view for small documents would not need this. Also a cell style factory style implementation (similar to ListView) could allow the nodes used to render the view to be updated and repurposed for different content rather than recreated (for efficiency), but that is just a performance optimization (and quite tricky) so may not be necessary.
I am getting this error/mal practice popup. I am not sure how to fix/ it. I've looked on QT and I still don't quite understand their explanation. I found this this issue on another post, but I am still not sure what they are saying. It occurs even wherever I create a Checkbox.
Try to import QtQuick 2.* as QtQuick and then use the checkbox as QtQuick.Checkbox
I want to make an application that will benefit both from OpenGL and QML advantages. I want to use QOpenGLWidget and place QML Item over it. Item will be partially transparent. I though it may be possible by using QQuickView but I just figured out that it does cover all transparent parts with black color. An attempt to achieve what I need (an example project) may be found on my GitHub here.
Is it possible to render QML Item with all children onto an existing QWidget in such way that it is visible under Item, wherever Item is not completely opaque?
I guess that in the worst case scenario I could create bitmaps from the QWidget and the Item objects, somehow combine and display them but I seek easier way. If there is no easier way I could just never display those two at the same time.
The reason why I do not want to use OpenGL features in QML directly is this. Maybe I should just use the work around mentioned in the link.
I think you don't need Widgets for it. You can do custom rendering in QtQuick using QQuickFramebufferObject class. I used to do it before, though don't have code anymore. This article seems to explain what to do https://blog.qt.io/blog/2015/05/11/integrating-custom-opengl-rendering-with-qt-quick-via-qquickframebufferobject/
I want to add context menu to a tree widget for one of my project: I want that, when the user clicks in one of the item, a context menu appears giving the user the opportunity to copy that item and paste it in another position.
Searching in the Qt Assistant I came to find there is an easy way to add context menus to QTreeWidgets:
treeView->setContextMenuPolicy(Qt::ActionsContextMenu);
treeView->addAction(yourAction1);
The problem with this method is that it makes the context menu available even when there is no items in the tree widget not to mention the fact that identifying which was the item clicked is not straightfoward.
After some research I discovered there are some ways of doing what I want. For instance this and this (which are essentially the same) as well as this give some idea (and this as well). The problem is I disliked those solutions and I'm curious as to if there is no other way known to implement what I want in a more "direct" way such as what is available for QTreeWidget as mentioned at the top. Notice I'm here considering Qt 5.4 and forward.
I plan to use a spacer in my toolbar (below) between the 'Pause' and 'Settings' buttons to make the 'Settings' button right-aligned within the QToolBar. The separator below isn't cutting it.
The following method apparently works, although I haven't actually tried it yet:
QWidget* spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// toolBar is a pointer to an existing toolbar
toolBar->addWidget(spacer);
toolBar->addAction("Right-aligned button");
(thanks to http://www.ffuts.org/blog/right-aligning-a-button-in-a-qtoolbar/)
Qt Designer doesn't seem to allow adding widgets to a QToolBar, although QToolBar does have an addWidget() method, as used above. So if I want to be able to add the spacer to my toolbar using Qt Designer (with a plugin, not promotion), should I subclass QAction, QWidget, or QWidgetAction? Is there even a way to write custom action plugins for Qt Designer?
You cannot subclass QAction and expect to use it gracefully in Qt Creator/Designer. It uses the tags in the ui and to add them to menus and toolbars. You can't specify a class="customclass" element along with a entry like other widgets. Because actions are treated differently, you cant even subclass them so that's out.
As for QWidgetAction/QWidget, you are in the same boat as QActionGroup, if you know what I mean.
(I personally think this is a big overlook in the design of the form designer!) So previewing that in the designer is also out.
The last thing you could do (invisible option 3) is QToolbar subclassing, but alas you can subclass it, but still, you wont be seeing anything in the desinger other than what a plain old QToolbar can do. That doesnt mean that it is completely out which is the point I'm coming to:
The real answer would have to be invisible option 4, subclassing QToolbar as a designer plugin (which would require a lot of coding on your part if you dont already know of one that is out there, I dont know of any that actually have designer plug-ins) Form Designer plugins are more not than often, but you could find someone's subclassed toolbar, and make it into a plugin yourself saving you a step (qt creator even has a template for that), then use it in qt designer and your set.
I know this question got asked 2 years ago but when I see an unanswered question, I think of those searchers that might read and get frustrated by a dead-end so if I can contribute I do... I hope I did at least help you or anyone else get on the right track, and good luck to you all!