blob: 4b32c5d9f339d456e65a79b9717856d8bf9deec2 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2013, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.ui.graphics;
import static org.eclipse.statet.r.core.model.RGraphicFunctions.COLORS_COLOR_DEF_TYPE;
import static org.eclipse.statet.r.core.model.RGraphicFunctions.PALETTE_COLOR_DEF_TYPE;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.statet.ecommons.graphics.core.ColorAlphaDef;
import org.eclipse.statet.ecommons.graphics.core.ColorDef;
import org.eclipse.statet.ecommons.graphics.core.HSVColorDef;
import org.eclipse.statet.ecommons.ui.components.ColorPalette;
import org.eclipse.statet.ecommons.ui.components.DoubleText;
import org.eclipse.statet.ecommons.ui.components.HSVSelector;
import org.eclipse.statet.ecommons.ui.components.IObjValueListener;
import org.eclipse.statet.ecommons.ui.components.IntText;
import org.eclipse.statet.ecommons.ui.components.ObjValueEvent;
import org.eclipse.statet.ecommons.ui.components.RGBSelector;
import org.eclipse.statet.ecommons.ui.dialogs.ToolPopup;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.r.core.model.RGraphicFunctions;
public class RColorChooser extends ToolPopup {
private static final String R_COLORS = "RColors"; //$NON-NLS-1$
private static final String R_PALETTE = "RPalette"; //$NON-NLS-1$
private static final String RGB = "RGB"; //$NON-NLS-1$
private static final String HSV = "HSV"; //$NON-NLS-1$
public static void drawPreview(final GC gc,
final int x, final int y, final int width, final int height,
final Color color ) {
if (color != null) {
gc.setBackground(color);
gc.fillRectangle(x, y, width + 1, height + 1);
}
}
public static Color createPreviewColor(final Display display, final ColorDef colorDef) {
return new Color(display, colorDef.getRed(), colorDef.getGreen(), colorDef.getBlue());
}
protected class PaletteTab extends ToolTab {
ColorPalette fPalette;
PaletteTab(final String key, final String name, final String tooltip, final List<? extends ColorDef> colors) {
super(key, RColorChooser.this, name, "Palette of Named Colors in R");
final Composite composite = create();
composite.setLayout(LayoutUtils.newTabGrid(1));
fPalette = new ColorPalette(composite);
fPalette.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
fPalette.setColors(colors);
fPalette.addValueListener(new IObjValueListener<ColorDef>() {
@Override
public void valueAboutToChange(final ObjValueEvent<ColorDef> event) {
}
@Override
public void valueChanged(final ObjValueEvent<ColorDef> event) {
if (event.newValue == null) {
return;
}
doSetColorValue(event.newValue);
if ((event.flags & ObjValueEvent.DEFAULT_SELECTION) != 0) {
performOK();
}
}
});
}
@Override
protected void activated() {
final ColorDef value = doGetColorValue();
fPalette.setValue(0, value);
final ColorDef paletteColor = fPalette.getValue(0);
if (paletteColor != null) {
doSetColorValue(paletteColor);
}
}
}
private static final int[] safeRGBIntArray(final ColorDef color) {
final int[] rgb = new int[] { 255, 0, 0 };
if (color != null) {
rgb[0] = color.getRed();
rgb[1] = color.getGreen();
rgb[2] = color.getBlue();
}
return rgb;
}
protected class RGBTab extends ToolTab {
private final RGBSelector fSelector;
private final Button[] fRGBButton = new Button[3];
private final IntText[] fRGBText = new IntText[3];
private Text fHexText;
private int fTextChange;
RGBTab() {
super(RGB, RColorChooser.this, "RGB", "Definition by Red-Green-Blue"); //$NON-NLS-1$
final Composite composite = create();
composite.setLayout(LayoutUtils.newTabGrid(4));
fSelector = new RGBSelector(composite);
fSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 5));
{ final String[] fieldLabels = new String[] { "&Red", "&Green", "&Blue" };
final Listener buttonListener = new Listener() {
@Override
public void handleEvent(final Event event) {
fSelector.setPrimary(indexOf((Button) event.widget));
}
};
final IObjValueListener<Integer> textListener = new IObjValueListener<Integer>() {
@Override
public void valueAboutToChange(final ObjValueEvent<Integer> event) {
}
@Override
public void valueChanged(final ObjValueEvent<Integer> event) {
if (event.newValue == null) {
return;
}
final ColorDef oldColor = fSelector.getValue(0);
final int[] rgb = safeRGBIntArray(oldColor);
fTextChange++;
try {
rgb[indexOf((IntText) event.getSource())] = event.newValue;
final ColorDef newColor = new ColorDef(rgb[0], rgb[1], rgb[2]);
if (!newColor.equals(oldColor)) {
fSelector.setValue(0, newColor);
doSetColorValue(newColor);
}
}
catch (final NumberFormatException e) {}
catch (final IllegalArgumentException e) {}
finally {
fTextChange--;
}
return;
}
};
for (int i = 0; i < 3; i++) {
final Button button = new Button(composite, SWT.RADIO);
button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
button.setText(fieldLabels[i]);
button.addListener(SWT.Selection, buttonListener);
fRGBButton[i] = button;
final IntText text = new IntText(composite, SWT.BORDER);
final GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
gd.widthHint = LayoutUtils.hintWidth(text.getControl(), 4);
text.getControl().setLayoutData(gd);
text.addValueListener(textListener);
text.setIncrement(1);
text.setMinMax(0, 255);
fRGBText[i] = text;
}
fRGBButton[0].setSelection(true);
}
{ final Label label = new Label(composite, SWT.NONE);
final GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true, 3, 1);
label.setLayoutData(gd);
}
{ final Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true, 1, 1));
label.setText("Hex:");
final Text text = new Text(composite, SWT.LEFT | SWT.SINGLE | SWT.BORDER);
final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
gd.widthHint = LayoutUtils.hintWidth(text, 9);
text.setLayoutData(gd);
text.addListener(SWT.Modify, new Listener() {
@Override
public void handleEvent(final Event event) {
final String s = fHexText.getText();
if (s.length() == 7 && s.charAt(0) == '#') {
final ColorDef newColor = ColorDef.parseRGBHex(s.substring(1));
if (newColor != null && !newColor.equals(fSelector.getValue(0))) {
fSelector.setValue(0, newColor);
}
}
}
});
fHexText = text;
}
fSelector.addValueListener(new IObjValueListener<ColorDef>() {
@Override
public void valueAboutToChange(final ObjValueEvent<ColorDef> event) {
}
@Override
public void valueChanged(final ObjValueEvent<ColorDef> event) {
final ColorDef value = event.newValue;
if (fTextChange == 0) {
final Integer[] rgb = new Integer[3];
rgb[0] = value.getRed();
rgb[1] = value.getGreen();
rgb[2] = value.getBlue();
for (int i = 0; i < 3; i++) {
fRGBText[i].setValue(0, rgb[i]);
}
doSetColorValue(value);
}
final StringBuilder sb = new StringBuilder(7);
sb.append('#');
value.printRGBHex(sb);
final Point selection = fHexText.getSelection();
fHexText.setText(sb.toString());
fHexText.setSelection(selection);
}
});
}
private int indexOf(final IntText source) {
for (int i = 0; i < 3; i++) {
if (fRGBText[i] == source) {
return i;
}
}
return -1;
}
private int indexOf(final Button source) {
for (int i = 0; i < 3; i++) {
if (fRGBButton[i] == source) {
return i;
}
}
return -1;
}
@Override
protected void activated() {
final ColorDef value = doGetColorValue();
fSelector.setValue(0, value);
}
}
private final static double[] safeHSVDoubleArray(final HSVColorDef color) {
final double[] hsv = new double[] { 0.0, 1.0, 1.0 };
if (color != null) {
hsv[0] = color.getHue();
hsv[1] = color.getSaturation();
hsv[2] = color.getValue();
}
return hsv;
}
protected class HSVTab extends ToolTab {
private final HSVSelector fSelector;
private final DoubleText[] fHSVText = new DoubleText[3];
private int fTextChange;
HSVTab() {
super(HSV, RColorChooser.this, "HSV", "Definition by Hue-Saturation-Value"); //$NON-NLS-1$
final Composite composite = create();
composite.setLayout(LayoutUtils.newTabGrid(3));
fSelector = new HSVSelector(composite);
fSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
{ final String[] fieldLabels = new String[] { "&Hue:", "&Saturation:", "&Value:" };
final IObjValueListener<Double> textListener = new IObjValueListener<Double>() {
@Override
public void valueAboutToChange(final ObjValueEvent<Double> event) {
}
@Override
public void valueChanged(final ObjValueEvent<Double> event) {
if (event.newValue == null) {
return;
}
final HSVColorDef oldValue = fSelector.getValue(0);
final double[] hsv = safeHSVDoubleArray(oldValue);
fTextChange++;
try {
hsv[indexOf((DoubleText) event.getSource())] = event.newValue;
final HSVColorDef value = new HSVColorDef((float) hsv[0], (float) hsv[1], (float) hsv[2]);
if (!value.equals(oldValue)) {
fSelector.setValue(0, value);
doSetColorValue(value);
}
}
catch (final NumberFormatException e) {}
catch (final IllegalArgumentException e) {}
finally {
fTextChange--;
}
return;
}
};
for (int i = 0; i < 3; i++) {
final Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
label.setText(fieldLabels[i]);
final DoubleText text = new DoubleText(composite, SWT.BORDER);
final GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
gd.widthHint = LayoutUtils.hintWidth(text.getControl(), 6);
text.getControl().setLayoutData(gd);
text.addValueListener(textListener);
text.setIncrement(0.001);
text.setMinMax(0.0, 1.0);
text.setFormat(DoubleText.createFormat(3));
fHSVText[i] = text;
}
}
{ final Label label = new Label(composite, SWT.NONE);
final GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true, 2, 1);
label.setLayoutData(gd);
}
fSelector.addValueListener(new IObjValueListener<ColorDef>() {
@Override
public void valueAboutToChange(final ObjValueEvent<ColorDef> event) {
}
@Override
public void valueChanged(final ObjValueEvent<ColorDef> event) {
if (fTextChange == 0) {
final HSVColorDef value = (HSVColorDef) event.newValue;
final Double[] hsv = new Double[3];
hsv[0] = Double.valueOf(value.getHue());
hsv[1] = Double.valueOf(value.getSaturation());
hsv[2] = Double.valueOf(value.getValue());
for (int i = 0; i < 3; i++) {
fHSVText[i].setValue(0, hsv[i]);
}
doSetColorValue(value);
}
}
});
}
private int indexOf(final DoubleText source) {
for (int i = 0; i < 3; i++) {
if (fHSVText[i] == source) {
return i;
}
}
return -1;
}
@Override
protected void activated() {
final ColorDef value = doGetColorValue();
fSelector.setValue(0, value);
}
}
private ColorDef fInitialValue;
private Color fInitialSWTColor;
private ColorDef fCurrentValue;
private Color fCurrentSWTColor;
private Composite fStatusControl;
public RColorChooser() {
}
public void open(final Shell parent, final Rectangle position, final ColorDef initialValue) {
fInitialValue = initialValue;
doSetValue((initialValue != null) ? initialValue :
RGraphicFunctions.DEFAULT.colorsMap.get("black") ); //$NON-NLS-1$
super.open(parent, position);
}
@Override
protected void addTabs(final CTabFolder tabFolder) {
new PaletteTab(R_COLORS, "R Colors", "Definition by R color names", RGraphicFunctions.DEFAULT.colorsList);
new RGBTab();
new HSVTab();
new PaletteTab(R_PALETTE, "R Palette", "Definition by R palette index", RGraphicFunctions.DEFAULT.defaultPalette);
new RAlphaChooser.AlphaTab(this, "+ Alpha", "Add transparency by Alpha [0, 1]") {
@Override
protected void setValue(final Float value) {
RColorChooser.this.doSetAlphaValue(value);
}
@Override
protected Float getValue() {
return RColorChooser.this.doGetAlphaValue();
}
@Override
protected ColorDef getBaseColor() {
return RColorChooser.this.doGetColorValue();
}
};
}
@Override
protected ToolTab getBestTab() {
if (fCurrentValue.getType() == "hsv") { //$NON-NLS-1$
return getTab(HSV);
}
else if (fCurrentValue.getType() == COLORS_COLOR_DEF_TYPE) {
return getTab(R_COLORS);
}
else if (fCurrentValue.getType() == PALETTE_COLOR_DEF_TYPE) {
return getTab(R_PALETTE);
}
else {
return getTab(RGB);
}
}
@Override
protected void addStatusControls(final Composite composite) {
fStatusControl = new PreviewCanvas(composite) {
@Override
protected void drawPreview(final GC gc, final int idx,
final int x, final int y, final int width, final int height) {
Color color = null;
boolean alpha = false;
switch (idx) {
case 0:
if (fInitialValue != null) {
alpha = (fInitialValue instanceof ColorAlphaDef);
if (fInitialSWTColor == null) {
fInitialSWTColor = (alpha) ?
RAlphaChooser.createPreviewColor(getDisplay(), ((ColorAlphaDef) fInitialValue).getAlpha255(), fInitialValue) :
createPreviewColor(getDisplay(), fInitialValue);
}
color = fInitialSWTColor;
}
break;
case 1:
if (fCurrentValue != null) {
alpha = (fCurrentValue instanceof ColorAlphaDef);
if (fCurrentSWTColor == null) {
fCurrentSWTColor = (alpha) ?
RAlphaChooser.createPreviewColor(getDisplay(), ((ColorAlphaDef) fCurrentValue).getAlpha255(), fCurrentValue) :
createPreviewColor(getDisplay(), fCurrentValue);
}
color = fCurrentSWTColor;
}
break;
default:
break;
}
if (alpha) {
RAlphaChooser.drawPreview(gc, x, y, width, height, color);
}
else {
RColorChooser.drawPreview(gc, x, y, width, height, color);
}
}
};
updateStatus();
}
private void doSetColorValue(final ColorDef value) {
final Float oldAlpha = doGetAlphaValue();
doSetValue((oldAlpha.floatValue() != 1f) ?
new ColorAlphaDef(value, oldAlpha.floatValue()) :
value );
}
private void doSetAlphaValue(final Float value) {
final ColorDef oldValue = doGetColorValue();
doSetValue((value.floatValue() == 1f) ? oldValue :
new ColorAlphaDef(oldValue, value.floatValue()) );
}
private void doSetValue(final ColorDef value) {
if (value == null || value == fCurrentValue) {
return;
}
if (fCurrentSWTColor != null && !value.equals(fCurrentValue)) {
fCurrentSWTColor.dispose();
fCurrentSWTColor = null;
}
fCurrentValue = value;
if (fStatusControl != null) {
updateStatus();
}
}
protected void updateStatus() {
final StringBuilder sb = new StringBuilder();
sb.append("Previous: ");
sb.append((fInitialValue != null) ? fInitialValue.toString() : "-"); //$NON-NLS-1$
sb.append("\n");
sb.append("Current: ");
sb.append(fCurrentValue.toString());
// fStatusLabel.setText(info);
fStatusControl.setToolTipText(sb.toString());
fStatusControl.redraw();
}
private ColorDef doGetColorValue() {
if (fCurrentValue instanceof ColorAlphaDef) {
return ((ColorAlphaDef) fCurrentValue).getRef();
}
return fCurrentValue;
}
private Float doGetAlphaValue() {
if (fCurrentValue instanceof ColorAlphaDef) {
return Float.valueOf(((ColorAlphaDef) fCurrentValue).getAlpha());
}
return Float.valueOf(1f);
}
public ColorDef getValue() {
return fCurrentValue;
}
@Override
protected void onDispose() {
fStatusControl = null;
super.onDispose();
if (fInitialSWTColor != null) {
fInitialSWTColor.dispose();
fInitialSWTColor = null;
}
if (fCurrentSWTColor != null) {
fCurrentSWTColor.dispose();
fCurrentSWTColor = null;
}
}
}