package com.peterhi.ui.bar2;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.swt.SWT;
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.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.jdesktop.core.animation.timing.Animator;
import org.jdesktop.core.animation.timing.TimingTarget;
import org.jdesktop.core.animation.timing.TimingTargetAdapter;
import com.peterhi.runtime.RT;
import com.peterhi.ui.UI;
import com.peterhi.ui.UIImageResource;
public class StripBar4 extends Canvas implements Listener {
static class BackgroundChangeTimingTarget extends TimingTargetAdapter {
private final StripItem item;
private final RGB from;
private final RGB to;
public BackgroundChangeTimingTarget(StripItem item, RGB from, RGB to) {
if (item == null || from == null || to == null) {
throw new NullPointerException();
}
this.item = item;
this.from = from;
this.to = to;
}
@Override
public void timingEvent(Animator source, double fraction) {
int r = (int )Math.round((to.red - from.red) * fraction);
int g = (int )Math.round((to.green - from.green) * fraction);
int b = (int )Math.round((to.blue - from.blue) * fraction);
RGB color = new RGB(from.red + r, from.green + g, from.blue + b);
setColor(color);
}
@Override
public void end(Animator source) {
setColor(to);
}
private void setColor(RGB color) {
StripBar4 parent = item.getParent();
Rectangle bounds = item.getBounds();
item.background = color;
parent.redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
}
}
public static class StripItem {
private StripBar4 parent;
private final String id;
private String text;
private Image image;
Animator animator;
RGB foreground;
RGB background;
public StripItem(String id) {
this.id = id;
}
public StripBar4 getParent() {
return parent;
}
void setParent(StripBar4 value) {
parent = value;
}
public String getId() {
return id;
}
public String getText() {
return text;
}
public void setText(String value) {
text = value;
}
public Image getImage() {
return image;
}
public void setImage(Image value) {
image = value;
}
public void dispose() {
if (parent != null) {
parent.remove(this);
}
}
public int getIndex() {
if (parent != null) {
return parent.indexOf(this);
}
return -1;
}
public StripItem getPreviousItem() {
if (parent != null) {
int index = getIndex();
return parent.getItem(index - 1);
}
return null;
}
public StripItem getNextItem() {
if (parent != null) {
int index = getIndex();
return parent.getItem(index + 1);
}
return null;
}
public Rectangle getBounds() {
if (parent != null) {
return parent.getBounds(this);
}
return null;
}
public Rectangle getImageBounds() {
if (parent != null) {
return parent.getImageBounds(this);
}
return null;
}
public Rectangle getTextBounds() {
if (parent != null) {
return parent.getTextBounds(this);
}
return null;
}
public Rectangle getCloseButtonBounds() {
if (parent != null) {
return parent.getCloseButtonBounds(this);
}
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StripItem other = (StripItem) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString() {
return MessageFormat.format("{0} ({1})", text, id);
}
}
public static final int SPACING = 6;
public static final RGB FORE_OFF = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND).getRGB();
public static final RGB BACK_OFF = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGB();
public static final RGB FORE_ON = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND).getRGB();
public static final RGB BACK_ON = new RGB(72, 118, 255);
public static final RGB FORE_DOWN = Display.getDefault().getSystemColor(SWT.COLOR_BLACK).getRGB();
public static final RGB BACK_DOWN = new RGB(65, 105, 225);
public static final RGB FORE_UP = Display.getDefault().getSystemColor(SWT.COLOR_WHITE).getRGB();
public static final RGB BACK_UP = new RGB(67, 110, 238);
private final List<StripItem> items = new ArrayList<StripItem>();
private StripItem hovered;
private StripItem pressed;
private Set<StripItem> selected = new HashSet<StripItem>();
public StripBar4(Composite parent, int style) {
super(parent, style);
addListener(SWT.Paint, this);
addListener(SWT.MouseEnter, this);
addListener(SWT.MouseExit, this);
addListener(SWT.MouseDown, this);
addListener(SWT.MouseMove, this);
addListener(SWT.MouseUp, this);
}
@Override
public void handleEvent(Event event) {
if (equals(event.widget)) {
if (event.type == SWT.Paint) {
for (StripItem item : items) {
paintStripItem(event.gc, item);
}
} else if (event.type == SWT.MouseEnter || event.type == SWT.MouseMove || event.type == SWT.MouseExit) {
onHoverItem(event.x, event.y);
} else if (event.type == SWT.MouseDown) {
onPressItem(event.x, event.y);
} else if (event.type == SWT.MouseUp) {
onSelectItem(event.x, event.y);
}
}
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
Point size = new Point(0, 0);
for (StripItem item : items) {
Rectangle bounds = item.getBounds();
size.x += bounds.width;
size.y = Math.max(size.y, bounds.height);
}
size.x += SPACING * (items.size() - 1);
size.x += SPACING * 2;
size.y += SPACING * 2;
Rectangle boundsWithTrim = computeTrim(0, 0, size.x, size.y);
size.x = boundsWithTrim.width;
size.y = boundsWithTrim.height;
return size;
}
public boolean add(StripItem item) {
if (item == null) {
throw new NullPointerException();
}
if (items.contains(item)) {
return false;
}
items.add(item);
item.setParent(this);
return true;
}
public boolean insert(StripItem item, int index) {
if (item == null) {
throw new NullPointerException();
}
if (items.contains(item)) {
return false;
}
items.add(index, item);
item.setParent(this);
return true;
}
public boolean remove(StripItem item) {
if (item == null) {
throw new NullPointerException();
}
if (!items.contains(item)) {
return false;
}
items.remove(item);
item.setParent(null);
return true;
}
public int getItemCount() {
return items.size();
}
public StripItem[] getItems() {
return items.toArray(new StripItem[items.size()]);
}
public int indexOf(StripItem item) {
if (item == null) {
throw new NullPointerException();
}
return items.indexOf(item);
}
public StripItem getItem(int index) {
if (index < 0 || index >= items.size()) {
return null;
}
return items.get(index);
}
public StripItem getItem(int x, int y) {
for (StripItem item : items) {
Rectangle bounds = item.getBounds();
if (bounds.contains(x, y)) {
return item;
}
}
return null;
}
public Rectangle getBounds(StripItem destItem) {
if (destItem == null) {
throw new NullPointerException();
}
if (!items.contains(destItem)) {
return null;
}
GC gc = new GC(this);
Rectangle bounds = new Rectangle(SPACING, SPACING, 0, 0);
for (StripItem srcItem : items) {
if (srcItem.getImage() != null) {
Rectangle imageBounds = srcItem.getImage().getBounds();
bounds.height = Math.max(bounds.height, imageBounds.height);
}
if (srcItem.getText() != null && !srcItem.getText().isEmpty()) {
Point textSize = gc.stringExtent(srcItem.getText());
bounds.height = Math.max(bounds.height, textSize.y);
}
Image closeButtonImage = UIImageResource.T.closeNormal10();
Rectangle closeButtonImageBounds = closeButtonImage.getBounds();
bounds.height = Math.max(bounds.height, closeButtonImageBounds.height);
}
bounds.height += SPACING * 2;
for (StripItem srcItem : items) {
int extent = SPACING;
if (srcItem.getImage() != null) {
Rectangle imageBounds = srcItem.getImage().getBounds();
extent += imageBounds.width;
extent += SPACING;
}
if (srcItem.getText() != null && !srcItem.getText().isEmpty()) {
Point textSize = gc.stringExtent(srcItem.getText());
extent += textSize.x;
extent += SPACING;
}
Image closeButtonImage = UIImageResource.T.closeNormal10();
Rectangle closeButtonImageBounds = closeButtonImage.getBounds();
extent += closeButtonImageBounds.width;
extent += SPACING;
if (srcItem.equals(destItem)) {
bounds.width = extent;
break;
} else {
bounds.x += extent;
bounds.x += SPACING;
}
}
gc.dispose();
return bounds;
}
public Rectangle getImageBounds(StripItem item) {
Rectangle bounds = getBounds(item);
if (bounds == null) {
return null;
}
bounds.y += bounds.height / 2;
bounds.width = 0;
bounds.height = 0;
if (item.getImage() != null) {
Rectangle imageBounds = item.getImage().getBounds();
bounds.x += SPACING;
bounds.y -= imageBounds.height / 2;
bounds.width = imageBounds.width;
bounds.height = imageBounds.height;
}
return bounds;
}
public Rectangle getTextBounds(StripItem item) {
Rectangle bounds = getImageBounds(item);
if (bounds == null) {
return null;
}
bounds.x += bounds.width;
bounds.y += bounds.height / 2;
bounds.width = 0;
bounds.height = 0;
if (item.getText() != null && !item.getText().isEmpty()) {
GC gc = new GC(this);
Point textSize = gc.stringExtent(item.getText());
bounds.x += SPACING;
bounds.y -= textSize.y / 2;
bounds.width = textSize.x;
bounds.height = textSize.y;
gc.dispose();
}
return bounds;
}
public Rectangle getCloseButtonBounds(StripItem item) {
Rectangle bounds = getTextBounds(item);
if (bounds == null) {
return null;
}
bounds.x += bounds.width;
bounds.y += bounds.height / 2;
bounds.width = 0;
bounds.height = 0;
Image closeButtonImage = UIImageResource.T.closeNormal10();
Rectangle closeButtonImageBounds = closeButtonImage.getBounds();
bounds.x += SPACING;
bounds.y -= closeButtonImageBounds.height / 2;
bounds.width = closeButtonImageBounds.width;
bounds.height = closeButtonImageBounds.height;
return bounds;
}
private void paintStripItem(GC gc, StripItem item) {
paintStripItemBackground(gc, item);
paintStripItemImage(gc, item);
paintStripItemText(gc, item);
paintStripItemCloseButton(gc, item);
paintStripItemFrame(gc, item);
}
private void paintStripItemBackground(GC gc, StripItem item) {
Display display = getDisplay();
Color oldBackground = gc.getBackground();
Color newBackground;
if (item.background == null) {
Color defaultBackground = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
newBackground = new Color(display, defaultBackground.getRGB());
} else {
newBackground = new Color(display, item.background);
}
gc.setBackground(newBackground);
Rectangle bounds = item.getBounds();
gc.fillRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
gc.setBackground(oldBackground);
newBackground.dispose();
}
private void paintStripItemImage(GC gc, StripItem item) {
if (item.getImage() == null) {
return;
}
Rectangle bounds = item.getImageBounds();
if (bounds == null) {
return;
}
gc.drawImage(item.getImage(), bounds.x, bounds.y);
}
private void paintStripItemText(GC gc, StripItem item) {
if (item.getText() == null || item.getText().isEmpty()) {
return;
}
Rectangle bounds = item.getTextBounds();
if (bounds == null) {
return;
}
Display display = getDisplay();
Color oldForeground = gc.getForeground();
Color newForeground;
if (item.foreground == null) {
Color defaultForeground = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
newForeground = new Color(display, defaultForeground.getRGB());
} else {
newForeground = new Color(display, item.foreground);
}
gc.setForeground(newForeground);
gc.drawText(item.getText(), bounds.x, bounds.y, true);
gc.setForeground(oldForeground);
newForeground.dispose();
}
private void paintStripItemCloseButton(GC gc, StripItem item) {
Rectangle bounds = item.getCloseButtonBounds();
if (bounds == null) {
return;
}
Image closeButtonImage = UIImageResource.T.closeNormal10();
gc.drawImage(closeButtonImage, bounds.x, bounds.y);
}
private void paintStripItemFrame(GC gc, StripItem item) {
Rectangle bounds = item.getBounds();
if (bounds == null) {
return;
}
Color oldForeground = gc.getForeground();
gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
gc.setForeground(oldForeground);
}
private void onHoverItem(int x, int y) {
StripItem old = hovered;
StripItem neo = getItem(x, y);
if (RT.equals(old, neo)) {
return;
}
if (old != null) {
if (old.animator != null) {
old.animator.cancel();
old.animator = null;
}
if (old.background == null) {
old.background = BACK_ON;
}
if (!RT.equals(old, pressed) && !selected.contains(old)) {
TimingTarget target = new BackgroundChangeTimingTarget(old, old.background, BACK_OFF);
old.animator = new Animator.Builder().addTarget(target)
.setDuration(160, TimeUnit.MILLISECONDS).build();
old.animator.start();
}
}
if (neo != null) {
if (neo.animator != null) {
neo.animator.cancel();
neo.animator = null;
}
if (neo.background == null) {
neo.background = BACK_OFF;
}
if (!RT.equals(neo, pressed) && !selected.contains(neo)) {
TimingTarget target = new BackgroundChangeTimingTarget(neo, neo.background, BACK_ON);
neo.animator = new Animator.Builder().addTarget(target)
.setDuration(160, TimeUnit.MILLISECONDS).build();
neo.animator.start();
}
}
hovered = neo;
}
private void onPressItem(int x, int y) {
StripItem item = getItem(x, y);
if (item != null) {
item.foreground = FORE_DOWN;
item.background = BACK_DOWN;
Rectangle bounds = item.getBounds();
redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
}
pressed = item;
}
private void onSelectItem(int x, int y) {
if (pressed != null) {
boolean multi = (getStyle() & SWT.MULTI) == SWT.MULTI;
if (multi) {
if (selected.contains(pressed)) {
pressed.foreground = FORE_ON;
pressed.background = BACK_ON;
selected.remove(pressed);
} else {
pressed.foreground = FORE_UP;
pressed.background = BACK_UP;
selected.add(pressed);
}
Rectangle bounds = pressed.getBounds();
redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
} else {
if (!selected.contains(pressed)) {
for (StripItem item : selected) {
item.foreground = FORE_OFF;
item.background = BACK_OFF;
Rectangle bounds = item.getBounds();
redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
}
}
selected.clear();
pressed.foreground = FORE_UP;
pressed.background = BACK_UP;
selected.add(pressed);
Rectangle bounds = pressed.getBounds();
redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
}
}
pressed = null;
}
public static void main(String[] args) {
UI.initialize();
Display display = Display.getDefault();
Shell shell = new Shell(display);
StripBar4 bar = new StripBar4(shell, SWT.DOUBLE_BUFFERED);
GridLayout layout = new GridLayout();
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginWidth = 0;
layout.marginHeight = 0;
shell.setLayout(layout);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
data.grabExcessHorizontalSpace = true;
bar.setLayoutData(data);
StripItem item0 = new StripItem("item0");
item0.setText("Item 0");
item0.setImage(UIImageResource.T.about16());
StripItem item1 = new StripItem("item1");
item1.setText("Item 1");
StripItem item2 = new StripItem("item2");
item2.setImage(UIImageResource.T.addContact16());
StripItem item3 = new StripItem("item3");
bar.add(item0);
bar.add(item1);
bar.add(item2);
bar.add(item3);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
UI.destroy();
}
}