blob: bae3f875ffd351c2b052d240ae0837bf61926a46 [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.ecommons.ui.components;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.statet.ecommons.collections.FastList;
import org.eclipse.statet.ecommons.graphics.core.ColorDef;
import org.eclipse.statet.ecommons.graphics.core.HSVColorDef;
import org.eclipse.statet.ecommons.ui.SharedUIResources;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
public class RGBSelector extends Canvas implements IObjValueWidget<ColorDef> {
private static final byte RED = 0x0;
private static final byte GREEN = 0x1;
private static final byte BLUE = 0x2;
private static final ColorDef DEFAULT_VALUE = new HSVColorDef(1, 0, 0);
private static final Color G_BACKGROUND = SharedUIResources.getColors().getColor(SharedUIResources.GRAPHICS_BACKGROUND_COLOR_ID);
private final static class Data {
private final int size;
private final float factor;
private final int primX0;
private final int primX1;
private final int rectX0;
private final int rectX1;
private final int y0;
private final int y1;
public Data(final int size) {
this.size = size;
factor = size - 1;
primX0 = 1;
primX1 = primX0 + Math.round(size * 0.15f);
rectX0 = primX1 + LayoutUtils.defaultHSpacing();
rectX1 = rectX0 + size;
y0 = 1;
y1 = y0 + size;
}
public int prim_y_255(final int y) {
final int v = 255 - Math.round(((y - y0) / factor) * 255f);
if (v <= 0) {
return 0;
}
if (v >= 255) {
return 255;
}
return v;
}
public int rect_x_255(final int x) {
final int v = Math.round(((x - rectX0) / factor) * 255f);
if (v <= 0) {
return 0;
}
if (v >= 255) {
return 255;
}
return v;
}
public int rect_y_255(final int y) {
final int v = 255 - Math.round(((y - y0) / factor) * 255f);
if (v <= 0) {
return 0;
}
if (v >= 255) {
return 255;
}
return v;
}
public int prim_255_y(final int v) {
return y0 + Math.round(((255 - v) / 255f) * factor);
}
public int rect_255_x(final int v) {
return rectX0 + Math.round((v / 255f) * factor);
}
public int rect_255_y(final int v) {
return y0 + Math.round(((255 - v) / 255f) * factor);
}
public Image createImage(final Display display, final byte primColor, final int primValue, final byte xColor, final byte yColor) {
final Image image = new Image(display, rectX1 + 1, y1 + 1);
final GC gc = new GC(image);
gc.setAdvanced(false);
gc.setBackground(G_BACKGROUND);
gc.fillRectangle(0, 0, image.getImageData().width, image.getImageData().height);
// prim
final int[] rgb = new int[3];
{ final int x1 = primX1 - 1;
for (int y = y0; y < y1; y++) {
rgb[primColor] = prim_y_255(y);
final Color color = new Color(display, rgb[0], rgb[1], rgb[2]);
gc.setForeground(color);
gc.drawLine(primX0, y, x1, y);
color.dispose();
}
}
// rect
rgb[primColor] = primValue;
for (int y = y0; y < y1; y++) {
rgb[yColor] = rect_y_255(y);
for (int x = rectX0; x < rectX1; x++) {
rgb[xColor] = rect_x_255(x);
final Color color = new Color(display, rgb[0], rgb[1], rgb[2]);
gc.setForeground(color);
gc.drawPoint(x, y);
color.dispose();
}
}
gc.dispose();
return image;
}
}
private class SWTListener implements PaintListener, Listener {
private static final int TRACK_PRIM = 1;
private static final int TRACK_RECT = 2;
private Data fData;
private Image fImage;
private int fImagePrim;
private int fImagePrimValue;
private int fMouseState;
@Override
public void handleEvent(final Event event) {
final Data data = fData;
switch (event.type) {
case SWT.MouseDown:
if (data != null && event.y >= data.y0 && event.y < data.y1) {
if (event.x >= data.primX0 && event.x < data.primX1) {
doSetValue(createColor(fCurrentPrim, data.prim_y_255(event.y)),
event.time, 0 );
fMouseState = TRACK_PRIM;
}
else if (event.x >= data.rectX0 && event.x < data.rectX1) {
doSetValue(createColor(fCurrentRectX, data.rect_x_255(event.x),
fCurrentRectY, data.rect_y_255(event.y)),
event.time, 0 );
fMouseState = TRACK_RECT;
}
}
return;
case SWT.MouseUp:
fMouseState = 0;
return;
case SWT.MouseMove:
if (data != null) {
switch (fMouseState) {
case TRACK_PRIM:
doSetValue(createColor(fCurrentPrim, data.prim_y_255(event.y)),
event.time, 0 );
break;
case TRACK_RECT:
doSetValue(createColor(fCurrentRectX, data.rect_x_255(event.x),
fCurrentRectY, data.rect_y_255(event.y)),
event.time, 0 );
break;
}
}
return;
case SWT.Dispose:
if (fImage != null) {
fImage.dispose();
fImage = null;
}
}
}
private int computeSize(int width, final int height) {
width -= LayoutUtils.defaultHSpacing();
width = Math.round(width / 1.15f);
return Math.min(width - 2, height - 2);
}
@Override
public void paintControl(final PaintEvent e) {
final Rectangle clientArea = getClientArea();
int size = computeSize(clientArea.width, clientArea.height);
if (fSize > 0 && fSize < size) {
size = fSize;
}
if (fData == null || fData.size != size) {
fData = new Data(size);
}
final GC gc = e.gc;
gc.setAdvanced(true);
gc.setAntialias(SWT.OFF);
final int currentAlpha = 255;
gc.setAlpha(currentAlpha);
gc.setBackground(G_BACKGROUND);
gc.fillRectangle(clientArea);
final int primValue = getComponent(fCurrentPrim);
if (fImage == null || fImagePrim != fCurrentPrim || fImagePrimValue != primValue) {
if (fImage != null) {
fImage.dispose();
}
fImage = fData.createImage(e.display, fCurrentPrim, primValue,
fCurrentRectX, fCurrentRectY );
fImagePrim = fCurrentPrim;
fImagePrimValue = primValue;
}
gc.drawImage(fImage, 0, 0);
gc.setLineWidth(1);
gc.setAdvanced(true);
gc.setAntialias(SWT.ON);
{ final int y = fData.prim_255_y(primValue);
gc.setForeground(e.display.getSystemColor(SWT.COLOR_BLACK));
gc.drawLine(fData.primX0 - 1, y, fData.primX1, y);
if (primValue < 127) {
gc.setForeground(e.display.getSystemColor(SWT.COLOR_WHITE));
gc.drawLine(fData.primX0, y, fData.primX1 - 1, y);
}
}
{ final int x = fData.rect_255_x(getComponent(fCurrentRectX));
final int y = fData.rect_255_y(getComponent(fCurrentRectY));
gc.setForeground(e.display.getSystemColor(
(Math.max(Math.max(fValue.getRed(), fValue.getGreen()), fValue.getBlue()) < 127) ? SWT.COLOR_WHITE : SWT.COLOR_BLACK ));
gc.drawOval(x - 1, y - 1, 3, 3);
}
}
}
private int fSize = 8 + LayoutUtils.defaultHSpacing() * 30;
private ColorDef fValue = DEFAULT_VALUE;
private byte fCurrentPrim;
private byte fCurrentRectX;
private byte fCurrentRectY;
private final FastList<IObjValueListener<ColorDef>> fValueListeners= (FastList) new FastList<>(IObjValueListener.class);
public RGBSelector(final Composite parent) {
super(parent, SWT.DOUBLE_BUFFERED);
setPrimary(RED);
final SWTListener listener = new SWTListener();
addPaintListener(listener);
addListener(SWT.MouseDown, listener);
addListener(SWT.MouseUp, listener);
addListener(SWT.MouseMove, listener);
addListener(SWT.Dispose, listener);
}
public void setSize(final int size) {
fSize = size;
}
private int getComponent(final byte c) {
switch (c) {
case RED:
return fValue.getRed();
case GREEN:
return fValue.getGreen();
case BLUE:
return fValue.getBlue();
default:
throw new IllegalArgumentException();
}
}
private ColorDef createColor(final byte color, final int value) {
final int[] rgb = new int[] { fValue.getRed(), fValue.getGreen(), fValue.getBlue() };
rgb[color] = value;
return new ColorDef(rgb[0], rgb[1], rgb[2]);
}
private ColorDef createColor(final byte change1, final int value1, final byte change2, final int value2) {
final int[] rgb = new int[] { fValue.getRed(), fValue.getGreen(), fValue.getBlue() };
rgb[change1] = value1;
rgb[change2] = value2;
return new ColorDef(rgb[0], rgb[1], rgb[2]);
}
private boolean doSetValue(final ColorDef newValue, final int time, final int flags) {
if (fValue.equals(newValue) && flags == 0 && fValue != DEFAULT_VALUE) {
return false;
}
final IObjValueListener<ColorDef>[] listeners = fValueListeners.toArray();
final ObjValueEvent<ColorDef> event= new ObjValueEvent<>(this, time, 0,
fValue, newValue, flags);
fValue = newValue;
for (int i = 0; i < listeners.length; i++) {
event.newValue = newValue;
listeners[i].valueChanged(event);
}
if (!isDisposed()) {
redraw();
}
return true;
}
@Override
public Point computeSize(final int wHint, final int hHint, final boolean changed) {
int width = 2 + Math.round(fSize * 1.15f) + LayoutUtils.defaultHSpacing();
int height = 2 + fSize;
final int border = getBorderWidth();
width += border * 2;
height += border * 2;
return new Point(width, height);
}
@Override
public Control getControl() {
return this;
}
@Override
public Class<ColorDef> getValueType() {
return ColorDef.class;
}
@Override
public void addValueListener(final IObjValueListener<ColorDef> listener) {
fValueListeners.add(listener);
}
@Override
public void removeValueListener(final IObjValueListener<ColorDef> listener) {
fValueListeners.remove(listener);
}
@Override
public ColorDef getValue(final int idx) {
if (idx != 0) {
throw new IllegalArgumentException("idx: " + idx); //$NON-NLS-1$
}
return fValue;
}
@Override
public void setValue(final int idx, final ColorDef value) {
if (idx != 0) {
throw new IllegalArgumentException("idx: " + idx); //$NON-NLS-1$
}
if (value.getType() == "rgb") { //$NON-NLS-1$
doSetValue(value, 0, 0);
}
else {
doSetValue(new ColorDef(value), 0, 0);
}
}
public void setPrimary(final int color) {
switch (color) {
case RED:
fCurrentPrim = RED;
fCurrentRectX = GREEN;
fCurrentRectY = BLUE;
break;
case GREEN:
fCurrentPrim = GREEN;
fCurrentRectX = BLUE;
fCurrentRectY = RED;
break;
case BLUE:
fCurrentPrim = BLUE;
fCurrentRectX = RED;
fCurrentRectY = GREEN;
break;
default:
throw new IllegalArgumentException();
}
redraw();
}
}