/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.utils.ui.color; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; /** * * Widget to edit colors * * @author Marko Luukkainen * */ public class ColorComposite extends Composite{ private Color color; private ColorGradient rGradient; private ColorGradient gGradient; private ColorGradient bGradient; private ColorGradient hGradient; private ColorGradient sGradient; private ColorGradient vGradient; private ColorGradient colorGradient; TabFolder tabFolder; Text rText; Text gText; Text bText; IntGradientWidget rCanvas; IntGradientWidget gCanvas; IntGradientWidget bCanvas; ColorGradientCanvas colorCanvas; Text hText; Text sText; Text vText; GradientWidget hCanvas; GradientWidget sCanvas; GradientWidget vCanvas; Button dynamicButton; boolean dynamic = true; public ColorComposite(Composite parent, int style) { super(parent,style); GridLayout layout = new GridLayout(); layout.makeColumnsEqualWidth = true; layout.numColumns = 1; this.setLayout(layout); colorCanvas = new ColorGradientCanvas(this,SWT.BORDER|SWT.HORIZONTAL); tabFolder = new TabFolder(this, SWT.NONE); TabItem rgbTab = new TabItem(tabFolder, SWT.NONE); rgbTab.setText("RGB"); Composite rgbComposite = new Composite(tabFolder, SWT.NONE); rgbTab.setControl(rgbComposite); Label rLabel = new Label(rgbComposite,SWT.NONE); rLabel.setText("Red"); rCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL){ @Override protected void updatePosition(int value) { Color newColor = new Color(value,color.getG(),color.getB()); setColor(newColor); } }; rText = new Text(rgbComposite,SWT.BORDER); rText.addModifyListener(new IntColorModifyListener() { @Override void setValue(int value) { Color newColor = new Color(value,color.getG(),color.getB()); setColor(newColor); } }); Label gLabel = new Label(rgbComposite,SWT.NONE); gLabel.setText("Green"); gCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL) { @Override protected void updatePosition(int value) { Color newColor = new Color(color.getR(),value,color.getB()); setColor(newColor); } }; gText = new Text(rgbComposite,SWT.BORDER); gText.addModifyListener(new IntColorModifyListener() { @Override void setValue(int value) { Color newColor = new Color(color.getR(),value,color.getB()); setColor(newColor); } }); Label bLabel = new Label(rgbComposite,SWT.NONE); bLabel.setText("Blue"); bCanvas = new IntGradientWidget(rgbComposite,SWT.BORDER|SWT.HORIZONTAL) { @Override protected void updatePosition(int value) { Color newColor = new Color(color.getR(),color.getG(),value); setColor(newColor); } }; bText = new Text(rgbComposite,SWT.BORDER); bText.addModifyListener(new IntColorModifyListener() { @Override void setValue(int value) { Color newColor = new Color(color.getR(),color.getG(),value); setColor(newColor); } }); TabItem hsvTab = new TabItem(tabFolder, SWT.NONE); hsvTab.setText("HSV"); Composite hsvComposite = new Composite(tabFolder, SWT.NONE); hsvTab.setControl(hsvComposite); Label hLabel = new Label(hsvComposite,SWT.NONE); hLabel.setText("Hue"); hCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { @Override protected boolean updatePosition(double d) { Color newColor = new Color(d*360.0,color.getS(),color.getV()); setColor(newColor); return true; } @Override protected void setPosition(double d) { super.setPosition(d/360.0); } }; hText = new Text(hsvComposite,SWT.BORDER); hText.addModifyListener(new DoubleColorModifyListener(0.,360.) { @Override void setValue(double value) { Color newColor = new Color(value,color.getS(),color.getV()); setColor(newColor); } }); Label sLabel = new Label(hsvComposite,SWT.NONE); sLabel.setText("Saturation"); sCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { @Override protected boolean updatePosition(double d) { Color newColor = new Color(color.getH(),d,color.getV()); setColor(newColor); return true; } }; sText = new Text(hsvComposite,SWT.BORDER); sText.addModifyListener(new DoubleColorModifyListener(0.,1.) { @Override void setValue(double value) { Color newColor = new Color(color.getH(),value,color.getV()); setColor(newColor); } }); Label vLabel = new Label(hsvComposite,SWT.NONE); vLabel.setText("Value"); vCanvas = new GradientWidget(hsvComposite,SWT.BORDER|SWT.HORIZONTAL) { @Override protected boolean updatePosition(double d) { Color newColor = new Color(color.getH(),color.getS(),d); setColor(newColor); return true; } }; vText = new Text(hsvComposite,SWT.BORDER); vText.addModifyListener(new DoubleColorModifyListener(0.,1.) { @Override void setValue(double value) { Color newColor = new Color(color.getH(),color.getS(),value); setColor(newColor); } }); TabItem settingsTab = new TabItem(tabFolder, SWT.NONE); settingsTab.setText("Settings"); Composite settingsComposite = new Composite(tabFolder, SWT.NONE); settingsTab.setControl(settingsComposite); dynamicButton = new Button(settingsComposite, SWT.CHECK); dynamicButton.setText("Dynamic widgets"); dynamicButton.setSelection(dynamic); dynamicButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { dynamic = dynamicButton.getSelection(); updateWidgets(); } }); GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(rgbComposite); GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(hsvComposite); GridLayoutFactory.fillDefaults().numColumns(3).equalWidth(false).applyTo(settingsComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(rgbComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(hsvComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(settingsComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(tabFolder); GridDataFactory.fillDefaults().hint(-1, 32).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(colorCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(rCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(gCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(bCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(hCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(sCanvas); GridDataFactory.fillDefaults().hint(-1, 16).grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(vCanvas); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(rText); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(gText); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(bText); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(hText); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(sText); GridDataFactory.fillDefaults().hint(20, 15).grab(false, false).applyTo(vText); if (color == null) setColor(new Color(255,255,255)); else setColor(color); } private abstract class IntColorModifyListener implements ModifyListener { boolean modify = false; @Override public void modifyText(ModifyEvent e) { if (internalUpdate) return; if (modify) return; modify = true; Text text = (Text)e.widget; try { int value = Integer.parseInt(text.getText()); if (value < 0) value = 0; if (value > 255) value = 255; setValue(value); } catch (NumberFormatException err) { } modify = false; } abstract void setValue(int value); } private abstract class DoubleColorModifyListener implements ModifyListener { boolean modify = false; double min; double max; public DoubleColorModifyListener( double min, double max) { this.min = min; this.max = max; } @Override public void modifyText(ModifyEvent e) { if (internalUpdate) return; if (modify) return; modify = true; Text text = (Text)e.widget; try { double value = Integer.parseInt(text.getText()); if (value < min) { value = min; } else if (value > max) value = max; setValue(value); } catch (NumberFormatException err) { } modify = false; } abstract void setValue(double value); } public void setColor(Color color) { this.color = color; updateWidgets(); } public Color getColor() { return color; } private void updateGradients() { if (dynamic) { rGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0,color.getG(),color.getB()),0.0), new ColorValue(new Color(255,color.getG(),color.getB()),1.0)}); gGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getR(),0,color.getB()),0.0), new ColorValue(new Color(color.getR(),255,color.getB()),1.0)}); bGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getR(),color.getG(),0),0.0), new ColorValue(new Color(color.getR(),color.getG(),255),1.0)}); // hue is interpolated along the shortest route, using just 0 and 360 would result in constant color. hGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0.0,color.getS(),color.getV()),0.0), new ColorValue(new Color(90.0,color.getS(),color.getV()),0.25), new ColorValue(new Color(180.0,color.getS(),color.getV()),0.5), new ColorValue(new Color(270.0,color.getS(),color.getV()),0.75), new ColorValue(new Color(360.0,color.getS(),color.getV()),1.0)}, ColorGradient.HSV); sGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getH(),0.0,color.getV()),0.0), new ColorValue(new Color(color.getH(),1.0,color.getV()),1.0)}, ColorGradient.HSV); vGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getH(),color.getS(),0.0),0.0), new ColorValue(new Color(color.getH(),color.getS(),1.0),1.0)}, ColorGradient.HSV); } else { rGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0,0,0),0.0), new ColorValue(new Color(255,0,0),1.0)}); gGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0,0,0),0.0), new ColorValue(new Color(0,255,0),1.0)}); bGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0,0,0),0.0), new ColorValue(new Color(0,0,255),1.0)}); // hue is interpolated along the shortest route, using just 0 and 360 would result in constant color. hGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(0.0,1.0,1.0),0.0), new ColorValue(new Color(90.0,1.0,1.0),0.25), new ColorValue(new Color(180.0,1.0,1.0),0.5), new ColorValue(new Color(270.0,1.0,1.0),0.75), new ColorValue(new Color(360.0,1.0,1.0),1.0)}, ColorGradient.HSV); sGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getH(),0.0,1.0),0.0), new ColorValue(new Color(color.getH(),1.0,1.0),1.0)}, ColorGradient.HSV); vGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getH(),1.0,0.0),0.0), new ColorValue(new Color(color.getH(),1.0,1.0),1.0)}, ColorGradient.HSV); } colorGradient = new ColorGradient(new ColorValue[]{ new ColorValue(new Color(color.getR(),color.getG(),color.getB()),0.0)}); } private boolean internalUpdate = false; private void updateWidgets() { if (internalUpdate) return; internalUpdate = true; updateGradients(); rCanvas.setGradient(rGradient); gCanvas.setGradient(gGradient); bCanvas.setGradient(bGradient); hCanvas.setGradient(hGradient); sCanvas.setGradient(sGradient); vCanvas.setGradient(vGradient); colorCanvas.setGradient(colorGradient); rCanvas.setPosition(color.getR()); gCanvas.setPosition(color.getG()); bCanvas.setPosition(color.getB()); hCanvas.setPosition(color.getH()); sCanvas.setPosition(color.getS()); vCanvas.setPosition(color.getV()); if(!rText.isFocusControl()) rText.setText(Integer.toString(color.getR())); if(!gText.isFocusControl()) gText.setText(Integer.toString(color.getG())); if(!bText.isFocusControl()) bText.setText(Integer.toString(color.getB())); if(!hText.isFocusControl()) hText.setText(Double.toString(color.getH())); if(!sText.isFocusControl()) sText.setText(Double.toString(color.getS())); if(!vText.isFocusControl()) vText.setText(Double.toString(color.getV())); internalUpdate = false; } private abstract static class IntGradientWidget extends GradientWidget { public IntGradientWidget(Composite parent, int style) { super(parent, style); } @Override protected boolean updatePosition(double d) { int value = (int)(d * 255); if (value < 0) value = 0; if (value > 255) value = 255; updatePosition(value); return true; } protected abstract void updatePosition(int value); protected void setPosition(int value) { double d = ((double)value)/255.0; setPosition(d); } } private abstract static class GradientWidget extends ColorGradientCanvas { int pos = -1; double posD = -1.0; int size = 8; int sized2 = 4; public GradientWidget(Composite parent, int style) { super(parent, style); this.addMouseListener(new MouseListener() { @Override public void mouseDown(MouseEvent e) { if (e.button == 1) { update(e); } } @Override public void mouseUp(MouseEvent e) {} @Override public void mouseDoubleClick(MouseEvent e) {} }); this.addMouseMoveListener(new MouseMoveListener() { @Override public void mouseMove(MouseEvent e) { if ((e.stateMask & SWT.BUTTON1)>0) { update(e); } } }); } protected void update(MouseEvent e) { Rectangle r = getClientArea(); double d; if ((style & SWT.HORIZONTAL) > 0) { d = (double)e.x / (double)r.width; } else { d = (double)e.y / (double)r.height; } if (d < 0.0) d = 0.0; else if (d > 1.0) d = 1.0; if (updatePosition(d)) { if ((style & SWT.HORIZONTAL) > 0) { pos = (int)(d*((double)r.width)); } else { pos = (int)(d*((double)r.height)); } posD = d; } } protected abstract boolean updatePosition(double d); protected void setPosition(double d) { if (d == posD) return; _setPosition(d); } private void _setPosition(double d) { Rectangle r = getClientArea(); posD = d; if (posD >= 0.0) { if ((style & SWT.HORIZONTAL) > 0) { pos = (int)(d * (double)r.width); } else { pos = (int)(d * (double)r.height); } } else { pos = -1; } } Rectangle prevClip; @Override protected void paintGradient(GC gc, Rectangle clip) { super.paintGradient(gc, clip); if (!clip.equals(prevClip)) { prevClip = clip; _setPosition(posD); } if (pos >= 0) { org.eclipse.swt.graphics.Color white = new org.eclipse.swt.graphics.Color(gc.getDevice(), 255,255,255); org.eclipse.swt.graphics.Color black = new org.eclipse.swt.graphics.Color(gc.getDevice(), 0, 0, 0); gc.setForeground(black); gc.setBackground(white); int x; int y; if ((style & SWT.HORIZONTAL) > 0) { x = pos; y = clip.height / 2; } else { x = clip.width / 2; y = pos; } gc.fillOval(x-sized2, y-sized2, size, size); gc.drawOval(x-sized2, y-sized2, size, size); white.dispose(); black.dispose(); } } } }