/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.
*/
package com.vaadin.ui;
import java.io.Serializable;
import java.util.Collection;
import java.util.Objects;
import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import com.vaadin.shared.ui.colorpicker.AbstractColorPickerState;
import com.vaadin.shared.ui.colorpicker.Color;
import com.vaadin.shared.ui.colorpicker.ColorPickerServerRpc;
import com.vaadin.ui.components.colorpicker.ColorPickerPopup;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
/**
* An abstract class that defines default implementation for a color picker
* component.
*
* @since 7.0.0
*/
public abstract class AbstractColorPicker extends AbstractField<Color> {
/**
* Interface for converting 2d-coordinates to a Color.
*/
public interface Coordinates2Color extends Serializable {
/**
* Calculates a color from coordinates.
*
* @param x
* the x-coordinate
* @param y
* the y-coordinate
*
* @return the color
*/
public Color calculate(int x, int y);
/**
* Calculates coordinates from a color.
*
* @param c
* the c
*
* @return the integer array with the coordinates
*/
public int[] calculate(Color c);
}
/**
* The style of the color picker popup.
*/
public enum PopupStyle {
/** A full popup with all tabs visible. */
POPUP_NORMAL("normal"),
/** A simple popup with only the swatches (palette) tab. */
POPUP_SIMPLE("simple");
private final String style;
PopupStyle(String styleName) {
style = styleName;
}
@Override
public String toString() {
return style;
}
}
private ColorPickerServerRpc rpc = this::showPopup;
protected static final String STYLENAME_DEFAULT = "v-colorpicker";
protected static final String STYLENAME_BUTTON = "v-button";
protected static final String STYLENAME_AREA = "v-colorpicker-area";
protected PopupStyle popupStyle = PopupStyle.POPUP_NORMAL;
private ColorPickerPopup window;
/** The currently selected color. */
protected Color color;
private UI parent;
private String popupCaption = null;
private int positionX = 0;
private int positionY = 0;
protected boolean rgbVisible = true;
protected boolean hsvVisible = true;
protected boolean swatchesVisible = true;
protected boolean historyVisible = true;
protected boolean textfieldVisible = true;
/**
* Instantiates a new color picker.
*/
public AbstractColorPicker() {
this("Colors", Color.WHITE);
}
/**
* Instantiates a new color picker.
*
* @param popupCaption
* the caption of the popup window
*/
public AbstractColorPicker(String popupCaption) {
this(popupCaption, Color.WHITE);
}
/**
* Instantiates a new color picker.
*
* @param popupCaption
* the caption of the popup window
* @param initialColor
* the initial color
*/
public AbstractColorPicker(String popupCaption, Color initialColor) {
super();
registerRpc(rpc);
setValue(initialColor);
this.popupCaption = popupCaption;
setDefaultStyles();
setCaption("");
}
/**
* Returns the current selected color of this color picker.
*
* @return the selected color, not null
*/
@Override
public Color getValue() {
return color;
}
/**
* Sets the selected color of this color picker. If the new color is not
* equal to getValue(), fires a {@link ValueChangeEvent}.
*
* @param color
* the new selected color, not null
* @throws NullPointerException
* if {@code color} is {@code null}
*/
@Override
public void setValue(Color color) {
Objects.requireNonNull(color, "color cannot be null");
super.setValue(color);
}
/**
* Set true if the component should show a default caption (css-code for the
* currently selected color, e.g. #ffffff) when no other caption is
* available.
*
* @param enabled
* {@code true} to enable the default caption, {@code false} to
* disable
*/
public void setDefaultCaptionEnabled(boolean enabled) {
getState().showDefaultCaption = enabled;
}
/**
* Returns true if the component shows the default caption (css-code for the
* currently selected color, e.g. #ffffff) if no other caption is available.
*
* @return {@code true} if the default caption is enabled, {@code false}
* otherwise
*/
public boolean isDefaultCaptionEnabled() {
return getState(false).showDefaultCaption;
}
/**
* Sets the position of the popup window.
*
* @param x
* the x-coordinate
* @param y
* the y-coordinate
*/
public void setPosition(int x, int y) {
positionX = x;
positionY = y;
if (window != null) {
window.setPositionX(x);
window.setPositionY(y);
}
}
/**
* Sets the style of the popup window.
*
* @param style
* the popup window style
*/
public void setPopupStyle(PopupStyle style) {
popupStyle = style;
switch (style) {
case POPUP_NORMAL:
setRGBVisibility(true);
setHSVVisibility(true);
setSwatchesVisibility(true);
setHistoryVisibility(true);
setTextfieldVisibility(true);
break;
case POPUP_SIMPLE:
setRGBVisibility(false);
setHSVVisibility(false);
setSwatchesVisibility(true);
setHistoryVisibility(false);
setTextfieldVisibility(false);
break;
default:
assert false : "Unknown popup style " + style;
}
}
/**
* Gets the style for the popup window.
*
* @since 7.5.0
* @return popup window style
*/
public PopupStyle getPopupStyle() {
return popupStyle;
}
/**
* Sets the visibility of the RGB tab.
*
* @param visible
* {@code true} to display the RGB tab, {@code false} to hide it
*/
public void setRGBVisibility(boolean visible) {
if (!visible && !hsvVisible && !swatchesVisible) {
throw new IllegalArgumentException("Cannot hide all tabs.");
}
rgbVisible = visible;
if (window != null) {
window.setRGBTabVisible(visible);
}
}
/**
* Gets the visibility of the RGB Tab.
*
* @since 7.5.0
* @return visibility of the RGB tab
*/
public boolean getRGBVisibility() {
return rgbVisible;
}
/**
* Sets the visibility of the HSV Tab.
*
* @param visible
* {@code true} to display the HSV tab, {@code false} to hide it
*/
public void setHSVVisibility(boolean visible) {
if (!visible && !rgbVisible && !swatchesVisible) {
throw new IllegalArgumentException("Cannot hide all tabs.");
}
hsvVisible = visible;
if (window != null) {
window.setHSVTabVisible(visible);
}
}
/**
* Gets the visibility of the HSV tab.
*
* @since 7.5.0
* @return {@code true} if the HSV tab is currently displayed, {@code false}
* otherwise
*/
public boolean getHSVVisibility() {
return hsvVisible;
}
/**
* Sets the visibility of the Swatches (palette) tab.
*
* @param visible
* {@code true} to display the Swatches tab, {@code false} to
* hide it
*/
public void setSwatchesVisibility(boolean visible) {
if (!visible && !hsvVisible && !rgbVisible) {
throw new IllegalArgumentException("Cannot hide all tabs.");
}
swatchesVisible = visible;
if (window != null) {
window.setSwatchesTabVisible(visible);
}
}
/**
* Gets the visibility of the Swatches (palette) tab.
*
* @since 7.5.0
* @return {@code true} if the Swatches tab is currently displayed,
* {@code false} otherwise
*/
public boolean getSwatchesVisibility() {
return swatchesVisible;
}
/**
* Sets the visibility of the color history, displaying recently picked
* colors.
*
* @param visible
* {@code true} to display the history, {@code false} to hide it
*/
public void setHistoryVisibility(boolean visible) {
historyVisible = visible;
if (window != null) {
window.setHistoryVisible(visible);
}
}
/**
* Gets the visibility of the Color history.
*
* @since 7.5.0
* @return {@code true} if the history is currently displayed, {@code false}
* otherwise
*/
public boolean getHistoryVisibility() {
return historyVisible;
}
/**
* Sets the visibility of the CSS color code text field.
*
* @param visible
* {@code true} to display the CSS text field, {@code false} to
* hide it
*/
public void setTextfieldVisibility(boolean visible) {
textfieldVisible = visible;
if (window != null) {
window.setPreviewVisible(visible);
}
}
/**
* Gets the visibility of CSS color code text field.
*
* @since 7.5.0
* @return {@code true} if the CSS text field is currently displayed,
* {@code false} otherwise
*/
public boolean getTextfieldVisibility() {
return textfieldVisible;
}
@Override
protected AbstractColorPickerState getState() {
return (AbstractColorPickerState) super.getState();
}
@Override
protected AbstractColorPickerState getState(boolean markAsDirty) {
return (AbstractColorPickerState) super.getState(markAsDirty);
}
/**
* Sets the default styles of the component.
*/
protected abstract void setDefaultStyles();
/**
* Shows a popup-window for color selection.
*/
public void showPopup() {
showPopup(true);
}
/**
* Hides a popup-window for color selection.
*/
public void hidePopup() {
showPopup(false);
}
/**
* Shows or hides the popup window depending on the given parameter. If
* there is no such window yet, one is created.
*
* @param open
* {@code true} to display the popup, {@code false} to hide it
*/
protected void showPopup(boolean open) {
if (open && !isReadOnly()) {
if (parent == null) {
parent = getUI();
}
Color color = getValue();
if (window == null) {
window = new ColorPickerPopup(color);
window.setCaption(popupCaption);
window.setRGBTabVisible(rgbVisible);
window.setHSVTabVisible(hsvVisible);
window.setSwatchesTabVisible(swatchesVisible);
window.setHistoryVisible(historyVisible);
window.setPreviewVisible(textfieldVisible);
window.addCloseListener(
event -> getState().popupVisible = false);
window.addValueChangeListener(
event -> setValue(event.getValue()));
window.getHistory().setValue(color);
window.setPositionX(positionX);
window.setPositionY(positionY);
window.setVisible(true);
parent.addWindow(window);
window.focus();
} else if (!parent.equals(window.getParent())) {
window.setRGBTabVisible(rgbVisible);
window.setHSVTabVisible(hsvVisible);
window.setSwatchesTabVisible(swatchesVisible);
window.setHistoryVisible(historyVisible);
window.setPreviewVisible(textfieldVisible);
window.setValue(color);
window.getHistory().setValue(color);
window.setVisible(true);
parent.addWindow(window);
window.focus();
}
} else if (window != null) {
window.setVisible(false);
parent.removeWindow(window);
}
getState().popupVisible = open;
}
@Override
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
Attributes attributes = design.attributes();
if (design.hasAttr("color")) {
// Ignore the # character
String hexColor = DesignAttributeHandler
.readAttribute("color", attributes, String.class)
.substring(1);
doSetValue(new Color(Integer.parseInt(hexColor, 16)));
}
if (design.hasAttr("popup-style")) {
setPopupStyle(PopupStyle.valueOf(
"POPUP_" + attributes.get("popup-style").toUpperCase()));
}
if (design.hasAttr("position")) {
String[] position = attributes.get("position").split(",");
setPosition(Integer.parseInt(position[0]),
Integer.parseInt(position[1]));
}
}
@Override
public void writeDesign(Element design, DesignContext designContext) {
super.writeDesign(design, designContext);
Attributes attribute = design.attributes();
DesignAttributeHandler.writeAttribute("color", attribute,
getValue().getCSS(), Color.WHITE.getCSS(), String.class,
designContext);
DesignAttributeHandler.writeAttribute("popup-style", attribute,
popupStyle == PopupStyle.POPUP_NORMAL ? "normal" : "simple",
"normal", String.class, designContext);
DesignAttributeHandler.writeAttribute("position", attribute,
positionX + "," + positionY, "0,0", String.class,
designContext);
}
@Override
protected Collection<String> getCustomAttributes() {
Collection<String> result = super.getCustomAttributes();
result.add("color");
result.add("position");
result.add("popup-style");
return result;
}
@Override
protected void doSetValue(Color color) {
this.color = color;
getState().color = color.getCSS();
}
@Override
public Color getEmptyValue() {
return Color.WHITE;
}
}