Skip to content

Commit

Permalink
fix estimated scroll bidirectional binding (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jugen authored Sep 3, 2020
1 parent 1314a20 commit 8b82d59
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 1 deletion.
95 changes: 95 additions & 0 deletions src/main/java/org/fxmisc/flowless/StableBidirectionalVar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.fxmisc.flowless;

import java.util.function.Consumer;

import org.reactfx.Subscription;
import org.reactfx.value.ProxyVal;
import org.reactfx.value.Val;
import org.reactfx.value.Var;

import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
/**
* This class overrides the <code>Var.bindBidirectional</code> method implementing a mechanism to prevent looping.
* <br>By default <code>bindBidirectional</code> delegates to <code>Bindings.bindBidirectional</code>
* which isn't always stable for the Val -> Var paradigm, sometimes producing a continuous feedback loop.<br>
*/
class StableBidirectionalVar<T> extends ProxyVal<T, T> implements Var<T>
{
private final Consumer<T> setval;
private Subscription binding = null;
private ChangeListener<T> left, right;
private T last = null;

StableBidirectionalVar( Val<T> underlying, Consumer<T> setter )
{
super( underlying );
setval = setter;
}

@Override
public T getValue()
{
return getUnderlyingObservable().getValue();
}

@Override
protected Consumer<? super T> adaptObserver( Consumer<? super T> observer )
{
return observer; // no adaptation needed
}

@Override
public void bind( ObservableValue<? extends T> observable )
{
unbind();
binding = Val.observeChanges( observable, (ob,ov,nv) -> setValue( nv ) );
setValue( observable.getValue() );
}

@Override
public boolean isBound()
{
return binding != null;
}

@Override
public void unbind()
{
if( binding != null ) binding.unsubscribe();
binding = null;
}

@Override
public void setValue( T newVal )
{
setval.accept( newVal );
}

@Override
public void unbindBidirectional( Property<T> prop )
{
if ( right != null ) prop.removeListener( right );
if ( left != null ) removeListener( left );
left = null; right = null;
}

@Override
public void bindBidirectional( Property<T> prop )
{
unbindBidirectional( prop );
prop.addListener( right = (ob,ov,nv) -> adjustOther( this, nv ) );
addListener( left = (ob,ov,nv) -> adjustOther( prop, nv ) );
}

private void adjustOther( Property<T> other, T nv )
{
if ( ! nv.equals( last ) )
{
Platform.runLater( () -> other.setValue( nv ) );
last = nv;
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/fxmisc/flowless/VirtualFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private VirtualFlow(
layoutBoundsProperty(),
b -> new Rectangle(b.getWidth(), b.getHeight())));

lengthOffsetEstimate = sizeTracker.lengthOffsetEstimateProperty().asVar(this::setLengthOffset);
lengthOffsetEstimate = new StableBidirectionalVar<>( sizeTracker.lengthOffsetEstimateProperty(), this::setLengthOffset );

// scroll content by mouse scroll
this.addEventHandler(ScrollEvent.ANY, se -> {
Expand Down

0 comments on commit 8b82d59

Please sign in to comment.