All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netopyr.reduxfx.vscenegraph.ReduxFXView Maven / Gradle / Ivy

package com.netopyr.reduxfx.vscenegraph;

import com.netopyr.reduxfx.vscenegraph.impl.differ.Differ;
import com.netopyr.reduxfx.vscenegraph.impl.differ.patches.Patch;
import com.netopyr.reduxfx.vscenegraph.impl.patcher.Patcher;
import com.netopyr.reduxfx.vscenegraph.property.VProperty;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import io.vavr.collection.Map;
import io.vavr.collection.Vector;
import io.vavr.control.Option;
import org.reactivestreams.Processor;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;

import java.util.function.Function;

import static com.netopyr.reduxfx.vscenegraph.VScenegraphFactory.Scene;
import static com.netopyr.reduxfx.vscenegraph.VScenegraphFactory.Stage;
import static com.netopyr.reduxfx.vscenegraph.VScenegraphFactory.Stages;

@SuppressWarnings({"unused", "WeakerAccess"})
public class ReduxFXView {

    public static  ReduxFXView createStages(
            Function view,
            Stage primaryStage) {
        final Stages stages = new Stages(primaryStage);
        final Option initialVNode = Option.of(Stages().children(Stage()));
        return new ReduxFXView<>(initialVNode, view, stages);
    }

    public static  ReduxFXView createStage(
            Function view,
            Stage primaryStage) {
        return new ReduxFXView<>(Option.none(), view, primaryStage);
    }

    public static  ReduxFXView create(
            Function view,
            Stage primaryStage) {
        final Function stageView = state -> Stage().scene(Scene().root(view.apply(state)));
        return new ReduxFXView<>(Option.none(), stageView, primaryStage);
    }

    public static  ReduxFXView create(
            Function view,
            Group group) {
        return new ReduxFXView<>(Option.none(), view, group);
    }

    public static  ReduxFXView create(
            Function view,
            Pane pane) {
        return new ReduxFXView<>(Option.none(), view, pane);
    }

    private final Option initialVNode;
    private final Function view;
    private final Object javaFXRoot;

    private ReduxFXView(
            Option initialVNode,
            Function view,
            Object javaFXRoot) {
        this.initialVNode = initialVNode;
        this.view = view;
        this.javaFXRoot = javaFXRoot;
    }

    public void connect(Processor store) {
        connect(store, store);
    }

    public void connect(Publisher statePublisher, Subscriber actionSubscriber) {
        final Flowable> vScenegraphStream =
                Flowable.fromPublisher(statePublisher)
                        .map(view::apply)
                        .map(Option::of)
                        .startWith(initialVNode);

        final Flowable>> patchesStream =
                vScenegraphStream.zipWith(vScenegraphStream.skip(1), Differ::diff);

        final Flowable paramsStream = vScenegraphStream.zipWith(patchesStream, PatchParams::new);

        Flowable.create(
                emitter ->
                        paramsStream.forEach(
                                params -> {
                                    if (Platform.isFxApplicationThread()) {
                                        Patcher.patch(emitter::onNext, javaFXRoot, params.vRoot, params.patches);
                                    } else {
                                        Platform.runLater(() -> Patcher.patch(emitter::onNext, javaFXRoot, params.vRoot, params.patches));
                                    }
                                }
                        ),
                BackpressureStrategy.BUFFER
        ).subscribe(actionSubscriber);
    }

    private static class PatchParams {
        private final Option vRoot;
        private final Map> patches;

        private PatchParams(Option vRoot, Map> patches) {
            this.vRoot = vRoot;
            this.patches = patches;
        }
    }

}