Add MultiStepBinding&ReadWriteComposedProperty

This commit is contained in:
yushijinhun 2018-10-01 15:35:39 +08:00
parent 5e5db332d0
commit a01e15d841
No known key found for this signature in database
GPG Key ID: 5BC167F73EA558E4
3 changed files with 192 additions and 12 deletions

View File

@ -0,0 +1,122 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util.javafx;
import static java.util.Objects.requireNonNull;
import java.util.function.Function;
import java.util.function.Supplier;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ObservableValue;
/**
* @author yushijinhun
*/
public abstract class MultiStepBinding<T, U> extends ObjectBinding<U> {
public static <T> MultiStepBinding<?, T> of(ObservableValue<T> property) {
return new SimpleBinding<>(property);
}
protected final ObservableValue<T> predecessor;
public MultiStepBinding(ObservableValue<T> predecessor) {
this.predecessor = requireNonNull(predecessor);
bind(predecessor);
}
public <V> MultiStepBinding<?, V> map(Function<U, V> mapper) {
return new MappedBinding<>(this, mapper);
}
public <V> MultiStepBinding<?, V> flatMap(Function<U, ? extends ObservableValue<V>> mapper) {
return flatMap(mapper, null);
}
public <V> MultiStepBinding<?, V> flatMap(Function<U, ? extends ObservableValue<V>> mapper, Supplier<V> nullAlternative) {
return new FlatMappedBinding<>(map(mapper), nullAlternative);
}
private static class SimpleBinding<T> extends MultiStepBinding<T, T> {
public SimpleBinding(ObservableValue<T> predecessor) {
super(predecessor);
}
@Override
protected T computeValue() {
return predecessor.getValue();
}
@Override
public <V> MultiStepBinding<?, V> map(Function<T, V> mapper) {
return new MappedBinding<>(predecessor, mapper);
}
}
private static class MappedBinding<T, U> extends MultiStepBinding<T, U> {
private final Function<T, U> mapper;
public MappedBinding(ObservableValue<T> predecessor, Function<T, U> mapper) {
super(predecessor);
this.mapper = mapper;
}
@Override
protected U computeValue() {
return mapper.apply(predecessor.getValue());
}
}
private static class FlatMappedBinding<T extends ObservableValue<U>, U> extends MultiStepBinding<T, U> {
private final Supplier<U> nullAlternative;
private T lastObservable = null;
public FlatMappedBinding(ObservableValue<T> predecessor, Supplier<U> nullAlternative) {
super(predecessor);
this.nullAlternative = nullAlternative;
}
@Override
protected U computeValue() {
T currentObservable = predecessor.getValue();
if (currentObservable != lastObservable) {
if (lastObservable != null) {
unbind(lastObservable);
}
if (currentObservable != null) {
bind(currentObservable);
}
lastObservable = currentObservable;
}
if (currentObservable == null) {
if (nullAlternative == null) {
throw new NullPointerException();
} else {
return nullAlternative.get();
}
} else {
return currentObservable.getValue();
}
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util.javafx;
import java.util.function.Consumer;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
/**
* @author yushijinhun
*/
public class ReadWriteComposedProperty<T> extends SimpleObjectProperty<T> {
@SuppressWarnings("unused")
private final ObservableValue<T> readSource;
private final Consumer<T> writeTarget;
private ChangeListener<T> listener;
public ReadWriteComposedProperty(ObservableValue<T> readSource, Consumer<T> writeTarget) {
this.readSource = readSource;
this.writeTarget = writeTarget;
this.listener = (observable, oldValue, newValue) -> set(newValue);
readSource.addListener(new WeakChangeListener<>(listener));
set(readSource.getValue());
}
@Override
protected void invalidated() {
writeTarget.accept(get());
}
}

View File

@ -17,26 +17,33 @@
*/
package org.jackhuang.hmcl.util.javafx;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.ComboBox;
import javafx.scene.control.SelectionModel;
public class SelectionModelSelectedItemProperty<T> extends SimpleObjectProperty<T> {
/**
* @author yushijinhun
*/
public final class SelectionModelSelectedItemProperty<T> extends SimpleObjectProperty<T> {
public static <T> SelectionModelSelectedItemProperty<T> selectedItemPropertyFor(ComboBox<T> comboBox) {
return new SelectionModelSelectedItemProperty<>(comboBox.getSelectionModel());
private static final String NODE_PROPERTY = SelectionModelSelectedItemProperty.class.getName() + ".instance";
@SuppressWarnings("unchecked")
public static <T> ObjectProperty<T> selectedItemPropertyFor(ComboBox<T> comboBox) {
return (ObjectProperty<T>) comboBox.getProperties().computeIfAbsent(
NODE_PROPERTY,
any -> createSelectedItemProperty(comboBox.selectionModelProperty()));
}
private SelectionModel<T> model;
public SelectionModelSelectedItemProperty(SelectionModel<T> model) {
this.model = model;
model.selectedItemProperty().addListener((observable, oldValue, newValue) -> set(newValue));
set(model.getSelectedItem());
private static <T> ObjectProperty<T> createSelectedItemProperty(Property<? extends SelectionModel<T>> modelProperty) {
return new ReadWriteComposedProperty<>(
MultiStepBinding.of(modelProperty)
.flatMap(SelectionModel::selectedItemProperty),
newValue -> modelProperty.getValue().select(newValue));
}
@Override
protected void invalidated() {
model.select(get());
private SelectionModelSelectedItemProperty() {
}
}