/*
* 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.components.colorpicker;
import java.util.Iterator;
import java.util.Objects;
import com.vaadin.data.HasValue;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.colorpicker.Color;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.HasComponents;
import com.vaadin.ui.TextField;
/**
* A component that represents color selection preview within a color picker.
*
* @since 7.0.0
*/
public class ColorPickerPreview extends CssLayout implements HasValue<Color> {
private static final String STYLE_DARK_COLOR = "v-textfield-dark";
private static final String STYLE_LIGHT_COLOR = "v-textfield-light";
/** The color. */
private Color color;
/** The field. */
private final TextField field;
/** The old value. */
private String oldValue;
private Registration valueChangeListenerRegistration = null;
private boolean readOnly;
private ColorPickerPreview() {
setStyleName("v-colorpicker-preview");
field = new TextField();
field.setSizeFull();
field.setStyleName("v-colorpicker-preview-textfield");
field.setData(this);
valueChangeListenerRegistration = field
.addValueChangeListener(this::valueChange);
addComponent(field);
}
/**
* Instantiates a new color picker preview.
*/
public ColorPickerPreview(Color color) {
this();
setValue(color);
}
/**
* Sets the value of this object. If the new value is not equal to
* {@code getValue()}, fires a {@link ValueChangeEvent}. Throws
* {@code NullPointerException} if the value is null.
*
* @param color
* the new value, not {@code null}
* @throws NullPointerException
* if {@code color} is {@code null}
*/
@Override
public void setValue(Color color) {
Objects.requireNonNull(color, "color cannot be null");
this.color = color;
// Unregister listener
valueChangeListenerRegistration.remove();
String colorCSS = color.getCSS();
field.setValue(colorCSS);
oldValue = colorCSS;
// Re-register listener
valueChangeListenerRegistration = field
.addValueChangeListener(this::valueChange);
// Set the text color
field.removeStyleName(STYLE_DARK_COLOR);
field.removeStyleName(STYLE_LIGHT_COLOR);
if (this.color.getRed() + this.color.getGreen()
+ this.color.getBlue() < 3 * 128) {
field.addStyleName(STYLE_DARK_COLOR);
} else {
field.addStyleName(STYLE_LIGHT_COLOR);
}
markAsDirty();
}
@Override
public Color getValue() {
return color;
}
@Override
public Registration addValueChangeListener(
ValueChangeListener<Color> listener) {
Objects.requireNonNull(listener, "listener cannot be null");
return addListener(ValueChangeEvent.class, listener,
ValueChangeListener.VALUE_CHANGE_METHOD);
}
private void valueChange(ValueChangeEvent<String> event) {
String value = event.getValue();
Color oldColor = color;
try {
if (value != null) {
/*
* Description of supported formats see
* http://www.w3schools.com/cssref/css_colors_legal.asp
*/
if (value.length() == 7 && value.startsWith("#")) {
// CSS color format (e.g. #000000)
int red = Integer.parseInt(value.substring(1, 3), 16);
int green = Integer.parseInt(value.substring(3, 5), 16);
int blue = Integer.parseInt(value.substring(5, 7), 16);
color = new Color(red, green, blue);
} else if (value.startsWith("rgb")) {
// RGB color format rgb/rgba(255,255,255,0.1)
String[] colors = value.substring(value.indexOf('(') + 1,
value.length() - 1).split(",");
int red = Integer.parseInt(colors[0]);
int green = Integer.parseInt(colors[1]);
int blue = Integer.parseInt(colors[2]);
if (colors.length > 3) {
int alpha = (int) (Double.parseDouble(colors[3])
* 255d);
color = new Color(red, green, blue, alpha);
} else {
color = new Color(red, green, blue);
}
} else if (value.startsWith("hsl")) {
// HSL color format hsl/hsla(100,50%,50%,1.0)
String[] colors = value.substring(value.indexOf('(') + 1,
value.length() - 1).split(",");
int hue = Integer.parseInt(colors[0]);
int saturation = Integer
.parseInt(colors[1].replace("%", ""));
int lightness = Integer
.parseInt(colors[2].replace("%", ""));
int rgb = Color.HSLtoRGB(hue, saturation, lightness);
if (colors.length > 3) {
int alpha = (int) (Double.parseDouble(colors[3])
* 255d);
color = new Color(rgb);
color.setAlpha(alpha);
} else {
color = new Color(rgb);
}
}
oldValue = value;
fireEvent(new ValueChangeEvent<>(this, oldColor,
event.isUserOriginated()));
}
} catch (NumberFormatException nfe) {
// Revert value
field.setValue(oldValue);
}
}
@Override
protected String getCss(Component c) {
return "background: " + color.getCSS();
}
@Override
public void setRequiredIndicatorVisible(boolean visible) {
field.setRequiredIndicatorVisible(visible);
}
@Override
public boolean isRequiredIndicatorVisible() {
return field.isRequiredIndicatorVisible();
}
@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
updateColorComponents();
}
@Override
public boolean isReadOnly() {
return readOnly;
}
private void updateColorComponents() {
iterator().forEachRemaining(this::updateColorComponents);
}
private void updateColorComponents(Component component) {
if (component instanceof HasValue<?>) {
((HasValue<?>) component).setReadOnly(isReadOnly());
}
if (component instanceof HasComponents) {
Iterator<Component> iterator = ((HasComponents) component)
.iterator();
while (iterator.hasNext()) {
updateColorComponents(iterator.next());
}
}
}
}