org.eclipse.fx.ui.controls.styledtext.internal.VerticalLineFlow.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.fx.ui.controls.styledtext.internal.VerticalLineFlow.java

Source

/*******************************************************************************
 * Copyright (c) 2016 BestSolution.at and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Tom Schindl<tom.schindl@bestsolution.at> - initial API and implementation
 *******************************************************************************/
package org.eclipse.fx.ui.controls.styledtext.internal;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

import javafx.beans.InvalidationListener;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;

@SuppressWarnings("javadoc")
public class VerticalLineFlow<M, N> extends NodeCachePane {

    private Predicate<Set<N>> needsPresentation;

    private BiConsumer<Node, Set<N>> nodePopulator;
    private Function<Integer, Set<N>> converter;
    protected Map<Integer, Double> yOffsetData = new HashMap<>();

    private DoubleProperty lineHeigth = new SimpleDoubleProperty(this, "lineHeight", 16.0); //$NON-NLS-1$

    public DoubleProperty lineHeightProperty() {
        return this.lineHeigth;
    }

    public double getLineHeight() {
        return this.lineHeigth.get();
    }

    public void setLineHeight(double lineHeight) {
        this.lineHeigth.set(lineHeight);
    }

    //   private ListProperty<M> model = new SimpleListProperty<>(this, "model", FXCollections.observableArrayList());
    //   public ListProperty<M> getModel() {
    //      return this.model;
    //   }

    private IntegerProperty numberOfLines = new SimpleIntegerProperty(this, "numberOfLines", 0); //$NON-NLS-1$

    public IntegerProperty numberOfLinesProperty() {
        return this.numberOfLines;
    }

    private ObjectProperty<Range<Integer>> visibleLines = new SimpleObjectProperty<>(this, "visibleLines", //$NON-NLS-1$
            Range.all());

    public ObjectProperty<Range<Integer>> visibleLinesProperty() {
        return this.visibleLines;
    }

    public RangeSet<Integer> getVisibleLines() {
        RangeSet<Integer> visibleLines = TreeRangeSet.create();
        visibleLines.add(this.visibleLines.get());
        return visibleLines
                .subRangeSet(Range.closedOpen(Integer.valueOf(0), Integer.valueOf(this.numberOfLines.get())));
    }

    public void setVisibleLines(Range<Integer> visibleLines) {
        this.visibleLines.set(visibleLines);
    }

    public VerticalLineFlow(Function<Integer, Set<N>> converter, Predicate<Set<N>> needsPresentation,
            Supplier<Node> nodeFactory, BiConsumer<Node, Set<N>> nodePopulator) {
        super(nodeFactory);
        this.needsPresentation = needsPresentation;
        this.nodePopulator = nodePopulator;
        this.converter = converter;

        this.visibleLines.addListener((InvalidationListener) (x) -> prepareNodes(getVisibleLines()));
        this.numberOfLines.addListener((InvalidationListener) (x) -> prepareNodes(getVisibleLines()));
    }

    protected Map<Integer, Node> activeNodes = new HashMap<>();

    protected void releaseNode(int lineIndex) {
        Node node = this.activeNodes.remove(lineIndex);
        if (node != null) {
            releaseNode(node);
        }
    }

    protected Node getNode(int lineIndex) {
        Node node = this.activeNodes.get(Integer.valueOf(lineIndex));

        if (node == null) {
            node = getNode();
        }

        this.activeNodes.put(Integer.valueOf(lineIndex), node);
        return node;
    }

    private void prepareNodes(RangeSet<Integer> range) {
        if (range == null)
            return;

        // release invisible nodes
        Iterator<Entry<Integer, Node>> iterator = this.activeNodes.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<Integer, Node> entry = iterator.next();
            Integer index = entry.getKey();
            if (!range.contains(index)) {
                releaseNode(entry.getValue());
                iterator.remove();
            }
        }

        // prepare range nodes
        range.asRanges().forEach(r -> ContiguousSet.create(r, DiscreteDomain.integers())
                .forEach(index -> prepareNode(index.intValue())));

    }

    private void prepareNode(int lineIndex) {
        Set<N> blub = this.converter.apply(Integer.valueOf(lineIndex));
        if (blub.isEmpty()) {
            releaseNode(lineIndex);
        } else {
            Node node = getNode(lineIndex);
            this.nodePopulator.accept(node, blub);
        }
    }

    public void setLineOffset(int lineIndex, double yOffset) {
        this.yOffsetData.put(Integer.valueOf(lineIndex), Double.valueOf(yOffset));
        requestLayout();
    }

    @Override
    protected void layoutChildren() {
        this.activeNodes.entrySet().forEach(e -> {
            if (!this.yOffsetData.containsKey(e.getKey())) {
                return;
            }
            double x = 0;
            double y = this.yOffsetData.get(e.getKey()).doubleValue();
            double width = getWidth();
            double height = getLineHeight();
            e.getValue().resizeRelocate(x, y, width, height);
        });
    }

    public void update(RangeSet<Integer> r) {
        prepareNodes(r.subRangeSet(this.visibleLines.get())
                .subRangeSet(Range.closedOpen(Integer.valueOf(0), Integer.valueOf(this.numberOfLines.get()))));
    }

}