gwt.material.design.addins.client.rating.MaterialRating.java Source code

Java tutorial

Introduction

Here is the source code for gwt.material.design.addins.client.rating.MaterialRating.java

Source

package gwt.material.design.addins.client.rating;

/*
 * #%L
 * GwtMaterial
 * %%
 * Copyright (C) 2015 - 2017 GwtMaterialDesign
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.HasValue;
import gwt.material.design.addins.client.base.constants.AddinsCssName;
import gwt.material.design.client.base.MaterialWidget;
import gwt.material.design.client.constants.Color;
import gwt.material.design.client.constants.IconType;
import gwt.material.design.client.ui.MaterialIcon;

import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 * MaterialRating is the component used by the 5-star rating system, for
 * example, allowing users to easily express their opinion about a product,
 * review, video and so on.
 * </p>
 * <p>
 * By default, it uses the {@link IconType#STAR} to represent the selected
 * rating, but other icons can be set using the
 * {@link #setSelectedRatingIcon(IconType)} method.
 * </p>
 * <p>
 * <h3>XML Namespace Declaration</h3>
 * <p>
 * <pre>
 * {@code
 * xmlns:ma='urn:import:gwt.material.design.addins.client'
 * }
 * </pre>
 * <p>
 * <h3>UiBinder Usage:</h3>
 * <p>
 * <pre>
 * {@code
 * <ma:rating.MaterialRating ui:field="rating" />
 * }
 * </pre>
 * <p>
 * To use different icons, for instance, hearts, you can set:
 * <p>
 * <pre>
 * {@code
 * <ma:rating.MaterialRating ui:field="rating" selectedRatingIcon="FAVORITE" unselectedRatingIcon="FAVORITE_BORDER" textColor="red" />
 * }
 * </pre>
 * <p>
 * You can also set the maximum rating (the default is 5):
 * <p>
 * <pre>
 * {@code
 * <ma:rating.MaterialRating ui:field="rating" maxRating="7" />
 * }
 * </pre>
 * <p>
 * <h3>Example Java Usage:</h3>
 * <p>
 * <pre>
 * {@code
 * MaterialRating rating = ... //create using new or using UiBinder
 * rating.addValueChangeHandler(...); // MaterialRating implements HasValue<Integer>
 * rating.setEditable(false); // disables user interaction
 * rating.setValue(2); // directly sets the desired rating
 * int selectedValue = rating.getValue(); // retrieves the selected rating
 * }
 * </pre>
 * <p>
 * <h3>Custom styling:</h3>
 * <p>
 * You use change the MaterialRating style by using the
 * <code>material-rating</code> CSS class. Selected rating icons have the
 * <code>material-rating-selected</code> CSS class, and unselected the
 * <code>material-rating-unselected</code> CSS class.
 * </p>
 *
 * @author gilberto-torrezan
 */
public class MaterialRating extends MaterialWidget implements HasValue<Integer> {

    private boolean editable = true;
    private int currentRating = 0;
    private int maxRating = 5;
    private IconType selectedRatingIcon = IconType.STAR;
    private IconType unselectedRatingIcon = IconType.STAR_BORDER;
    private List<MaterialIcon> iconList = new LinkedList<>();

    /**
     * Default constructor.
     */
    public MaterialRating() {
        super(DOM.createDiv(), AddinsCssName.MATERIAL_RATING);
    }

    public MaterialRating(IconType selectedRatingIcon, IconType unselectedRatingIcon, Color textColor) {
        this();
        setSelectedRatingIcon(selectedRatingIcon);
        setUnselectedRatingIcon(unselectedRatingIcon);
    }

    public MaterialRating(IconType selectedRatingIcon, IconType unselectedRatingIcon, Color textColor,
            Integer value) {
        this(selectedRatingIcon, unselectedRatingIcon, textColor);
        setValue(value);
    }

    public MaterialRating(IconType selectedRatingIcon, IconType unselectedRatingIcon, Color textColor,
            Integer value, Integer maxRating) {
        this(selectedRatingIcon, unselectedRatingIcon, textColor, value);
        setMaxRating(maxRating);
    }

    @Override
    protected void onLoad() {
        super.onLoad();

        revalidateLayout();
    }

    /**
     * Sets the maximum number of icons to show - which represents the maximum
     * selectable rating. The default is 5.
     *
     * @param maxRating The maximum selectable rating for this component
     */
    public void setMaxRating(int maxRating) {
        this.maxRating = maxRating;
        revalidateLayout();
    }

    /**
     * Returns the maximum selectable rating in this component.
     * The default is 5.
     *
     * @return The maximum rating
     */
    public int getMaxRating() {
        return maxRating;
    }

    /**
     * Sets the {@link IconType} to be used to represent the selected ratings.
     * The default is {@link IconType#STAR}.
     *
     * @param selectedRatingIcon The icon of the selected ratings
     */
    public void setSelectedRatingIcon(IconType selectedRatingIcon) {
        this.selectedRatingIcon = selectedRatingIcon;
        revalidateLayout();
    }

    /**
     * Returns the {@link IconType} used to represent the selected ratings. The
     * default is {@link IconType#STAR}.
     *
     * @return The icon for selected ratings
     */
    public IconType getSelectedRatingIcon() {
        return selectedRatingIcon;
    }

    /**
     * Sets the {@link IconType} to be used to represent the not selected
     * ratings. The default is {@link IconType#STAR_BORDER}.
     *
     * @param unselectedRatingIcon The icon of the unselected ratings
     */
    public void setUnselectedRatingIcon(IconType unselectedRatingIcon) {
        this.unselectedRatingIcon = unselectedRatingIcon;
        revalidateLayout();
    }

    /**
     * Returns the {@link IconType} used to represent the not selected ratings.
     * The default is {@link IconType#STAR_BORDER}.
     *
     * @return The icon for unselected ratings
     */
    public IconType getUnselectedRatingIcon() {
        return unselectedRatingIcon;
    }

    @Override
    public void clear() {
        iconList.clear();
        super.clear();
    }

    /**
     * Method called internally by the component to re-validate the number of
     * icons when the maximum rating is changed.
     */
    protected void revalidateLayout() {
        for (MaterialIcon icon : iconList) {
            icon.removeFromParent();
        }
        iconList.clear();

        // same mouse-out handler for all icons
        MouseOutHandler outHandler = event -> {
            if (!isEnabled() || !isEditable()) {
                return;
            }
            revalidateSelection(currentRating);
        };

        for (int i = 0; i < maxRating; i++) {
            final int rating = i + 1;
            MaterialIcon icon = new MaterialIcon(unselectedRatingIcon);
            registerHandler(icon.addClickHandler(event -> {
                if (!isEnabled() || !isEditable()) {
                    return;
                }
                setValue(rating, true);
            }));

            registerHandler(icon.addMouseOverHandler(event -> {
                if (!isEnabled() || !isEditable()) {
                    return;
                }
                revalidateSelection(rating);
            }));

            registerHandler(icon.addMouseOutHandler(outHandler));
            add(icon);
            iconList.add(icon);
        }
        revalidateSelection(currentRating);
    }

    /**
     * Method called internally by the component to revalidade selections by the
     * user, switching the icons accordingly.
     */
    protected void revalidateSelection(int rating) {
        for (MaterialIcon icon : iconList) {
            icon.removeStyleName(AddinsCssName.MATERIAL_RATING_UNSELECTED);
            icon.removeStyleName(AddinsCssName.MATERIAL_RATING_SELECTED);
        }

        for (int i = 0; i < rating && i < iconList.size(); i++) {
            MaterialIcon icon = iconList.get(i);
            icon.setIconType(selectedRatingIcon);
            icon.addStyleName(AddinsCssName.MATERIAL_RATING_SELECTED);
        }

        for (int i = rating; i < iconList.size(); i++) {
            MaterialIcon icon = iconList.get(i);
            icon.setIconType(unselectedRatingIcon);
            icon.addStyleName(AddinsCssName.MATERIAL_RATING_UNSELECTED);
        }
    }

    @Override
    public Integer getValue() {
        return currentRating;
    }

    @Override
    public void setValue(Integer value) {
        setValue(value, false);
    }

    @Override
    public void setValue(Integer value, boolean fireEvents) {
        currentRating = value;
        revalidateSelection(currentRating);
        if (fireEvents) {
            ValueChangeEvent.fire(this, value);
        }
    }

    /**
     * Sets whether the user can interact with the component or not.
     * Non-editable MaterialRatings can only show values, not allowing users to
     * change them. The default is <code>true</code> (editable).
     *
     * @param editable <code>true</code> to allow the user change the state of the component,
     *                 <code>false</code> otherwise.
     */
    public void setEditable(boolean editable) {
        this.editable = editable;
    }

    /**
     * Returns whether the component is editable by the user. The default is
     * <code>true</code> (editable).
     *
     * @return <code>true</code> if the component is editable by the user,
     * <code>false</code> otherwise'
     */
    public boolean isEditable() {
        return editable;
    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }
}