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

Java tutorial

Introduction

Here is the source code for org.eclipse.fx.ui.controls.styledtext.internal.Scroller.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 com.google.common.collect.Range;

import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.ScrollBar;

@SuppressWarnings("javadoc")
public class Scroller {

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

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

    private DoubleProperty lineCount = new SimpleDoubleProperty(this, "lineCount", 0); //$NON-NLS-1$

    public DoubleProperty lineCountProperty() {
        return this.lineCount;
    }

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

    private DoubleBinding pageHeight = this.lineHeight.multiply(this.visibleLineCount.add(-1));

    private DoubleProperty contentHeight = new SimpleDoubleProperty(this, "contentHeight", 0); //$NON-NLS-1$

    public ReadOnlyDoubleProperty contentHeightProperty() {
        return this.contentHeight;
    }

    private DoubleProperty contentAreaHeight = new SimpleDoubleProperty(this, "contentAreaHeight", 0); //$NON-NLS-1$

    public DoubleProperty contentAreaHeightProperty() {
        return this.contentAreaHeight;
    }

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

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

    public ReadOnlyIntegerProperty visibleLineCountProperty() {
        return this.visibleLineCount;
    }

    private DoubleProperty offset = new SimpleDoubleProperty(this, "offset", 0.01); //$NON-NLS-1$

    public DoubleProperty offsetProperty() {
        return this.offset;
    }

    private DoubleProperty min = new SimpleDoubleProperty(this, "min", 0); //$NON-NLS-1$
    private DoubleProperty max = new SimpleDoubleProperty(this, "max", 0); //$NON-NLS-1$

    public Scroller() {

        install();
    }

    private void recomputeContentHeight(Observable x) {
        double contentHeight = this.lineCount.get() * this.lineHeight.get();
        this.contentHeight.set(contentHeight);
    }

    private void recomputeMax(Observable x) {
        double max = Math.max(0, this.contentHeight.get() - this.contentAreaHeight.get());
        this.max.set(max);
    }

    private void recomputeVisibleLineCount(Observable x) {
        int visibleLineCount = (int) Math.ceil(this.contentAreaHeight.get() / this.lineHeight.get());
        this.visibleLineCount.set(visibleLineCount);
    }

    private void recomputeVisibleLines(Observable x) {
        int lower = (int) Math.floor(this.offset.get() / this.lineHeight.get());
        int upper = lower + this.visibleLineCount.get();
        Range<Integer> visibleLines = Range.closed(Integer.valueOf(lower), Integer.valueOf(upper));
        this.visibleLines.set(visibleLines);
    }

    private void recomputeAll() {
        recomputeContentHeight(null);
        recomputeMax(null);
        recomputeVisibleLineCount(null);
        recomputeVisibleLines(null);
    }

    private void install() {
        this.lineCount.addListener(this::recomputeContentHeight);
        this.lineHeight.addListener(this::recomputeContentHeight);

        this.contentHeight.addListener(this::recomputeMax);
        this.contentAreaHeight.addListener(this::recomputeMax);

        this.lineHeight.addListener(this::recomputeVisibleLineCount);
        this.contentAreaHeight.addListener(this::recomputeVisibleLineCount);

        this.offset.addListener(this::recomputeVisibleLines);
        this.lineHeight.addListener(this::recomputeVisibleLines);
        this.visibleLineCount.addListener(this::recomputeVisibleLines);
    }

    public void bind(ScrollBar bar) {
        bar.minProperty().bind(this.min);

        bar.maxProperty().bind(this.max);

        bar.visibleAmountProperty().bind(this.contentAreaHeight.divide(this.contentHeight.divide(this.max)));

        bar.valueProperty().bindBidirectional(this.offset);

        bar.unitIncrementProperty().bind(this.lineHeight);
        bar.blockIncrementProperty().bind(this.pageHeight);

        recomputeAll();
    }

    public void scrollBy(int lines) {
        scrollBy(lineHeightProperty().get() * lines);
    }

    public void scrollBy(double units) {

        double newVal = this.offset.get() + units;
        newVal = Math.min(this.max.get(), newVal);
        newVal = Math.max(this.min.get(), newVal);

        this.offset.set(newVal);
    }

    public void scrollIntoView(int line) {
        double lineOffset = line * this.lineHeight.get();

        if (lineOffset < this.offset.get()) {
            // we need to scroll up
            this.offset.set(lineOffset);
        }

        double lastLineDelta = this.contentAreaHeight.get() - this.lineHeight.get();
        if (lineOffset > this.offset.get() + lastLineDelta) {
            // we need to scroll down
            this.offset.set(lineOffset - lastLineDelta);
        }
    }

    public void scrollTo(int line) {
        this.offset.set(line * this.lineHeight.get());
    }

    public void refresh() {
        recomputeAll();
    }

}