blob: 4137f47d29738acdfbc6efecb8c1e04b37f36b73 [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.ui.SharedUIResources;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
public class AlphaSelector extends Canvas implements IObjValueWidget<Float> {
private static final Float DEFAULT_VALUE = new Float(1f);
private static final Color G_BACKGROUND = SharedUIResources.getColors().getColor(SharedUIResources.GRAPHICS_BACKGROUND_COLOR_ID);
private static final ColorDef DEFAULT_BASE = new ColorDef(0, 0, 0);
private final static class Data {
private final int size;
private final float factor;
private final ColorDef baseColor;
private final int alphaX0;
private final int alphaX1;
private final int y0;
private final int y1;
public Data(final int size, final ColorDef baseColor) {
this.size = size;
this.baseColor = baseColor;
factor = size - 1;
alphaX0 = 1;
alphaX1 = alphaX0 + Math.round(size * 0.15f);
y0 = 1;
y1 = y0 + size;
}
public int alpha_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 alpha_255_y(final int v) {
return y0 + Math.round((v / 255f) * factor);
}
public float alpha_y_01(final int y) {
final float v = 1f - ((y - y0) / factor);
if (v <= 0f) {
return 0f;
}
if (v >= 1f) {
return 1f;
}
return v;
}
public int alpha_01_y(final float v) {
return y0 + Math.round(((1f - v) * factor));
}
public int getBaseMax() {
return Math.max(baseColor.getRed(), Math.max(baseColor.getGreen(), baseColor.getBlue()));
}
public Image createImage(final Display display) {
final Image image = new Image(display, alphaX1 + 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
if (baseColor.equalsRGB(DEFAULT_BASE)) {
final int x1 = alphaX1 - 1;
for (int y = y0; y < y1; y++) {
final int alpha255 = 255 - alpha_y_255(y);
final Color color = new Color(display, alpha255, alpha255, alpha255);
gc.setForeground(color);
gc.drawLine(alphaX0, y, x1, y);
color.dispose();
}
}
else {
final int x1 = alphaX1 - 1;
for (int y = y0; y < y1; y++) {
final int alpha255 = alpha_y_255(y);
final int white = 255 - alpha255;
final Color color = new Color(display,
white + (baseColor.getRed() * alpha255) / 255,
white + (baseColor.getGreen() * alpha255) / 255,
white + (baseColor.getBlue() * alpha255) / 255 );
gc.setForeground(color);
gc.drawLine(alphaX0, y, x1, y);
color.dispose();
}
}
gc.dispose();
return image;
}
}
private class SWTListener implements PaintListener, Listener {
private static final int TRACK_ALPHA = 1;
private Data fData;
private Image fImage;
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
&& event.x >= data.alphaX0 && event.x < data.alphaX1) {
doSetValue(Float.valueOf(data.alpha_y_01(event.y)), event.time, 0);
fMouseState = TRACK_ALPHA;
}
return;
case SWT.MouseUp:
fMouseState = 0;
return;
case SWT.MouseMove:
if (data != null) {
switch (fMouseState) {
case TRACK_ALPHA:
doSetValue(Float.valueOf(data.alpha_y_01(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 = Math.round(width / 0.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.baseColor.equalsRGB(fBaseColor)) {
if (fImage != null) {
fImage.dispose();
fImage = null;
}
fData = new Data(size, fBaseColor);
}
final GC gc = e.gc;
gc.setAdvanced(false);
gc.setBackground(G_BACKGROUND);
gc.fillRectangle(clientArea);
// if (fImage == null) {
if (fImage != null) {
fImage.dispose();
fImage = null;
}
fImage = fData.createImage(e.display);
// }
gc.drawImage(fImage, 0, 0);
gc.setLineWidth(1);
gc.setAdvanced(true);
gc.setAntialias(SWT.ON);
{ final float alpha = fValue.floatValue();
final int y = fData.alpha_01_y(alpha);
gc.setForeground(e.display.getSystemColor(SWT.COLOR_BLACK));
gc.drawLine(fData.alphaX0 - 1, y, fData.alphaX1, y);
if (255 * (1f - alpha) + (fData.getBaseMax() * alpha) < 127) {
gc.setForeground(e.display.getSystemColor(SWT.COLOR_WHITE));
gc.drawLine(fData.alphaX0, y, fData.alphaX1 - 1, y);
}
}
}
}
private int fSize = 8 + LayoutUtils.defaultHSpacing() * 30;
private Float fValue = DEFAULT_VALUE;
private final FastList<IObjValueListener<Float>> fValueListeners= (FastList) new FastList<>(IObjValueListener.class);
private ColorDef fBaseColor = DEFAULT_BASE;
public AlphaSelector(final Composite parent) {
super(parent, SWT.DOUBLE_BUFFERED);
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;
}
public void setBaseColor(final ColorDef color) {
fBaseColor = (color != null) ? color : DEFAULT_BASE;
redraw();
}
private boolean doSetValue(final Float newValue, final int time, final int flags) {
if (fValue.equals(newValue) && flags == 0 && fValue != DEFAULT_VALUE) {
return false;
}
final IObjValueListener<Float>[] listeners = fValueListeners.toArray();
final ObjValueEvent<Float> 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 * 0.15f);
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<Float> getValueType() {
return Float.class;
}
@Override
public void addValueListener(final IObjValueListener<Float> listener) {
fValueListeners.add(listener);
}
@Override
public void removeValueListener(final IObjValueListener<Float> listener) {
fValueListeners.remove(listener);
}
@Override
public Float 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 Float value) {
if (idx != 0) {
throw new IllegalArgumentException("idx: " + idx); //$NON-NLS-1$
}
if (value == null) {
throw new NullPointerException("value"); //$NON-NLS-1$
}
doSetValue(value, 0, 0);
}
}