-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Workaround to correct trackpad scrolling gestures under macOS #64
Conversation
…st). CellListManager takes responsibility for pushing ScrollEvents to the owning VirtualFlow, bypassing normal event bubbling that fails when Cell Nodes still receive ScrollEvents after being cropped by their Parent Navigator.
Aye, it works. However, I don't know whether this is the best way to fix it (that's why I didn't merge it). Have you read through my comment in #56? |
No, I didn't see that issue, just the other one I mentioned. To be honest, child versus container handling sounds more like a philosophical question at this point, while there is a real problem of Flowless being broken in OSX at this point, and this really fixes it. Also, internal changes can always be remade later if needed. |
Can't argue with that...
I think the larger issue of why I didn't like this approach was a (perhaps very unreasonable) fear that it might create a memory leak because I see an |
It seems an event handler is only added for non reusable cells, with the hope that they will get garbage collected, but unless the link between them is made weak, I guess they will never be collected. I haven't tested this behavior yet, so cannot confirm if that assumption is right. |
That was my assumption yes, as nothing holds a reference to the event handler except the cell itself, so should be garbage-collected when the cell is. ISTR there's something in the javafx javadocs to that effect. Elsewhere I've also not really seen explicit removal of event handlers of a node before its own destruction. Bearing in mind the event handler itself is just a methodref to a method fulfilling the EventHandler functional interface. There's no bigger object involved. What exactly is there more to be garbage collected? That value is just set on the scroll event properties on the node. The node dies, so should its property values. :-) |
Rather than just assume, could we profile this PR and the current master branch to insure no memory leak is being added in this PR? Once the assumption is proven, there's no reason not to merge this PR and a very good reason to do so. As @Arcnor said, the internal handling can always be changed later if we should so desire. |
@Arcnor @StrangeNoises One other thought... If this is being submitted to specifically fix the corresponding issue in RichTextFX, we could always fix it there, too, using the same event-forwarding approach that @StrangeNoises created. Such a thing could be done in the That would fix the problem and I'd merge that immediately, giving us time to figure out how to best proceed in fixing the code here since I'm guessing profiling this PR would take more time and energy than submitting a PR such as that. |
Using the following code (not sure whether I implemented the reusable cell correctly), it seems like there is a very small memory leak: import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class ProfileTest extends Application {
private class R extends Rectangle {
R() {
super();
setWidth(20);
setHeight(20);
setFill(Color.BLACK);
}
}
public static void main(String[] args) {
launch(args);
}
private final SimpleBooleanProperty showTop = new SimpleBooleanProperty(false);
private boolean shouldShowTop() { return showTop.get(); }
private void invertView() { showTop.set(!showTop.get()); }
@Override
public void start(Stage primaryStage) {
boolean useNonReusableCells = false;
System.out.println("Using reusable cells? " + !useNonReusableCells);
System.gc();
System.out.println("Starting: " + memoryStatus());
List<Integer> l = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
l.add(i);
}
ObservableList<Integer> list = FXCollections.observableList(l);
System.gc();
System.out.println("Finished list creation: " + memoryStatus());
VirtualFlow<Integer, Cell<Integer, R>> flow = VirtualFlow.createHorizontal(
list,
useNonReusableCells ? useNonReusableCells() : useReusableCells()
);
primaryStage.setScene(new Scene(flow, 1960, 100));
primaryStage.show();
System.gc();
System.out.println("Finished setting up stage: " + memoryStatus());
for (int i = 0; i < 10_000; i++) {
if (shouldShowTop()) {
flow.showAsFirst(0);
} else {
flow.showAsLast(list.size() - 1);
}
invertView();
}
System.gc();
System.out.println("Final status: " + memoryStatus());
Platform.exit();
}
public Function<Integer, Cell<Integer, R>> useReusableCells() {
return i -> new Cell<Integer, R>() {
R h = new R();
@Override
public R getNode() {
return h;
}
@Override
public boolean isReusable() {
return true;
}
@Override
public void updateItem(Integer item) {
// do nothing
}
};
}
public Function<Integer, Cell<Integer, R>> useNonReusableCells() {
return i -> Cell.wrapNode(new R());
}
public String memoryStatus() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
NumberFormat format = NumberFormat.getInstance();
StringBuilder sb = new StringBuilder();
sb.append("Used ");
sb.append(format.format((runtime.totalMemory() - runtime.freeMemory())));
sb.append(" B from ");
sb.append(format.format((freeMemory + (maxMemory - allocatedMemory))));
return sb.toString();
}
} which outputs (formatted for clarity):
|
Hi, sorry for the delay, I haven't been working on the project that uses this. To answer your question above: No, I don't want to use RichText but Flowless directly (RichText is not flexible enough for my needs). I've also run your test, but with 1M rows instead of just 100, and 1M iterations on the change loop, also ran the test multiple times. Here are my results, but I don't see any leak here? Without change:
With change:
|
Ok, thanks for running the test. I think that proves @StrangeNoises assumption true. |
Awesome, thanks. Unfortunately, this doesn't seem to fix the issue completely. I'm getting the same problem randomly, but I can't figure out why (haven't debugged it, though) Anyway, this is better than having it not work at any point. |
Hmm.... Is this an issue in JavaFX for OSX then? (I'm still wondering why this doesn't occur on the other two major platforms) |
After I merged this, I received a report about the build failing: |
Well, I haven't tried any other platform, not sure if it's Mac specific. However, there is no "smooth" scrolling (as in, scrolling with "weight", whatever that's called) in Windows at least? So you won't see this problem happening, I think. Also, I think I saw the explanation of why this was the way it is on OSX in one of the liked issues, but I might be mistaken. |
StrangeNoises had a comment in the issue in RichTextFX that probably mentioned something in that regard. It's been a while since I read through it. |
About the build, that's very strange. As far as I know, the PR was built before being merged as well (because Travis won't let you merge when the build is red, unless I'm mistaken about that), and it was ok? |
Yeah, it was: https://travis-ci.org/FXMisc/Flowless/builds/353589200 |
Yeah, it was. That's why I'm a bit confused... The test might need to be rewritten slightly. I'll have to look into it more next week. |
Latest build #66 worked fine. Weird. |
Flaky test, then, probably. I still need to go through this and check why is not working in some situations, but I think it's good that we have this in place anyway. Thanks again! |
Just a thought, but would there be any reason to change the internal way of adding/setting the scroll-event-forwarding event handlers to this way? Hope you get it worked out! |
I haven't tried your change, but it looks similar to what there was before. I'm guessing now you're adding the event handler even for reusable cells, but you're guaranteed to add them only when the cell is added to the stage? (so you do it only once) Maybe @StrangeNoises can comment a bit more on it. |
I didn't create this fix, but @StrangeNoises. It was never applied for some reason, but it should fix FXMisc/RichTextFX/issues/265.
I don't know if this is the "correct" way of fixing it or not, but it works as expected on my OSX machine.