JavaFX Make WebView behave like Label in VBox - javafx

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.

Related

How to implement specific functionality in a custom text object in pyQt?

I need to implement a specific text object in QEdit PyQt. I need rich objects that will content special information, capture the keyboard arrow keys only when cursor text is on these objects. They need support selection and copy / paste operations also. Outside of them, the cursor must act as normal behavior. At this time, the object does not need to show images or svg etc. Is only for capture text cursor purposes and to store specific information related with the object.
I read that I can implement a customized text object with this example:
textobject.py
but the example does not contain information about implement new html tags, how to capture focus, etc.
My questions are:
How can I add a new html tag related with my custom object for loading and saving documents?
How can I detect when text cursor is on the custom object to modify keyboard cursor behavior?
PyQt documentation says:
"Copy and Paste operations ignore custom text objects."
So is no way to implement copy / paste functionality?
How can I access to my custom object content?
I want to have specifics methods to navigate within the information contained in the object. I could save this information in another object, but I need to synchronize the information with the cursor because a document can contain many of these objects.
Context for my application: I'm developping a very specific application for blind users. I am blind myself. I need to make very specifics customizations to the Richttext area for accessibility. The goal is to develop an accessible math equation editor. For this, I need to add support for mathMl tag. I will process mathMl content inside these tags in my custom object, this part is already implemented at basic level without using any GUI framework. At this time I don't need to show the equation in the screen, I let to screen readers interact with my editor via a COM object. At this time, the equation box is functional, but I need to integrate this functionality in a rich text area. the idea is, a document can contain N math expressions that can be read, modified and saved in a document.
I plan to implement accessibility for sighted people also in the equation editor, but it is beyond my skills and is not a priority right now.
Also I need to accessibilize tables in rich text area, currently tables are not accesible in QT. Then I think to extend qt table functionality. Style elements like headings etc, does not exist in pyQt. OK, you can write a paragraph with heading style, but screen readers can't detect it. So I need to develop this functionality. I could solve it with customized objects I think.
Currently it need to work in windows only. Maybe later I'll consider other platforms.
I started to program in pyQt yesterday, but I already read a lot of documentation. I wrote a basic rich text editor to test my requirements.
I already tried to implement this application with wxPython phoenix because I've been using WX for a long time. But I couldn't implement custom text objects and I didn't find documentation about it.
If pyQt is limited for my requirements, could you recoment another gui framework?
Y could consider another languages, I chose Python because Python has many math tools like sympy or numpy, and implement user scripting functionality in the app is very easy.
Sorry for my bad english.

Layout required in designer but not in hand coded app?

I'm new to Qt and it's quite a learning curve!
I've been search/reading/hacking and learning for most of the day on this one.
I'm working on an app that will have a image in a scroll area as it's main purpose. The image viewer example is where I started. This example appears to be "laid out by hand" if you will - i.e. there are no .ui form files for it (it's trivial so why not).
So here is the rub - I want to use designer to build a much more sophisticated app. If I start fresh with a new project and use designer the resizing doesn't work unless I add layout (used vertical, all appear to fix the resize issue) to the .ui form.
Designer creates a lot more code to do the same thing - I don't care, it is after all an IDE / code generation tool.
However it's not clear to me why the layout is required when I use designer and not if I code it by hand. I searched the code for the image viewer example and there doesn't seem to be ANY layout involved at all, just 3 or 4 nested widgits (Main/Scroll/Label).
Is the default layout basically built in?
The example you linked to uses a QMainWindow. This widget has its own layout because it has built-in support for menubars, toolbars, dock widgets and a statusbar:
Main Window Framework:
Normally, the widget set as the central-widget would need to have a layout explicitly set on it in order to layout its own child widgets. However, in your linked example, the central-widget is a QScrollArea, which also happens to have a built-in layout. This is all just coincidental, though. The large majority of widgets don't have a built-in layout, so most GUIs will need to explictly add at least one layout, and several will usually be needed for more complex applications.
I would say Qt Designer is absolutely essential when it comes to experimenting with layouts (especially when you start learning Qt). Even if you don't actually use the ui file, it's still very helpful to just view the code that would be generated from it.

Zooming a view in PyQt?

I have a simple to do tree application that displays a QTreeView inside of a QMainWindow. I want to give the user the ability to change the magnification level of the content (using a spinbox most likely), but without actually changing the underlying font size of the text.
Is there a way to do this without changing my whole app to a QGraphicsScene? The app is just showing a good-old fashioned tree with text, no graphics or anything fancy other than wanting to change the magnification of the view; hence, I am thinking that switching to a graphics scene would be overkill.
Or, am I wrong, and switching to a graphics scene is the only simple way to do it?
Note a trimmed down version of the app is at Code Review. It contains a SSCCE, but is a bit long to post here.
In a site discussing how to put widgets on a scene, trolltech wrote (emphasis added):
I myself and several other Trolls’ve spent some time researching this
topic [how to embed a widget in a QGraphicsScene]. It’s not trivial;
most solutions to embedding widgets into a scene end up with several
serious drawbacks. That’s also why Qt doesn’t have any off-the-shelf
solution to this.
Widgets cannot be scaled or rotated, but graphics items can.
This suggests I cannot perform, in a simple way, the operations I want to on my QWidget by itself. That is, perhaps I need to add it to a scene, which is what I was trying to avoid. If that is the answer, then I'll accept it and start a new question if I get stuck doing that.
Note I just found this question, which is pretty much a duplicate, and does not have an (accepted) answer.
Related content
http://www.qtcentre.org/threads/62745-Zoom-a-view
QTableView Zoom In/Out
Drawing widgets (such as buttons) over QGraphicsView
QGraphicsView Zooming in and out under mouse position using mouse wheel
https://forum.qt.io/topic/15308/qgraphicsview-zooming-with-qslider
https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView
As suggested by the docs quoted from trolltech in the original question, there is no built-in method to zoom on a view.
If the goal is to avoid the use of QGraphicsViews, the simplest way to separate the size of the font on the screen, versus the size of the font saved or printed, is to basically have two fonts. One to be displayed on the screen you can call 'zoom', versus the other to be saved/printed and call that 'font size'.
I got this idea from qtcentre (in a post I added to the original post too):
http://www.qtcentre.org/threads/62745-Zoom-a-view

Qt and UI Skinning

I wanted to consult with the sages here regarding Qt and skinning, get your opinion and chart a path for my development. My requirements are as follows:
My Qt/C++ application (cross platform with Mac, Windows and Linux versions) needs to have modular skins.
A skin is defined as a set of one or more elements: - Window background texture - Look/feel of UI controls such as edit boxes, drop down, radio buttons, buttons etc. - Look/feel of window "caption", resize grips etc.
Skins will be installed with the application installer, allowing the user to choose which one he/she wants to use. Users should be able to change skins on the fly.
Can I go the QML route? should this be custom and based on simple resources which are built into the application? Any design advice will be appreciated.
Thanks.
If I understood you correctly then stylesheet is the best way forward. You can create stylesheets similar to CSS and then pass them as command line option to your application or load on invocation to style your application at runtime. That way you can create multiple stylesheets each having a different look and feel and allow user to load them at will. Since its CSS it doesn't need any new learning and you can keep all your styling outside your source code.
Here are a list of resources that can get you up and running quickly:
http://blog.qt.io/blog/2007/11/27/theming-qt-for-fun-and-profit/
http://doc.qt.io/qt-5/stylesheet.html
I haven't played with QML yet, but you could also create a custom QStyle implementation that supports your resource format. Note that you'd lose style sheet support if you went this route.
Changing window captions is a little trickier if you want portability.
QML, if I understand correctly, doesn't really skin the widgets, it mainly deals with GUI layout etc etc.
QStyle is used to change the looks. It is a bit low-level though, and requires programming, so if you want to load different user-created skins (from an XML or so) it might be tricky to support extensive skinning. Chaining colors and a few items are easy enough though. (There might be someone else who've done something you could re-use.. not sure.)
For modifying widgets, use QStyle::polish(). You could use that to change the background picture (if it's a top-level window, or of a certain class). There are numerous repaint method to change almost every part of every widget.
Store/load the style using QSettings, by reading and setting the desired Style just after QApplication but before your main window is constructed.

Using QT to build a WYSIWYG Editor for a Custom Markup Language

I'm new to QT, and am trying to figure out the best means of creating a WYSIWYG editor widget for a custom markup language that displays simple text, images, and links. I need to be able to propagate changes from the WYSIWYG editor to the custom markup representation.
As a concrete example of the problem domain, imagine that the custom markup might have a "player" tag which contains a player name and a team name. The markup could look like this:
Last week, <player id="1234"><name>Aaron Rodgers</name><team>Packers</team></player> threw a pass.
This text would display in the editor as:
Last week, Aaron Rodgers of the Packers threw a pass.
The player name and the team name would be editable directly within the editor in standard WYSIWYG fashion, so that my users do not have to learn any markup. Also, when the player name is moused-over, a details pop-up will appear about that player, and similarly for the team.
With that long introduction, I'm trying to figure out where to start with QT. It seems that the most logical option would be the Rich Text API using a QTextDocument. This approach seems less than ideal given the limitations of a QTextDocument:
I can't figure out how to capture navigation events from clicking on links.
Following links on click seems to only be enabled when the QTextEdit is readonly.
Custom objects that implement QTextObjectInterface are ignored in copy-and-paste operations
Any HTML-based markup that is passed to it as Rich Text is retranslated into a series of span tags and lots of other junk, making it extremely difficult to propagate changes from the editor back to the original custom markup.
A second option appears to be QWebKit, which allows for live editing of HTML5 markup, so I could specify a two-way translation between the custom markup and HTML5. I'm not clear on how one would propagate changes from the editor back to the original markup in real-time without re-translating the entire document on every text change. The QWebKit solutions looks like awfully bulky to me (Learning WebKit along with QT) to what should be a relatively simple problem.
I have also considered implementing the WYSIWYG with a custom class using native QT containers, labels, images, and other widgets manually. This seems like the most flexible approach, and the one most likely not to run into unresolvable problems. However, I'm pretty sure that implementing all the details of a normal text editor (selecting text, font changes, cut-and-paste support, undo/redo, dragging of objects, cursor placement, etc.) will be incredibly time consuming.
So, finally, my question: are there any QT gurus out there with some advice on where to start with this sort of project?
BTW, I am using QT because the application is a desktop application that needs platform independence.
Given that I got no advice here, I decided to go with the QTextEdit approach, although I'm actually using a QTextBrowser that is set to be editable so that I can capture link navigation events. I will be using QTextCharFormat's with the link names set to unique identifiers in order to convert from the QTextEdit back to the custom markup. The QTextEdit supports images already, so I won't have to deal with those.
I think I will hit the biggest roadblocks with the fact that I need to be able to insert/grow/shrink tables whose cells can have Excel-style functionality. I have not yet figured that whole process out.

Resources