Comments (8)
@Jugen Hello, sorry for the late response.
I can confirm that now the smooth scrolling works, however there still seems to be some issues:
- The DRAG_DETECTED handler is not working. So if the view is scrolling, and you click on the scrollbar it doesn't stop scrolling.
- There's some sort of "jump" when the scrolling starts which should not happen
2021-04-11.12-41-49_Trim.mp4
EDIT: I was wondering if I could fix it by changing the type of handler... Yep, everything works now
The code now is:
public static void setSmoothScrolling(VirtualFlow<?, ?> flow, Var<Double> scrollDirection) {
final double[] frictions = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
final double[] pushes = {1};
final double[] derivatives = new double[frictions.length];
Timeline timeline = new Timeline();
final EventHandler<MouseEvent> dragHandler = event -> {
System.out.println("STOP!");
timeline.stop();
};
final EventHandler<ScrollEvent> scrollHandler = event -> {
if (event.getEventType() == ScrollEvent.SCROLL) {
System.out.println("Smooth Scrolling");
int direction = event.getDeltaY() > 0 ? -1 : 1;
for (int i = 0; i < pushes.length; i++) {
derivatives[i] += direction * pushes[i];
}
if (timeline.getStatus() == Animation.Status.STOPPED) {
timeline.play();
}
event.consume();
}
};
if (flow.getParent() != null) {
flow.getParent().addEventFilter(MouseEvent.DRAG_DETECTED, dragHandler);
}
flow.parentProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.removeEventFilter(MouseEvent.DRAG_DETECTED, dragHandler);
}
if (newValue != null) {
newValue.addEventFilter(MouseEvent.DRAG_DETECTED, dragHandler);
}
});
flow.addEventFilter(MouseEvent.DRAG_DETECTED, dragHandler);
flow.addEventFilter(ScrollEvent.ANY, scrollHandler);
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), (event) -> {
for (int i = 0; i < derivatives.length; i++) {
derivatives[i] *= frictions[i];
}
for (int i = 1; i < derivatives.length; i++) {
derivatives[i] += derivatives[i - 1];
}
double dy = derivatives[derivatives.length - 1];
scrollDirection.setValue(scrollDirection.getValue() + dy);
if (Math.abs(dy) < 0.001) {
timeline.stop();
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
}
I changed the handlers to filters and everything seems to work good.
The only thing I'm going to change is making this method private and create a new public method that accepts a list view as a parameter because the virtual flow is in the skin class.
This method will probably look like this:
public static void setSmoothScrolling(AbstractMFXFlowlessListView<?, ?, ?> listView) {
if (listView.getScene() != null) {
VirtualFlow<?, ?> flow = (VirtualFlow<?, ?>) listView.lookup(".virtual-flow");
setSmoothScrolling(flow, flow.estimatedScrollYProperty());
} else {
listView.skinProperty().addListener(new ChangeListener<>() {
@Override
public void changed(ObservableValue<? extends Skin<?>> observable, Skin<?> oldValue, Skin<?> newValue) {
if (newValue != null) {
VirtualFlow<?, ?> flow = (VirtualFlow<?, ?>) listView.lookup(".virtual-flow");
setSmoothScrolling(flow, flow.estimatedScrollYProperty());
listView.skinProperty().removeListener(this);
}
}
});
}
}
from flowless.
Now that I've documented the code in #33, I don't know if your Val.animate
idea is feasible. In my understanding, the viewport lays out its content first, and then checks whether there is unused space. If there is, it may relayout that content a second time. Since the scroll values are estimates calculated from the averages of the displayed cells' nodes, I'd guess that these jerky value changes are a result of these averages being recalculated when the displayed nodes are repositioned or nodes are added/removed.
Instead, I'd suggest the code does something similar to the RichTexfFX approach of suspending a value when the view is being updated and resuming it when its finished.
from flowless.
Can you give a standalone test that demonstrates this jerkiness? I'm assuming the solution would be to wrap the layoutChildren
and any show()
-related methods in a Suspendables.combine(estimatedScrollX, estimatedScrollY).suspendWhile()
block
from flowless.
Still no support for smooth scrolling?
I tried to make it work with the same strategy I use for JavaFX's scroll panes, see here: MaterialFX ScrollPane, but it won't work
from flowless.
What happens when you try your method on a VirtualFlow ?
Can you provide a demo with both a ScrollPane and a VirtualFlow with the same content that show/compares the behavior you want ?
from flowless.
2021-03-21_14-07-36.mp4
As you can see the handler in never executed with the VirtualizedScrollPane.
I had to modify the code a bit though because VirtualizedScrollPane doesn't have vvalue and hvalue properties.
I had to use reflection and change the smooth scroll code to:
private static void customScrolling(MFXVirtualizedScrollPane<?> scrollPane, DoubleProperty scrollDirection, Function<Bounds, Double> sizeFunc) {
final double[] frictions = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
final double[] pushes = {1};
final double[] derivatives = new double[frictions.length];
Timeline timeline = new Timeline();
final EventHandler<MouseEvent> dragHandler = event -> timeline.stop();
final EventHandler<ScrollEvent> scrollHandler = event -> {
System.out.println("Smooth Scrolling");
if (event.getEventType() == ScrollEvent.SCROLL) {
int direction = event.getDeltaY() > 0 ? -1 : 1;
for (int i = 0; i < pushes.length; i++) {
derivatives[i] += direction * pushes[i];
}
if (timeline.getStatus() == Animation.Status.STOPPED) {
timeline.play();
}
event.consume();
}
};
if (scrollPane.getContent().getParent() != null) {
scrollPane.getContent().getParent().addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
scrollPane.getContent().getParent().addEventHandler(ScrollEvent.ANY, scrollHandler);
}
scrollPane.getContent().parentProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.removeEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
oldValue.removeEventHandler(ScrollEvent.ANY, scrollHandler);
}
if (newValue != null) {
newValue.addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
newValue.addEventHandler(ScrollEvent.ANY, scrollHandler);
}
});
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), (event) -> {
for (int i = 0; i < derivatives.length; i++) {
derivatives[i] *= frictions[i];
}
for (int i = 1; i < derivatives.length; i++) {
derivatives[i] += derivatives[i - 1];
}
double dy = derivatives[derivatives.length - 1];
double size = sizeFunc.apply(scrollPane.getContent().getLayoutBounds());
scrollDirection.set(Math.min(Math.max(scrollDirection.get() + dy / size, 0), 1));
if (Math.abs(dy) < 0.001) {
timeline.stop();
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
}
public static void smoothVScrolling(MFXVirtualizedScrollPane<?> scrollPane) {
try {
Field vvalue = VirtualizedScrollPane.class.getDeclaredField("vBarValue");
vvalue.setAccessible(true);
Var<Double> obj = (Var<Double>) vvalue.get(scrollPane);
DoubleProperty doubleProperty = new SimpleDoubleProperty();
doubleProperty.bind(obj);
customScrolling(scrollPane, doubleProperty, Bounds::getHeight);
} catch (Exception ex) {
ex.printStackTrace();
}
}
from flowless.
Here you go, try this slightly modified version of the code you provided:
// Changed method signature to use VirtualFlow and its estimated scroll property directly
private static void customScrolling( VirtualFlow<?,?> flowPane, Var<Double> scrollDirection ) {
final double[] frictions = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
final double[] pushes = {1};
final double[] derivatives = new double[frictions.length];
Timeline timeline = new Timeline();
final EventHandler<MouseEvent> dragHandler = event -> timeline.stop();
final EventHandler<ScrollEvent> scrollHandler = event -> {
if (event.getEventType() == ScrollEvent.SCROLL) {
System.out.println("Smooth Scrolling");
int direction = event.getDeltaY() > 0 ? -1 : 1;
for (int i = 0; i < pushes.length; i++) {
derivatives[i] += direction * pushes[i];
}
if (timeline.getStatus() == Animation.Status.STOPPED) {
timeline.play();
}
event.consume();
}
};
// Changed this to get ScrollEvents to work, "Smooth Scrolling" prints now :-)
flowPane.addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
flowPane.addEventHandler(ScrollEvent.ANY, scrollHandler);
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), (event) -> {
for (int i = 0; i < derivatives.length; i++) {
derivatives[i] *= frictions[i];
}
for (int i = 1; i < derivatives.length; i++) {
derivatives[i] += derivatives[i - 1];
}
double dy = derivatives[derivatives.length - 1];
// Changed this as values aren't between 0 & 1
scrollDirection.setValue(scrollDirection.getValue() + dy);
if (Math.abs(dy) < 0.001) {
timeline.stop();
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
}
from flowless.
Sorry the method signature can be reduced to:
private static void customScrolling( VirtualFlow<?,?> flowPane, Var<Double> scrollDirection )
which then is invoked with:
customScrolling( virtualFlow, virtualFlow.estimatedScrollYProperty() );
from flowless.
Related Issues (20)
- Modular application cannot find flowless HOT 1
- Error fetching URL: https://github.com/FXMisc/Flowless/#flowless/apidocs/
- VirtualFlow.java does not compile in OpenJDK 17 HOT 3
- Scrollbar thumb is too small HOT 1
- VirtualFlow GridPane HOT 10
- Changing styles of rows HOT 3
- Need new maintainer HOT 6
- Why happens to performance when Nodes are used for items in the VirtualFlow/createVertical? HOT 1
- Custom Editor, Multiple Custom Objects, adding HyperlinkSegment and ImageSegment
- Expandable cell problem HOT 2
- VirtualFlow doesn't resize cells properly when layout bounds changes depending on the Orientation. HOT 1
- Keyboard paging computation error while paging up
- VirtualizedScrollPane causes high CPU and GPU on idle with 125% screen scaling HOT 6
- Can't Get Clicked Item in List HOT 27
- Adding items too fast causes scroll when item list is empty HOT 1
- Introduce negative checks, just before each access. HOT 8
- Cell positioning
- org.fxmisc.flowless.CellListManager#getCellIfPresent HOT 1
- JavaFX 16 HOT 2
- JavaFX 17 breaks scrollbar behaviours in VirtualizedScrollPane HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flowless.