package com.peterhi.ui.examples;
import java.util.HashSet;
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.Point;
import org.eclipse.swt.graphics.Rectangle;
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.TimingSource;
import org.jdesktop.core.animation.timing.TimingTarget;
import org.jdesktop.core.animation.timing.TimingTargetAdapter;
import org.jdesktop.core.animation.timing.interpolators.AccelerationInterpolator;
import org.jdesktop.swt.animation.timing.sources.SWTTimingSource;
public class LineUp {
public static class Button {
private final String id;
private final Rectangle bounds;
private Animation animation;
public Button(String id) {
if (id == null) {
throw new NullPointerException();
}
if (id.isEmpty()) {
throw new IllegalArgumentException();
}
this.id = id;
this.bounds = new Rectangle(0, 0, 0, 0);
}
public String getId() {
return id;
}
public Rectangle getBounds() {
return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
}
public void setBounds(Rectangle value) {
if (value == null) {
throw new NullPointerException();
}
bounds.x = value.x;
bounds.y = value.y;
bounds.width = value.width;
bounds.height = value.height;
}
public boolean hasAnimation() {
return animation != null;
}
public Animation getAnimation() {
return animation;
}
public void setAnimation(Animation value) {
animation = value;
}
@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;
Button other = (Button) 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 id;
}
}
public static class Animation {
private final Shell shell;
private final Button target;
private final int from;
private final int to;
private Animator animator;
public Animation(Shell shell, Button target, int from, int to) {
if (shell == null) {
throw new NullPointerException();
}
if (target == null) {
throw new NullPointerException();
}
this.shell = shell;
this.target = target;
this.from = from;
this.to = to;
}
public int getFrom() {
return from;
}
public int getTo() {
return to;
}
public Shell getShell() {
return shell;
}
public Button getTarget() {
return target;
}
public void start() {
Rectangle bounds = target.getBounds();
TimingTarget tt = new TimingTargetAdapter() {
@Override
public void timingEvent(Animator source, double fraction) {
double doubleDistance = (to - from) * fraction;
int intDistance = (int )Math.round(doubleDistance);
Rectangle oldBounds = target.getBounds();
Rectangle newBounds = new Rectangle(from + intDistance, oldBounds.y, oldBounds.width, oldBounds.height);
target.setBounds(newBounds);
shell.redraw(oldBounds.x, oldBounds.y, oldBounds.width, oldBounds.height, true);
shell.redraw(newBounds.x, newBounds.y, newBounds.width, newBounds.height, true);
}
@Override
public void end(Animator source) {
Rectangle oldBounds = target.getBounds();
Rectangle newBounds = new Rectangle(to, oldBounds.y, oldBounds.width, oldBounds.height);
target.setBounds(newBounds);
shell.redraw(oldBounds.x, oldBounds.y, oldBounds.width, oldBounds.height, true);
shell.redraw(newBounds.x, newBounds.y, newBounds.width, newBounds.height, true);
target.setAnimation(null);
}
};
animator = new Animator.Builder().addTarget(tt).setDuration(160, TimeUnit.MILLISECONDS).setInterpolator(new AccelerationInterpolator(0, 0.9)).build();
animator.start();
}
public void stop() {
if (animator == null) {
return;
}
animator.stop();
}
public void cancel() {
if (animator == null) {
return;
}
animator.cancel();
target.setAnimation(null);
}
}
public static void main(String[] args) {
final Display display = Display.getDefault();
final Shell shell = new Shell(display);
final Button button0 = new Button("Button 0");
final Button button1 = new Button("Button 1");
final Button button2 = new Button("Button 2");
final Button button3 = new Button("Button 3");
final Button button4 = new Button("Button 4");
final Button[] buttons = new Button[] { button0, button1, button2, button3, button4};
TimingSource timingSource = new SWTTimingSource(16, TimeUnit.MILLISECONDS, display);
timingSource.init();
Animator.setDefaultTimingSource(timingSource);
button0.setBounds(new Rectangle(10, 10, 100, 20));
button1.setBounds(new Rectangle(120, 10, 60, 20));
button2.setBounds(new Rectangle(190, 10, 140, 20));
button3.setBounds(new Rectangle(340, 10, 50, 20));
button4.setBounds(new Rectangle(400, 10, 80, 20));
shell.addListener(SWT.Paint, new Listener() {
@Override
public void handleEvent(Event event) {
GC gc = event.gc;
Color foreground = gc.getForeground();
Color background = gc.getBackground();
gc.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
for (Button button : buttons) {
Rectangle bounds = button.getBounds();
gc.fillRectangle(bounds);
gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
}
gc.setForeground(foreground);
gc.setBackground(background);
}
});
Listener listener = new Listener() {
private Button selected;
private Rectangle oldBounds;
private Point oldMouseLocation;
@Override
public void handleEvent(Event event) {
if (event.type == SWT.MouseDown) {
onMouseDown(event);
} else if (event.type == SWT.MouseMove) {
onMouseMove(event);
} else if (event.type == SWT.MouseUp) {
onMouseUp(event);
}
}
private void onMouseDown(Event event) {
if (event.button != 1) {
return;
}
selected = get(event.x, event.y);
if (selected == null) {
return;
}
oldBounds = selected.getBounds();
oldMouseLocation = new Point(event.x, event.y);
}
private void onMouseMove(Event event) {
if (selected == null) {
return;
}
int xDiff = event.x - oldMouseLocation.x;
int yDiff = event.y - oldMouseLocation.y;
Button[] affects = getAffected(selected, xDiff);
Rectangle oldBounds = selected.getBounds();
Rectangle newBounds = new Rectangle(oldBounds.x + xDiff, oldBounds.y, oldBounds.width, oldBounds.height);
selected.setBounds(newBounds);
shell.redraw(oldBounds.x, oldBounds.y, oldBounds.width, oldBounds.height, true);
shell.redraw(newBounds.x, newBounds.y, newBounds.width, newBounds.height, true);
for (Button affect : affects) {
Rectangle bounds = affect.getBounds();
if (xDiff > 0) {
// Move subsequent items before
if (affect.hasAnimation()) {
Animation animation = affect.getAnimation();
// if (animation.getFrom() < animation.getTo()) {
animation.cancel();
affect.setAnimation(null);
// }
}
if (!affect.hasAnimation()) {
int destination = shouldAlignAfter(affect);
Animation animation = new Animation(shell, affect, bounds.x, destination);
affect.setAnimation(animation);
animation.start();
}
}
if (xDiff < 0) {
// Move previous items after
if (affect.hasAnimation()) {
Animation animation = affect.getAnimation();
// if (animation.getFrom() > animation.getTo()) {
animation.cancel();
affect.setAnimation(null);
// }
}
if (!affect.hasAnimation()) {
int destination = shouldAlignBefore(affect);
Animation animation = new Animation(shell, affect, bounds.x, destination);
affect.setAnimation(animation);
animation.start();
}
}
}
oldMouseLocation.x = event.x;
oldMouseLocation.y = event.y;
}
private void onMouseUp(Event event) {
if (event.button != 1) {
return;
}
if (selected == null) {
return;
}
Rectangle currentBounds = selected.getBounds();
int from = currentBounds.x;
int to = oldBounds.x;
Animation animation = new Animation(shell, selected, from, to);
selected.setAnimation(animation);
animation.start();
if (selected.hasAnimation()) {
selected.getAnimation().start();
}
selected = null;
oldBounds = null;
oldMouseLocation = null;
}
private int shouldAlignAfter(Button button) {
int left = 10;
for (int i = 0; i < buttons.length; i++) {
if (buttons[i].equals(selected)) {
continue;
}
if (buttons[i].equals(button)) {
return left;
}
Rectangle bounds = buttons[i].getBounds();
left += bounds.width;
left += 10;
}
return 0;
/*for (int i = 0; i < buttons.length; i++) {
if (!buttons[i].equals(button)) {
continue;
}
int previousIndex = i - 1;
if (previousIndex < 0) {
return 10;
}
if (buttons[previousIndex].equals(selected)) {
previousIndex --;
}
if (previousIndex < 0) {
return 10;
}
Rectangle otherBounds = buttons[previousIndex].getBounds();
return otherBounds.x + otherBounds.width + 10;
}
return 0;*/
}
private int shouldAlignBefore(Button button) {
int right = 480;
for (int i = buttons.length - 1; i >= 0; i--) {
if (buttons[i].equals(selected)) {
continue;
}
if (buttons[i].equals(button)) {
Rectangle bounds = button.getBounds();
return right - bounds.width;
}
Rectangle bounds = buttons[i].getBounds();
right -= bounds.width;
right -= 10;
}
return 0;
/*for (int i = 0; i < buttons.length; i++) {
if (!buttons[i].equals(button)) {
continue;
}
Rectangle bounds = button.getBounds();
int nextIndex = i + 1;
if (nextIndex == buttons.length) {
return 490 - 10 - bounds.width;
}
if (buttons[nextIndex].equals(selected)) {
nextIndex ++;
}
if (nextIndex == buttons.length) {
return 490 - 10 - bounds.width;
}
Rectangle otherBounds = buttons[nextIndex].getBounds();
return otherBounds.x - 10 - bounds.width;
}
return 0;*/
}
private Button get(int x, int y) {
for (Button button : buttons) {
Rectangle bounds = button.getBounds();
if (bounds.contains(x, y)) {
return button;
}
}
return null;
}
private Button[] getAffected(Button button, int delta) {
Set<Button> affected = new HashSet<Button>();
Rectangle bounds = button.getBounds();
for (Button otherButton : buttons) {
if (otherButton.equals(button)) {
continue;
}
Rectangle otherBounds = otherButton.getBounds();
if (delta > 0) {
if (bounds.x < otherBounds.x) {
if (bounds.x + delta + bounds.width > otherBounds.x + otherBounds.width / 2) {
affected.add(otherButton);
}
}
}
if (delta < 0) {
if (bounds.x > otherBounds.x) {
if (bounds.x + delta < otherBounds.x + otherBounds.width / 2) {
affected.add(otherButton);
}
}
}
}
return affected.toArray(new Button[affected.size()]);
}
private int indexOf(Button button) {
for (int i = 0; i < buttons.length; i++) {
if (buttons[i].equals(button)) {
return i;
}
}
return -1;
}
};
shell.addListener(SWT.MouseDown, listener);
shell.addListener(SWT.MouseMove, listener);
shell.addListener(SWT.MouseUp, listener);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}