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

org.controlsfx.control.SegmentedBar Maven / Gradle / Ivy

Go to download

High quality UI controls and other tools to complement the core JavaFX distribution

There is a newer version: 11.2.1
Show newest version
/**
 * Copyright (c) 2016, ControlsFX
 * All rights reserved.
 * 

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of ControlsFX, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.controlsfx.control; import com.sun.javafx.css.converters.EnumConverter; import impl.org.controlsfx.skin.SegmentedBarSkin; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.WeakInvalidationListener; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.css.*; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.OverrunStyle; import javafx.scene.control.Skin; import javafx.scene.layout.StackPane; import javafx.util.Callback; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * A control that makes it easy to create a horizontal bar that visualizes the * segmentation of a total value. It consists of several segments, each segment * representing a value. The sum of all values is the total value of the bar * (see {@link #totalProperty()}). The bar can be customized by setting a * factory for the creation of the segment views. Another factory can be set for * for the creation of info nodes shown by a {@link PopOver}. *
*

Segmented Bar

*

Example 1:

* The most basic version of the bar. It is using the default segment view * factory. *
 * SegmentedBar bar = new SegmentedBar();
 * bar.getSegments().addAll(
 *     new Segment(10, "10"),
 *     new Segment(10, "10"),
 *     new Segment(10, "10"),
 *     new Segment(10, "10"),
 *     new Segment(10, "10"),
 *     new Segment(50, "50"));
 * 
*

Example 1:

* In this example the bar is used to visualize the usage of disk space for * various media types (photos, videos, music, ...). A special info node factory * is supplied to present a useful detailed description of the segment. The * type "TypeSegment" is a subclass of {@link org.controlsfx.control.SegmentedBar.Segment} *
 * typesBar.setSegmentViewFactory(segment -> new TypeSegmentView(segment));
 * typesBar.setInfoNodeFactory(segment -> new InfoLabel(segment.getText() + " " + segment.getValue() + " GB"));
 * typesBar.getSegments().addAll(
 *     new TypeSegment(14, MediaType.PHOTOS),
 *     new TypeSegment(32, MediaType.VIDEO),
 *     new TypeSegment(9, MediaType.APPS),
 *     new TypeSegment(40, MediaType.MUSIC),
 *     new TypeSegment(5, MediaType.OTHER),
 *     new TypeSegment(35, MediaType.FREE));
 * 
* * @see #setSegmentViewFactory(Callback) * @see #setInfoNodeFactory(Callback) * * @param the segment type */ public class SegmentedBar extends ControlsFXControl { private static final String DEFAULT_STYLE = "segmented-bar"; private static final PseudoClass VERTICAL_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("vertical"); private static final PseudoClass HORIZONTAL_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("horizontal"); /** * Constructs a new segmented bar. */ public SegmentedBar() { segments.addListener((Observable it) -> listenToValues()); listenToValues(); getStyleClass().add(DEFAULT_STYLE); setSegmentViewFactory(segment -> new SegmentView(segment)); setInfoNodeFactory(segment -> { Label label = new Label("Value: " + segment.getValue()); label.setPadding(new Insets(4)); return label; }); } @Override protected Skin createDefaultSkin() { return new SegmentedBarSkin(this); } @Override public String getUserAgentStylesheet() { return getUserAgentStylesheet(SegmentedBar.class, "segmentedbar.css"); } // popover node factory private final ObjectProperty> infoNodeFactory = new SimpleObjectProperty<>(this, "infoNodeFactory"); /** * Stores a factory callback for creating nodes that will be shown by a popover when the mouse * enters a segment. The popover / node can provide detailed information about a segment. * * @return the info node factory property */ public final ObjectProperty> infoNodeFactoryProperty() { return infoNodeFactory; } /** * Returns the value of {@link #infoNodeFactoryProperty()}. * * @return the info node factory */ public final Callback getInfoNodeFactory() { return infoNodeFactory.get(); } /** * Sets the value of {@link #infoNodeFactoryProperty()}. * * @param factory the info node factory */ public void setInfoNodeFactory(Callback factory) { this.infoNodeFactory.set(factory); } // orientation private ObjectProperty orientation = new StyleableObjectProperty(null) { @Override protected void invalidated() { final boolean vertical = (get() == Orientation.VERTICAL); pseudoClassStateChanged(VERTICAL_PSEUDOCLASS_STATE, vertical); pseudoClassStateChanged(HORIZONTAL_PSEUDOCLASS_STATE, !vertical); } @Override public CssMetaData getCssMetaData() { return StyleableProperties.ORIENTATION; } @Override public Object getBean() { return SegmentedBar.this; } @Override public String getName() { return "orientation"; //$NON-NLS-1$ } }; /** * Sets the value of the orientation property. * * @param value the new orientation (horizontal, vertical). * @see #orientationProperty() */ public final void setOrientation(Orientation value) { orientationProperty().set(value); } /** * Returns the value of the orientation property. * * @return the current orientation of the control * @see #orientationProperty() */ public final Orientation getOrientation() { return orientation == null ? Orientation.HORIZONTAL : orientation.get(); } /** * Returns the styleable object property used for storing the orientation of * the segmented bar. The CSS property "-fx-orientation" can be used to * initialize this value. * * @return the orientation property */ public final ObjectProperty orientationProperty() { return orientation; } private final ObjectProperty> segmentViewFactory = new SimpleObjectProperty<>(this, "segmentViewFactory"); /** * Stores the segment view factory that is used to create one view for each segment added to * the control. * * @return the property that stores the factory */ public final ObjectProperty> segmentViewFactoryProperty() { return segmentViewFactory; } /** * Returns the value of {@link #segmentViewFactoryProperty()}. * * @return the segment view factory */ public final Callback getSegmentViewFactory() { return segmentViewFactory.get(); } /** * Sets the value of {@link #segmentViewFactoryProperty()}. * * @param factory the segment view factory */ public final void setSegmentViewFactory(Callback factory) { this.segmentViewFactory.set(factory); } private final ListProperty segments = new SimpleListProperty<>(this, "segments", FXCollections.observableArrayList()); /** * A property used to store the list of segments shown by the bar. * * @return the segment list */ public final ListProperty segmentsProperty() { return segments; } /** * Returns the list of segments (the model). * * @return the list of segments shown by the bar */ public final ObservableList getSegments() { return segments.get(); } /** * Sets the list of segments (the model). * * @param segments the list of segments shown by the bar */ public void setSegments(ObservableList segments) { this.segments.set(segments); } private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(this, "total"); /** * A read-only property that stores the sum of all segment values attached * to the bar. * * @return the total value of the bar (sum of segments) */ public final ReadOnlyDoubleProperty totalProperty() { return total.getReadOnlyProperty(); } /** * Returns the value of {@link #totalProperty()}. * * @return the total value of the bar (sum of segments) */ public final double getTotal() { return total.get(); } private final InvalidationListener sumListener = (Observable it) -> total.set(segments.stream().collect(Collectors.summingDouble(segment -> segment.getValue()))); private final WeakInvalidationListener weakSumListener = new WeakInvalidationListener(sumListener); private void listenToValues() { segments.get().addListener(weakSumListener); getSegments().forEach(segment -> { // first remove then add listener to ensure listener is only added once segment.valueProperty().removeListener(weakSumListener); segment.valueProperty().addListener(weakSumListener); }); } /** * A model class used by the {@link SegmentedBar} control. Applications * usually subclass this type for their own specific needs. A segment always * carries a value and an optional text. Changing the value of the segment * will trigger a rebuild of the control. */ public static class Segment { /** * Constructs a new segment with the given value. * * @param value the segment's value */ public Segment(double value) { if (value < 0) { throw new IllegalArgumentException("value must be larger or equal to 0 but was " + value); } setValue(value); } /** * Constructs a new segment with the given value. * * @param value the segment's value * @param text the segment's text */ public Segment(double value, String text) { this(value); setText(text); } // text support private final StringProperty text = new SimpleStringProperty(this, "text"); /** * Stores the text of the segment (optional). * * @return the text property */ public final StringProperty textProperty() { return text; } /** * Sets the value of {@link #textProperty()}. * * @param text the new text */ public final void setText(String text) { this.text.set(text); } /** * Returns the value of {@link #textProperty()}. * * @return the segment's text */ public final String getText() { return text.get(); } // value support private final DoubleProperty value = new SimpleDoubleProperty(this, "value") { @Override public void set(double newValue) { if (newValue < 0) { throw new IllegalArgumentException("segment value must be >= 0 but was " + newValue); } super.set(newValue); } }; /** * Stores the current value of the segment. The value has to be larger or equal * to zero. * * @return the value property */ public final DoubleProperty valueProperty() { return value; } /** * Sets the value of {@link #valueProperty()}. * * @param value the new value */ public final void setValue(double value) { this.value.set(value); } /** * Returns the value of {@link #valueProperty()}. * * @return the segment's value */ public final double getValue() { return value.get(); } } private static class StyleableProperties { private static final CssMetaData ORIENTATION = new CssMetaData( "-fx-orientation", new EnumConverter<>( //$NON-NLS-1$ Orientation.class), Orientation.VERTICAL) { @Override public Orientation getInitialValue(SegmentedBar node) { // A vertical bar should remain vertical return node.getOrientation(); } @Override public boolean isSettable(SegmentedBar n) { return n.orientation == null || !n.orientation.isBound(); } @SuppressWarnings("unchecked") @Override public StyleableProperty getStyleableProperty( SegmentedBar n) { return (StyleableProperty) n.orientationProperty(); } }; private static final List> STYLEABLES; static { final List> styleables = new ArrayList<>(Control.getClassCssMetaData()); styleables.add(ORIENTATION); STYLEABLES = Collections.unmodifiableList(styleables); } } /** * The view class used by the default segment view factory. The view is a simple * StackPane that contains a label. The default style class is "segment-view". * * @see #setSegmentViewFactory(Callback) */ public class SegmentView extends StackPane { private Label label; /** * Constructs a new segment view. * * @param segment the segment for which the view will be created. */ public SegmentView(T segment) { getStyleClass().add("segment-view"); label = new Label(); label.textProperty().bind(segment.textProperty()); label.setTextOverrun(OverrunStyle.CLIP); StackPane.setAlignment(label, Pos.CENTER_LEFT); getChildren().add(label); } /** * The layout method has been overridden to ensure that the label used for displaying the * text of a segment will be made invisible when there is not enough space to show the entire * text. */ @Override protected void layoutChildren() { super.layoutChildren(); label.setVisible(label.prefWidth(-1) < getWidth() - getPadding().getLeft() - getPadding().getRight()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy