Skip to content

Commit

Permalink
added pannable pane base
Browse files Browse the repository at this point in the history
  • Loading branch information
Shadi Shaheen committed Apr 30, 2020
1 parent 3aa5fd7 commit 23ae68c
Show file tree
Hide file tree
Showing 9 changed files with 770 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.bindings;

import com.jfoenix.bindings.base.IBiBinder;
import com.jfoenix.bindings.base.IPropertyConverter;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.util.Callback;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.function.Consumer;

/**
* Custom bidirectional binder, used for bidirectional binding between properties of different types
* or with different accessibility methods (e.g {@link ReadOnlyProperty} with setter method)
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public class CustomBidirectionalBinding<A, B> implements IBiBinder {

private final WeakReference<ReadOnlyProperty<A>> propertyRef1;
private final WeakReference<ReadOnlyProperty<B>> propertyRef2;
private final Consumer<A> propertyRef1Setter;
private final Consumer<B> propertyRef2Setter;
private HashMap<ReadOnlyProperty<?>, ChangeListener> listeners = new HashMap<>();
private IPropertyConverter<A, B> converter;

public CustomBidirectionalBinding(Property<A> a, Property<B> b, IPropertyConverter<A, B> converter) {
this(a, value -> a.setValue(value),
b, value -> b.setValue(value),
converter);
}

public CustomBidirectionalBinding(ReadOnlyProperty<A> a, Consumer<A> propertyRef1Setter,
ReadOnlyProperty<B> b, Consumer<B> propertyRef2Setter,
IPropertyConverter<A, B> converter) {
this.propertyRef1 = new WeakReference<>(a);
this.propertyRef2 = new WeakReference<>(b);
this.propertyRef1Setter = propertyRef1Setter;
this.propertyRef2Setter = propertyRef2Setter;
this.converter = converter;
}

@SuppressWarnings("unchecked")
@Override
public void unbindBi() {
listeners.entrySet().forEach(entry -> entry.getKey().removeListener(entry.getValue()));
}

@Override
public void bindBi() {
addFlaggedChangeListener(propertyRef1.get(), propertyRef1Setter, propertyRef2.get(), propertyRef2Setter, param -> converter.to(param));
addFlaggedChangeListener(propertyRef2.get(), propertyRef2Setter, propertyRef1.get(), propertyRef1Setter, param -> converter.from(param));
propertyRef2Setter.accept(converter.to(propertyRef1.get().getValue()));
}

private <a, b> void addFlaggedChangeListener(ReadOnlyProperty<a> a, Consumer<a> aConsumer,
ReadOnlyProperty<b> b, Consumer<b> bConsumer,
Callback<a, b> updateB) {
ChangeListener<a> listener = new ChangeListener<a>() {
private boolean alreadyCalled = false;

@Override
public void changed(ObservableValue<? extends a> observable, a oldValue, a newValue) {
if (alreadyCalled) {
return;
}
try {
alreadyCalled = true;
bConsumer.accept(updateB.call(newValue));
} finally {
alreadyCalled = false;
}
}
};
listeners.put(a, listener);
a.addListener(listener);
}
}
33 changes: 33 additions & 0 deletions jfoenix/src/main/java/com/jfoenix/bindings/base/IBiBinder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.bindings.base;

/**
* Custom bidirectional binder interface
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public interface IBiBinder {
void unbindBi();

void bindBi();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.bindings.base;

/**
* Converts between two properties (e.g String->Integer and vice versa)
* used when binding properties of different types
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public interface IPropertyConverter<A, B> {
B to(A a);

A from(B b);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.controls.pannable;

import com.jfoenix.controls.pannable.base.IPannablePane;
import com.jfoenix.controls.pannable.gestures.PanningGestures;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.scene.transform.Scale;

/**
* Simple pannable pane implementation
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public class PannablePane extends Pane implements IPannablePane {

private Scale scale = new Scale(1, 1, 0, 0);

public PannablePane() {
getTransforms().add(scale);
scale.yProperty().bind(scale.xProperty());
}

@Override
public double getScale() {
return scale.getX();
}

@Override
public void setScale(double scale) {
this.scale.setX(scale);
}

@Override
public DoubleProperty scaleProperty() {
return scale.xProperty();
}

public static PannablePane wrap(Node... children) {
PannablePane canvas = new PannablePane();
PanningGestures.attachViewPortGestures(canvas);
canvas.getChildren().setAll(children);
return canvas;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.controls.pannable;

import com.jfoenix.bindings.CustomBidirectionalBinding;
import com.jfoenix.bindings.base.IPropertyConverter;
import com.jfoenix.controls.pannable.base.IPannablePane;
import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.Property;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;

import java.util.function.Function;

/**
* Used to add scroll functionality to {@link PannablePane}
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public class PannableScrollPane extends Pane {

private ScrollBar vBar = new ScrollBar();
private ScrollBar hBar = new ScrollBar();

private void init() {
getStyleClass().add("pannable-scroll-pane");
getChildren().addAll(vBar, hBar);
vBar.setManaged(false);
vBar.setOrientation(Orientation.VERTICAL);
hBar.setManaged(false);
hBar.setOrientation(Orientation.HORIZONTAL);
}

private static final double SCROLL_PAD = 20;

public <P extends Region & IPannablePane> PannableScrollPane(P pane) {
super(pane);
init();
bindScrollBar(vBar, pane, pane.translateYProperty(), (p) -> p.heightProperty());
bindScrollBar(hBar, pane, pane.translateXProperty(), (p) -> p.widthProperty());
}

<P extends Region & IPannablePane> void bindScrollBar(ScrollBar bar, P pane, Property<Number> trans, Function<Region, DoubleExpression> propFun) {
CustomBidirectionalBinding<Number, Number> binding = new CustomBidirectionalBinding<>(
trans
, bar.valueProperty()
, new IPropertyConverter<Number, Number>() {
@Override
public Number to(Number number) {
return number.doubleValue() * -1;
}

@Override
public Number from(Number number) {
return number.doubleValue() * -1;
}
});
binding.bindBi();
bar.minProperty().bind(pane.scaleProperty().negate());
bar.maxProperty().bind(propFun.apply(pane).add(SCROLL_PAD).multiply(pane.scaleProperty()).subtract(propFun.apply(this)));
bar.visibleProperty().bind(bar.maxProperty().greaterThan(0));
}

public PannableScrollPane() {
init();
}

@Override
protected void layoutChildren() {
super.layoutChildren();
double w = getWidth();
double h = getHeight();
Insets insets = getInsets();
final double prefWidth = vBar.prefWidth(-1);
vBar.resizeRelocate(w - prefWidth - insets.getRight(), insets.getTop(), prefWidth, h - insets.getTop() - insets.getBottom());

final double prefHeight = hBar.prefHeight(-1);
hBar.resizeRelocate(insets.getLeft(), h - prefHeight - insets.getBottom(), w - insets.getLeft() - insets.getRight(), prefHeight);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package com.jfoenix.controls.pannable.base;

/**
* Observable object is an interface for listening to object events
*
* @author Shadi Shaheen
* @version 1.0
* @since 2020-04-30
*/
public interface IObservableObject<L> {
boolean addListener(L listener);

boolean removeListener(L listener);
}
Loading

0 comments on commit 23ae68c

Please sign in to comment.