fxmisc / flowless Goto Github PK
View Code? Open in Web Editor NEWEfficient VirtualFlow for JavaFX
License: BSD 2-Clause "Simplified" License
Efficient VirtualFlow for JavaFX
License: BSD 2-Clause "Simplified" License
Per the end of my comment in #32:
As a side note, I think there should be a way to get the navigator's current position, so that one could use that to determine what to display after some change occurs (e.g. scroll from the current position or display an item 10 indexes higher, etc.) via
VirtualFlow
's show method. In order to support that functionality, there would need to be more view-related API methods that returned aTargetPosition
of a specific cell.
As reported in FXMisc/RichTextFX#535, binding two Vertical-Gravity Virtualized content's estimatedScrollY
properties bidirectionally doesn't update them correctly. If the call is prop1.bindBidirectionally(prop2)
, the two values will be correct whenever prop1
scrolls but not when prop2
scrolls.
I've been experimenting with Flowless in the context of a log-viewer and so far it has suited my needs perfectly, thanks!
I do however have one use-case that I'm struggling to implement. I want the VirtualFlow to vertically resize dynamically to the size of its content up to the point where it's limited by it's parents size. See this gist: https://gist.github.com/oddbjornkvalsund/f232c0f61fdc368d704f
It almost works, but for the rendering of the first cell it seems to get the calculated height wrong when the VirtualFlow is need of a layout pass. See line 90 of the gist.
Coming from FXMisc/RichTextFX#412. There's a nice GIF and explanation that shows the problem at hand.
Is it Possible To Itntoduce Negative Checks, As in some instances, I get Index Out Of Bounds Exception -1, with no cause, usually after being idle for a while,with a Huge List
Coming from FXMisc/RichTextFX#390, when an anchor cell is replaced, the viewport does not reuse the same index for the anchor cell. In some situations, this causes scrolling where none should occur.
Adding items too fast to item list of a VirtualFlow causes scroll if the item list is empty. Waiting few milliseconds after adding first item fixes the problem. Also calling visibleCells() method after adding first item fixes the problem but I don't know if its about time it takes or something else.
Demo:
public class Main extends Application {
ObservableList<Integer> items = FXCollections.observableArrayList();
VBox vbox = new VBox();
Button add30Buttton = new Button("add 30");
Button add1Button = new Button("add 1");
Button removeAllButton = new Button("remove all");
VirtualFlow<Integer, ?> virtualFlow = VirtualFlow.createVertical(items, i -> Cell.wrapNode(new Label(i.toString())));
VirtualizedScrollPane<VirtualFlow<Integer, ?>> scrollPane = new VirtualizedScrollPane<>(virtualFlow);
@Override
public void start(Stage stage) {
add30Buttton.setOnAction(event -> {
items.add(0);
// virtualFlow.visibleCells(); // uncommenting this fixes the problem
for (int i = 1; i < 29; i++) { items.add(i); }
});
add1Button.setOnAction(event -> items.add(999));
removeAllButton.setOnAction(event -> items.clear());
VBox.setVgrow(scrollPane, Priority.ALWAYS);
vbox.getChildren().addAll(add30Buttton, add1Button, removeAllButton, scrollPane);
Scene scene = new Scene(vbox, 300, 400);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Hi,
Nice projects you've got here. I'm trying to use Flowless because using ListView + Textflow in Java 8u45 somehow makes the TextFlows on the list lose or change formatting when I add a new item. I'm not sure if I got the concept of TextFlow right, but I'm using a new instance for each item, because my ListView is meant to hold Nodes other than TextFlow e.g. WebView.
Anyway, I switched to test Flowless, because I was suspecting a layout issue with ListView, but I found that my TextFlows lose some formatting such as bold highlighting; though other stuff, like underline, still works.
The abstract class of my list item is just an extension of AnchorPane where I add the content after instantiation:
// content is an arbitrary node
AnchorPane.setTopAnchor(content, 0.0);
AnchorPane.setBottomAnchor(content, 0.0);
AnchorPane.setLeftAnchor(content, 0.0);
AnchorPane.setRightAnchor(content, 0.0);
getChildren().add(content);
Checking the TextFlow properties, everything seems as it should be:
[Text[text="Me:
", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Bold, family=System, style=Bold, size=14.0], fontSmoothingType=GRAY, underline, fill=0x000000ff], Text[text="test", x=0.0, y=0.0, alignment=LEFT, origin=BASELINE, boundsType=LOGICAL, font=Font[name=System Regular, family=System, style=Regular, size=15.0], fontSmoothingType=GRAY, fill=0x000000ff]]
Would you have any ideas on what's going on?
Thanks :)
Edit 1: Agh. Sorry for the bother, it seems the same issue is happening as in ListView, so it might be an issue with TextFlow itself? It seems the only time certain formatting becomes active or works, is if ALL children have the format e.g. bold.
Edit 2: I didn't know where to ask, but how do you force the VirtualFlow to scroll to the very bottom? ListView had the convenient scrollTo(index), but VirtualFlow doesn't have that. I've tried VirtualFlow.scrollY(Double.MAX) and VirtualFlow.show(index), but they work erratically.
In the VirtualFlow class, there is no constructor but static methods taking a list of items and a mapping function to convert the items into Cells.
Are there any costs to performance if Node objects are passed in directly?
This is really a question (I think), but I don't see any place to make those :).
For reusable cells, I change the classes associated with some of the elements when I reuse them. For example, my cell can be an HBox
that contains a few Label
elements. I try to change the classes on the labels, but when I scroll, Flowless applies the classes randomly (I'm changing colors with those classes).
Is there anything I can do to make this work, or is this a bug?
A deficiency found in FXMisc/RichTextFX#238.
Basic idea from Tomas:
Methods of
VirtualFlow
whose result depends on the layout should themselves calllayout()
as the first thing. [Otherwise, the returned results aren't always valid. (I added this)] Such methods would includegetCellIfVisible
,visibleCells
andhit
. The fact that they first call layout() should be documented in their Javadoc.
Thanks for this great library.
I was wondering if there are similar projects or if it would be easy to implement a GridPane in a virtualFlow. Given we have a List and we could specify that we want two columns and distribute space equally between the cells.
Do you know of any project that achieves this or is it rather simple to implement?
Cheers
Robin
Coming from FXMisc/RichTextFX#256, there should be an option to render a default cell when the model's list of cells is empty.
This may require implementing TomasMikula/ReactFX#53
Caused by this commit, which is meant to fix a scroll that occurs do to a layout call when a VirtualFlow is wrapped in a VirtualizedScrollPane.
Running maven-javadoc-plugin with detectLinks set to true gives an error:
error: Error fetching URL: https://github.com/FXMisc/Flowless/#flowless/apidocs/ (java.io.FileNotFoundException: https://github.com/FXMisc/Flowless/package-list)
I am using RichTextFX in my project, but I am having an issue which seems to be contained to Flowless:
I use the following code to append a certain message in bold font:
private InlineCssTextArea text;
//....
//In constructor
text = new InlineCssTextArea();
text.setWrapText(true);
//....
public void appendBold(String msg) {
Platform.runLater(() -> {
int curPos = text.getText().length(); //The index of the first new character
int endPos = msg.length() + curPos - 1; //The index of the last new character
text.appendText(msg);
text.setStyle(curPos, endPos, "-fx-font-weight: bold;");
});
}
(I have two other, similar methods for italic and normal fonts)
This usually works, but sometimes it fails with a StackOverflowError, where the following section of stacktrace repeats continuously:
at org.fxmisc.flowless.VirtualFlow$$Lambda$238/562912964.invalidated(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.binding.DoubleBinding.invalidate(DoubleBinding.java:222)
at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:51)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.binding.DoubleBinding.invalidate(DoubleBinding.java:222)
at org.fxmisc.flowless.VirtualFlowContent.setLengthOffset(VirtualFlow.java:1426)
at org.fxmisc.flowless.VirtualFlowContent.setLengthPosition(VirtualFlow.java:608)
at org.fxmisc.flowless.VerticalFlowMetrics.setVerticalPosition(VirtualFlow.java:1848)
at org.fxmisc.flowless.VirtualFlow.lambda$new$3(VirtualFlow.java:217)
at org.fxmisc.flowless.VirtualFlow$$Lambda$240/1469374270.changed(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:361)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.DoublePropertyBase.fireValueChangedEvent(DoublePropertyBase.java:106)
at javafx.beans.property.DoublePropertyBase.markInvalid(DoublePropertyBase.java:113)
at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:146)
at javafx.scene.control.ScrollBar.setValue(ScrollBar.java:151)
at org.fxmisc.flowless.VirtualFlow.lambda$new$1(VirtualFlow.java:211)
I can not find any common factor between the instances where this error occurs, so I'm not sure if you can help me with this, but I would very much appreciate it if you could have a look.
Coming from FXMisc/RichTextFX#171 (and also FXMisc/RichTextFX#77)
It would be great if [the viewport] automatically changes it's height [and by implication, width] based on its content, so that [one] can set a minimum and a maximum and the box just gets bigger as additional content is added [or smaller as content is removed]. This could probably be done using
minHeight
andmaxHeight
.
When using a control embedded inside a VirtualizedScrollPane
with the upcoming release 16 of JavaFX, then it is possible under specific circumstances that the method layoutChildren()
becomes perpetually invoked, causing high CPU and GPU usage even when the application is idle.
These conditions are:
The following code sample illustrates the issue, when run under javafx 16, with for instance a 125% scale (you can force it with -Dglass.win.uiScale=1.25
or -Dglass.gtk.uiScale=1.25
, on windows or linux, respectively):
public class TextScroll extends Application {
private final static String LONG_LINE_OF_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Nulla viverra fringilla dictum. Integer faucibus laoreet nulla eget vehicula. " +
"Vivamus et arcu eget metus interdum tincidunt ac sed libero. Phasellus vestibulum" ;
@Override
public void start(Stage stage) throws Exception {
var textArea = new CodeArea();
textArea.replaceText(LONG_LINE_OF_TEXT);
Scene scene = new Scene(new VirtualizedScrollPane<>(textArea), 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The root cause for this, is that the VirtualizedScrollPane
request a layout when the height of the horizontal scroll bar changes but in the course and the layout, the calculated value for this height changes, which in turns triggers a request layout, etc...
This is due to the fact that a bug was fixed in JavaFX 16 (https://bugs.openjdk.java.net/browse/JDK-8211294) where the mechanism which snaps coordinates to actual pixels on screen with a non integer scale factor was broken; this is now fixed in javaFX, but Flowless remains unaware of the need for enforcing snapping, which causes some coordinates calculated in Flowless to be rounded differently that their equivalent in JavaFX; in this case this is what caused the scrollbar height to be different in two nested calls to layoutChildren
, and lead to the constant layout.
A solution is to use snapSize()
method to ensure height and width are snapped (i.e. ceil'd) to pixels consistently with JavaFX.
(NB: Ideally, we'd want to use snapSizeX()
and snapSizeY()
instead of the deprecated snapSize()
so that we properly support non square screen ratio, but this is not available in JavaFX 8 that Flowless is built against.)
Is there a way to position the cells manually?
I'm trying to create a tree structure. In a tree structure the children of a node are positioned slightly to the right of the parent.
How can I tell Flowless to position the cell like that? Also, the space from the right depends on the level of the node, so it should be dynamic
Coming from FXMisc/RichTextFX#507, VirtualizedScrollPane
should include a pseudo css class, focused
, when its content is focused since the scroll bars are now separated from the content:
import javafx.scene.Node;
import org.fxmisc.flowless.Virtualized;
import org.fxmisc.flowless.VirtualizedScrollPane;
public class VirtualizedScrollPane<T extends Node & Virtualized> extends Region {
private PseudoClass FOCUSED = PseudoClass.getPseudoClass("focused");
public VirtualizedScrollPaneExt(T content) {
super(content);
content.focusedProperty().addListener((obs, oldVal, newVal) -> {
pseudoClassStateChanged(FOCUSED, newVal);
});
}
}
Created a simple application that works with RichTextFX. Added a VirtualizedScrollPane
, but IntelliJ reports the following problems:
Is there a minimal example showing how to use Flowless + RichTextFX + JDK 17 + Gradle 7.2?
The relevant files are below.
module com.keenwrite.editor {
requires javafx.controls;
requires javafx.fxml;
requires org.fxmisc.richtext;
// Is this line correct? If not, what module is required?
requires org.fxmisc.flowless;
opens com.keenwrite.editor to javafx.fxml;
exports com.keenwrite.editor;
}
package com.keenwrite.editor;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.fxmisc.richtext.StyleClassedTextArea;
public class HelloApplication extends Application {
@Override
public void start(final Stage stage) {
final var area = new StyleClassedTextArea();
area.requestFollowCaret();
area.setWrapText(true);
// The IDE cannot find the class to import, despite the module being found.
final var scrollPane = new VirualizedScrollPane(area);
final var scene = new Scene(area, 400, 400);
stage.setScene(scene);
stage.show();
}
public static void main(final String[] args) {
launch();
}
}
plugins {
id 'java'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'org.fxmisc.richtext:richtextfx:0.10.7'
implementation 'org.fxmisc.flowless:flowless:0.6.7'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
group = 'com.keenwrite'
version = '1.0-SNAPSHOT'
description = 'editor'
java.sourceCompatibility = JavaVersion.VERSION_17
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
rootProject.name = 'editor'
The current design of this class has a no-args constructor which hinders the ability to use VirtualizedScrollPane
inside an FXML file.
This is from RichTextFX issue 935
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.stage.Stage;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.StyleClassedTextArea;
public class Pagination extends Application {
public static void main( final String[] args ) {
launch( args );
}
@Override
public void start( final Stage stage ) {
final var editor = new StyleClassedTextArea( false );
final var scrollbars = new VirtualizedScrollPane<>( editor );
scrollbars.setVbarPolicy( ScrollPane.ScrollBarPolicy.ALWAYS );
editor.setWrapText( true );
stage.setScene( new Scene(scrollbars) );
stage.show();
}
}
Ctrl+Home
to navigate to the top of the document.Ctrl+End
to navigate to the bottom of the document.Page Up
to navigate upwards.The view port navigates back one page for as long as the key is held, or the top is reached.
The computation for the new caret position goes awry in a variety of ways, depending on how much text is pasted and the view port size, resulting in one of the following behaviours:
I propose that the Gravity of VirtualFlow should be made styleable via CSS (and thus mutable). If you're happy with the justification to add and the implementation strategy, I'm happy to write the code and submit a pull request.
Justification:
Use case 1: I have a RichTextFX control where I want the lines to be at the bottom (it's a history view of a terminal-like display, where the entry is underneath, and history should appear at the bottom and be bumped upwards as more is added). Because the Gravity of the VirtualFlow is a constructor parameter, I can't actually alter it unless I patch RichTextFX to allow it, and even then, it's a bit odd to have to pass a style attribute as an unchangeable constructor parameter. The VirtualFlow isn't public and we don't want to make it so, but if we could set the gravity parameter by CSS then it would solve the whole issue.
Use case 2: I am thinking of using VirtualFlow for a code-completion like popup window. It will appear below the line if there's room (with items at the top), or above the line otherwise (with items at the bottom). If Gravity is unchangeable then I can't dynamically move it from below to above without creating a new VirtualFlow.
Implementation:
I've had a look at the code and I don't think it will be a difficult change. VirtualFlow's constructor takes the gravity parameter, but only uses it to pass to Navigator's constructor. Navigator stores it as a field but only uses it in the utility methods at the bottom of the class. In turn these are only used by the method fillViewportFrom. Assuming IntelliJ's call hierarchy is complete, this all leads back to Navigator.layoutChildren():
I think this means that we can make a styleable property (with accessor and mutator) for gravity in VirtualFlow, which is passed as a reference to Navigator, and add a change listener to the property that calls VirtualFlow.requestLayout (or just Navigator.requestLayout?). Let me know if I've missed anything on the implementation side.
org.fxmisc.flowless.CellListManager#getCellIfPresent
changes the implementation as blow is better.
public Optional<C> getCellIfPresent(int itemIndex) {
if (itemIndex>=cells.size()||itemIndex<0){
return Optional.empty();
}
return cells.getIfMemoized(itemIndex); // getIfMemoized() may throw
}
Hi, I am facing an issue with implementing Flowless with irregular sized cells. Basically it doesn't populate a cell with data even if a part of it is visible in the viewport. This can be seen in the image below:
This goes away if I scroll a few ticks down as seen below:
Another issue that I have is that it does not update the cell if the underlying data has been modified by the user. For example in the last image above the thumbnails should loose their borders once a user double clicks on them, but this does not happen with Flowless. It works fine if I use a ListView.
import java.util.Arrays;
import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class VirtualFlowTest2 extends Application{
// Attributes
private static final Random RANDOM = new Random();
// Start Methods
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage stage) throws Exception{
StackPane contentPane = new StackPane();
VirtualFlow<String, Cell<String, ?>> virtualFlow = VirtualFlow
.createHorizontal(FXCollections.observableArrayList(Arrays.asList("A", "B", "C", "D")), (value) -> {
Button button = new Button();
button.prefHeightProperty().bind(contentPane.heightProperty());
button.prefWidthProperty().bind(contentPane.widthProperty().multiply(.1));
button.setText(value);
button.setBackground(new Background(
new BackgroundFill(new Color(RANDOM.nextDouble(), RANDOM.nextDouble(), RANDOM.nextDouble(), 1),
CornerRadii.EMPTY, Insets.EMPTY)));
return Cell.wrapNode(button);
});
contentPane.getChildren().add(virtualFlow);
max(virtualFlow);
stage.setScene(new Scene(contentPane, 500, 500));
stage.show();
}
public static Node[] max(Region... nodes){
for(Region region : nodes){
region.setMinSize(0, 0);
region.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
}
return nodes;
}
// End Methods
}
As you can see I created a Horizontal VirtualFlow with cells 10% of the width of the parent. Now when resizing using only the width of the stage, which is also resizing the parent, you can see that the cells don't resize to 10% of the parent. This is due to SizeTracker lengthFN:
Val<Function<Cell<?, ?>, Double>> lengthFn =
avoidFalseInvalidations(breadthForCells).map(breadth -> cell -> {
return orientation.prefLength(cell, breadth);
});
That static avoid false invalidation method I'm not 100% sure the purpose of it but essentially in this cause eats up the event that would cause the cells to be resized. This is just for the width. When you change the height of the VirtualFlow you can see that the cells height also change with it.
When you delete the call to avoidFalseInvalidations it will now properly resize the cells regarding resizing the stage's width.
Val<Function<Cell<?, ?>, Double>> lengthFn =
breadthForCells.map(breadth -> cell -> {
return orientation.prefLength(cell, breadth);
});
Now say you make a Vertical VirtualFlow. With my same code you would have to make the cell's height take 10% of its parent and max width. Now when changing the height of the stage the VirtualFlow doesn't resize its cells while if you change the width it does. The same fix above works.
Now I know I provided a solution but I don't exactly know what that avoid false invalidation method exact purpose is. Maybe it is trying to prevent like an infinite loop of invalidating I don't know.
In the 0.6 release and also in master:
Flowless/src/main/java/org/fxmisc/flowless/Navigator.java
Lines 242 to 245 in 6466073
I think we should check whether avgLen.isPresent() and Math.abs(avgLen.get()) < 1e-6, and if that's the case just set itemsBefore and itemsAfter to 5, same as what we do when avgLen is not present.
Otherwise, during layout, under some circumstances, avgLen is 0.0, itemsBefore is roughly 2^31, itemIndex - itemsBefore is a big negative number, after several function calls, the values are checked in reactfx and exception is thrown.
Maybe we should figure out why sizeTracker returns 0.0 as average length estimate, but that's beyond my knowledge of this project, and doing such a check before doing dividing seems reasonable enough to me.
I've asked this on StackOverflow, since that seems like the place to have that discussion. However, I am also opening an issue here because I think you, @TomasMikula , would probably be one of the few who could answer this specific question.
Some components in JavaFx, which contains scrollbars, such as TextArea, ListView and ScrollPane, when the content is very long, the scrollbar's thumb become very small.
I kind of hoping this Flowless could provide a solution for this problem. So, is there any suggestion?
When laying out cells with varied heights, the scroll bar's value is only an estimate of how far the content has been scrolled, not the actual position. Thus, the goal is to turn this estimated value into the accurate value. Since this is only useful in some use cases, this should be an option, not a requirement.
One solution for achieving this thought of by Tomas himself (from above issue):
One could come up with some sophisticated algorithms, where the scroll position starts with an estimate (as it does now), and then in the background gradually improves the estimate. This would result in some flickering of the scrollbars before reaching the exact value, but that might be acceptable.
Hi,
is this library of any use for JavaFX versions >8
? The benchmarks in the readme have been performed on very old JavaFX 8 builds, so I was wondering if it still provides performance benefits on newer versions?
This occurs because Navigator#distanceFromSky
returns a positive (rather than zero) value. The horizontal scrollbar is temporarily not visible and then later is visible. This screws up the returned value of the last cell's maxY
- sizeTracker.getViewportLength()
.
This exception is from a use of RichTextFX, but the stack trace is solely from Flowless so I'm reporting it here:
Exception in thread "JavaFX Application Thread" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at org.fxmisc.flowless.CellListManager.getPresentCell(CellListManager.java:56)
at org.fxmisc.flowless.CellPositioner.getVisibleCell(CellPositioner.java:28)
at org.fxmisc.flowless.Navigator.fillForwardFrom0(Navigator.java:231)
at org.fxmisc.flowless.Navigator.fillForwardFrom0(Navigator.java:217)
at org.fxmisc.flowless.Navigator.fillTowardsSkyFrom0(Navigator.java:323)
at org.fxmisc.flowless.Navigator.fillViewportFrom(Navigator.java:284)
at org.fxmisc.flowless.Navigator.visit(Navigator.java:161)
at org.fxmisc.flowless.MinDistanceTo.accept(TargetPosition.java:148)
at org.fxmisc.flowless.Navigator.layoutChildren(Navigator.java:67)
at javafx.scene.Parent.layout(Parent.java:1087)
at org.fxmisc.flowless.VirtualFlow.layoutChildren(VirtualFlow.java:165)
at javafx.scene.Parent.layout(Parent.java:1087)
at javafx.scene.Parent.layout(Parent.java:1093)
...
at javafx.scene.Scene.doLayoutPass(Scene.java:552)
The method getPresentCell does warn that it can throw, but I can't spot a relevant catch anywhere in the hierarchy. What's particularly problematic is that after this exception, the GUI stopped updating or responding. (That may be due to our surrounding code or something RichTextFX does, though -- it's hard to say.) I haven't yet been able to reproduce it consistently, unfortunately, but I've seen it twice now.
The vertical scroll lacks fluidity on OSX 10.11.6, while the horizontal scroll works as expected.
I have this problem with the demos, with juliar, and with my apps that integrate RichTextFx
See attached image for an illustration of the problem
This issue its related to RichTextFX/issues/657 and RichTextFX/issues/265, and the solution proposed by @StrangeNoises works for me on OSX.
The @StrangeNoises code is integrated in the last codebase at #57
The VirtualFlow's scroll-related values (estimated & total) are calculated based on the average height/width of a given cell's node. When the VirtualFlow is rendering its children, sometimes these averages get recalculated to a different value than they were before the rendering began. After rendering, these values are often set back to what they were before the rendering started.
However, since there is nothing stopping these values from propagating to their public end points (estimated & total scroll X/Y values), anything listening to them or subscribed to them will be updated immediately as well. As a result, one cannot use the scroll values to trigger some change when they are updated, as the layout process itself will always trigger such a change even when one doesn't ultimately occur.
I'm trying to create list having expandable component inside cell. Everyting works great except from few last items on the list. Sometimes trying to expand/collapse has no effect. ActionEvent is never brought to handler as completly different button seems to be focused.
Is there any chance I can get my feature working correctly?
To reproduce please expand some element in the begining of the list, then scroll down to the bottom of the list and try to expand/collapse item using "Expand" button. Sometimes it takes few tries to reproduce.
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<Employee> employees = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
employees.add(new Employee("John " + i));
}
VirtualFlow<Employee, Cell<Employee, EmployeeView>> virtualFlow = VirtualFlow.createVertical(employees, employee -> Cell.wrapNode(new EmployeeView(employee)));
VirtualizedScrollPane<VirtualFlow<Employee, Cell<Employee, EmployeeView>>> virtualizedScrollPane = new VirtualizedScrollPane<>(virtualFlow);
Scene scene = new Scene(virtualizedScrollPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
class Employee {
private boolean expanded;
private final String name;
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
}
class EmployeeView extends VBox {
private final Button button = new Button("Expand");
private final TextField textField = new TextField();
private final HBox headerPane = new HBox(button, textField);
private final StackPane contentPane = new StackPane();
{
contentPane.setStyle("-fx-background-color: red;");
contentPane.setPrefHeight(100);
getChildren().addAll(headerPane, contentPane);
}
public EmployeeView(Employee employee) {
textField.setText(employee.getName());
contentPane.setVisible(employee.isExpanded());
contentPane.setManaged(employee.isExpanded());
button.setOnAction(actionEvent -> {
employee.setExpanded(!employee.isExpanded());
contentPane.setVisible(employee.isExpanded());
contentPane.setManaged(employee.isExpanded());
});
}
}
Starting with javaFX 17-ea+11 I noticed that scrolling a StyledTextArea (from RichTextFX) no longer worked properly: when clicking the scroll bar elements, the text scrolls for the first couple of clicks and then entirely stop responding to clicks on these elements.
It does respond to using the mouse wheel of keyboard arrows however.
Since it did work with previous early access of jfx (up to 17-ea-9, actually), I did manage to pinpoint that change responsible: openjdk/jfx#454
The above PR changes the type of listener used internally by the built-in bi-directional bindings, from ChangeListeners<T>
to InvalidationListeners
.
This should be transparent to most if not all applications, but because Flowless relies on ReactFX, which interacts with bindings and events in a very intimate way, something was broken by that change.
I managed to workaround the issue in the VirtualizedScrollPane
by replacing the affect bi-directional bindings by a couple of mirrored ChangeListeners
.
I will submit a PR illustrating this workaround for your consideration.
Coming from FXMisc/RichTextFX#205 and relating to #9, provide more access to VirtualizedScrollPane's objects so that layoutChildren can be better overridden.
There's a few ways this could be done:
content
, hbar
, and vbar
protected. Then the developer can fully layout the children as they desire.VirtualizedScrollPane
(or more probably, screw up how the scroll bars get laid out), use a protected method to layout the content which developers can override:protected void layoutChildren() {
// normal layout code...
// instead of "content.resize(w, h)" have
layoutContent(w, h);
// rest of method's code.
}
protected void layoutContent(double width, double height) {
content.resize(width, height);
}
Coming from FXMisc/RichTextFX#107, add API that can "hide" multiple cells by rendering a placeholder cell and "showing" them again later on.
Coming from FXMisc/RichTextFX#205, allow the option of not showing the horizontal and/or the vertical scrollBars.
Tomas already said in the above issue:
Hard-wiring auto-scrollbars into
VirtualFlow
didn't feel quite right from the start. On the other hand, the internal architecture of VirtualFlow already very much separates the scrollbar concern:VirtualFlow
basically just adds scrollbars ontoVirtualFlowContent
VirtualFlow calculates a reasonable jump size when using the mouse wheel or scroll bar increment/decrement buttons. This leads to reasonable scrolling behaviour but with large content (e.g. a many page PDF) the scroll amount can be quite large, like 20 pixels or so, and this leads to a jerky feeling when scrolling.
Seeing as how ReactFX has the nice Val.animate() method it would probably be quite easy to fix this. I had a quick go at doing so without hacking Flowless itself but it seems the methods I'd need are all (package) private. Perhaps there's a simple trick to get this; I guess inserting a simple animated val with short duration between the desired scroll position and the actual position is sufficient.
If I'm not mistaken items laid out starting from top, it would be great if we can change to behavior to start stacking from the bottom this is useful for creating a widget for displaying chat messages.
This tends to be the default behavior of most scroll panes nowadays
I will no longer be maintaining this project.
See FXMisc/RichTextFX#768 for more info.
When I added hyperlinks and images to Jugen's StyledSegmentTextArea, I was hoping the hyperlinks would work.
The first and third hyperlinks in the demo behave correctly, but the second hyperlink in the demo is somehow corrupted:
I first reported this issue here: FXMisc/RichTextFX#807
In there, Jugen said:
So it turns out that the culprit for this behavior is the image, in particular the vertical size of the image.
When the image is very tall, specifically it seems when the image is taller than the view-port, AND the hyperlink is in the same paragraph as the image then the mouse over somehow causes a re-layout to occur which is what you are seeing.
If you replace the image with one that is smaller vertically or put the hyperlink in a different paragraph then the re-layout doesn't occur anymore and the hyperlink behaves as expected.
The origins of this misbehavior is I think inside VirtualFlow in Flowless ?
The source files are here: https://github.com/Kim4444/RichTextFXIssue
Demo Notes: First I downloaded the files at https://github.com/Jugen/StyledSegmentTextArea. I tweaked the source slightly, then added HyperlinkSegment and ImageSegment. I also use ReadOnlyStyledDocumentBuilder so I could build the doc one paragraph at a time. I tried to make as few changes to Jugen's demo as I could. There are more details in the demo file CodeChanges.txt. I am uncertain if it is a bug in my code, or in RichTextFX/supporting classes. Thank you so much for any help!
RichTextFX Version: 0.9.3
Flowless 0.6.1
ReactFX 2.0-M5
UndoFX 2.1.0
WellBehavedFX 0.3.3
Operating System: Windows 10
Java version: 8u181
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.