package org.objectstyle.wolips.componenteditor.inspector; 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.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; public class PopAnimator implements PaintListener { private static final int minBorderWidth = 2; private static final int maxBorderWidth = 6; private static final int animationDuration = 150; private static final int borderRadius = 10; private static final int borderRedrawSize = maxBorderWidth * 2; private boolean _isAnimating; private long _startTime; private Rectangle _animationRect; private Color _backgroundColor; private Control _control; public PopAnimator() { RGB bindingHoverColorPreference = new RGB(240, 220, 0); _backgroundColor = new Color(Display.getCurrent(), bindingHoverColorPreference); } public void setControl(Control control) { if (_control != null && !_control.isDisposed()) { _control.removePaintListener(this); } _control = control; if (_control != null) { _control.addPaintListener(this); } } public void setAnimationRect(Rectangle animationRect) { repaint(); _animationRect = animationRect; } public void step() { if (isAnimating()) { repaint(); } } public void repaint() { repaint(_animationRect); } public void repaint(Rectangle rect) { if (_control != null && rect != null) { _control.redraw(rect.x - borderRedrawSize, rect.y - borderRedrawSize, rect.width + 2 * borderRedrawSize, rect.height + 2 * borderRedrawSize, true); } } public boolean isAnimating() { return _isAnimating; } public void dispose() { setControl(null); if (_backgroundColor != null) { _backgroundColor.dispose(); } _backgroundColor = null; } public synchronized void stopAnimation() { _isAnimating = false; Rectangle animationRect = _animationRect; _animationRect = null; if (animationRect != _animationRect) { repaint(animationRect); } } public synchronized void startAnimation() { _startTime = System.currentTimeMillis(); _isAnimating = true; repaint(); } public void paintControl(PaintEvent e) { try { boolean shouldThrob; double animationTime; Rectangle selectionRect; synchronized (this) { // Determine whether or not the animation should still be // running animationTime = System.currentTimeMillis() - _startTime; if (_isAnimating && animationTime > 2 * animationDuration) { _isAnimating = false; } if (_animationRect != null) { selectionRect = new Rectangle(_animationRect.x, _animationRect.y, _animationRect.width, _animationRect.height); } else { selectionRect = null; } shouldThrob = _isAnimating; } if (_animationRect != null) { // Set the line width based on the animation curve int lineWidth = minBorderWidth; if (shouldThrob) { lineWidth += (int) ((maxBorderWidth - minBorderWidth) * Math.sin(0.5 * Math.PI * animationTime / animationDuration)); } int margin = lineWidth / 2; if (lineWidth > 0) { e.gc.setLineWidth(lineWidth); // Set the rectangle size according to the current animation // position selectionRect.x -= margin; selectionRect.y -= margin; selectionRect.width += 2 * margin; selectionRect.height += 2 * margin; // Make selections on the edge look a little nicer if (selectionRect.x < minBorderWidth / 2) { selectionRect.x += minBorderWidth; selectionRect.width -= minBorderWidth; } if (selectionRect.y < minBorderWidth / 2) { selectionRect.y += minBorderWidth; selectionRect.height -= minBorderWidth; } // Draw the shadow -- we have to cheat some here, because // the text is rendered underneath us, so we can only render // the bottom of the shadow if (true || lineWidth > minBorderWidth) { int shadowHeight = 2 * margin; int shadowMargin = borderRadius / 2 + 2 * margin; e.gc.setAlpha(80); e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_BLACK)); e.gc.drawRoundRectangle(selectionRect.x + shadowMargin, selectionRect.y + selectionRect.height, selectionRect.width - 2 * shadowMargin, shadowHeight, borderRadius, borderRadius); } e.gc.setForeground(_backgroundColor); e.gc.setBackground(_backgroundColor); // Fill the rectangle with a light color for multiline // selections e.gc.setAlpha(50); e.gc.fillRoundRectangle(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height, borderRadius, borderRadius); // And now draw the border of the rectangle in full opaque e.gc.setAlpha(255); e.gc.drawRoundRectangle(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height, borderRadius, borderRadius); } } } catch (Throwable t) { t.printStackTrace(); } } }