package org.rsg.lib.chrome;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import org.rsg.lib.LibUtilities;
import org.rsg.lib.chrome.button.AbstractButton;
import org.rsg.lib.chrome.button.Header;
import org.rsg.lib.chrome.button.Kill;
import org.rsg.lib.chrome.button.Maximize;
import org.rsg.lib.chrome.button.Minimize;
import org.rsg.lib.chrome.button.Resize;
import org.rsg.lib.color.ColorUtilities;
import processing.core.PApplet;
public class Chrome implements ComponentListener {
public static boolean shouldWarnAboutLowFrameRate = false;
private static boolean shouldLockToAspectRatio = false;
public static boolean shouldAutoHide = false;
private boolean initialized = false;
private static float originalAspectRatio = 768/1024;
public static final int MAC_CHROME_HEIGHT = 22;
public static final int HEIGHT = 33;
private static final int MINIMUM_WINDOW_WIDTH = 200;
private static final int MINIMUM_WINDOW_HEIGHT = HEIGHT*2;
public static PApplet papplet;
private float mousex = 0.0f;
private float mousey = 0.0f;
private float pwidth = 0.0f;
private float pheight = 0.0f;
private Point ppointer;
private boolean isLocked4Movement = false;
private boolean isLocked4Resize = false;
private static Dimension APP_SIZE_DEFAULT = new Dimension(800, 600);
private static Dimension APP_SIZE = APP_SIZE_DEFAULT;
public static boolean FULL_SCREEN = false;
// public static boolean WAS_FULL_SCREEN_BEFORE_MINIMIZE = false;
private static String label = "Application";
private final static int ALPHA_MAX = 255;
private final static int ALPHA_MIN = 1;
private final static int FRAMESPERSEC = 20;
private final static int TIMEOUTSECS = 10;
private final static int ALPHA_STEP = 50;
private final static int alpha_start = ALPHA_MAX + (FRAMESPERSEC * TIMEOUTSECS * ALPHA_STEP);
private static int _alpha = alpha_start;
public static AbstractButton minimize = new Minimize();
// public static AbstractButton maximize = new Maximize();
public static AbstractButton kill = new Kill();
public static AbstractButton resize = new Resize();
public static AbstractButton header = new Header();
//singleton pattern
public static Chrome instance() { return INSTANCE; }
private static final Chrome INSTANCE = new Chrome();
private Chrome() {}
public final static int FRAME_RATE_STARTING = 20;
//DRAW
public void draw(PApplet papplet) {
if(!initialized) { initWithParent(papplet); }
drawChrome(papplet);
if(shouldAutoHide)
updateAlpha();
Tooltip.run();
minimize.pick(papplet);
// maximize.pick(papplet);
kill.pick(papplet);
resize.pick(papplet);
// if(resize.pick(papplet) && isVisible()) {
// cursor(Cursor.SE_RESIZE_CURSOR);
// } else {
// cursor(Cursor.DEFAULT_CURSOR);
// }
//System.out.println("[Chrome] alpha():"+alpha() + " _alpha:"+_alpha);
}
// //only changes it if different
// private void cursor(int i) {
// Cursor c = papplet.getCursor();
// if(c.getType() != i)
// papplet.cursor(i);
// }
private void updateAlpha() {
if(_alpha > ALPHA_MIN)
_alpha -= ALPHA_STEP;
}
private void drawChrome(PApplet papplet) {
//TODO: stop drawing if gui is invisible?
//header
header.draw(papplet);
//buttons
minimize.draw(papplet);
// maximize.draw(papplet);
kill.draw(papplet);
resize.draw(papplet);
//thin box around whole app
papplet.noFill();
papplet.stroke(Color.BLACK.getRGB()); //no alpha for this, it's always visible
if(shouldWarnAboutLowFrameRate) {
int rate = PApplet.constrain(PApplet.ceil(papplet.frameRate), 0, FRAME_RATE_STARTING);
if(rate < FRAME_RATE_STARTING) {
papplet.stroke(Color.RED.getRGB()); //no alpha for this, it's always visible
papplet.strokeWeight(3);
}
}
papplet.rect(0, 0, APP_SIZE.width-1, APP_SIZE.height-1);
}
//MOUSE
public void mouseEvent(MouseEvent e) {
resetAlpha();//any mouse event makes chrome visible
int id = e.getID();
if(!FULL_SCREEN) {
//LOCK ON MOUSE DOWN
if (id == MouseEvent.MOUSE_PRESSED) {
if (resize.isPicked(e)) { //for resize
isLocked4Resize = true;
ppointer = MouseInfo.getPointerInfo().getLocation();
pwidth = APP_SIZE.width;
pheight = APP_SIZE.height;
} else if (header.isPicked(e)) { //for move
isLocked4Movement = true;
mousex = papplet.mouseX;
mousey = papplet.mouseY;
}
//RESIZE WINDOW
} else if (isLocked4Resize && (id == MouseEvent.MOUSE_DRAGGED)) {
Point pointer = MouseInfo.getPointerInfo().getLocation();
float dx = pointer.x - ppointer.x;
float dy = pointer.y - ppointer.y;
int w = (int)(pwidth + dx);
int h = (int)(pheight + dy);
Dimension newSize = normalizeScreenSize(w,h);
papplet.frame.setSize(newSize);
//MOVE WINDOW
} else if (isLocked4Movement && (id == MouseEvent.MOUSE_DRAGGED)) {
Point p = MouseInfo.getPointerInfo().getLocation();
int x = (int) (p.x - mousex);
int y = (int) (p.y - mousey);
Point newLocation = magnetizeToScreenEdge(normalizeScreenLocation(x,y));
papplet.frame.setLocation(newLocation);
//UNLOCK ALL ON MOUSE UP
} else if (id == MouseEvent.MOUSE_RELEASED) {
isLocked4Resize = false;
isLocked4Movement = false;
}
}
//DOUBLE CLICK ON HEADER
if (header.isPicked(e) && (id == MouseEvent.MOUSE_CLICKED) && (e.getClickCount() > 1)) {
// System.out.println("[Chrome] DOUBLE CLICK ON HEADER");
header.click(papplet);
}
}
// private boolean highlightButton(AbstractButton button, MouseEvent e) {
// if (button.isPicked) {
// if(e.getID() == MouseEvent.MOUSE_CLICKED) {
// button.click(papplet);
// }
// return true;
// } else {
// button.isHighlighted = false;
// return false;
// }
// }
private Dimension normalizeScreenSize(int w, int h) {
return normalizeScreenSize(new Dimension(w,h));
}
public static Dimension getAPP_SIZE() {
return APP_SIZE;
}
private Dimension normalizeScreenSize(Dimension d) {
int w = PApplet.max(d.width, MINIMUM_WINDOW_WIDTH);
int h = PApplet.max(d.height, MINIMUM_WINDOW_HEIGHT);
// System.out.println("[Chrome] normalizeScreenSize shouldLockToAspectRatio:"+shouldLockToAspectRatio + " " + originalAspectRatio);
if(shouldLockToAspectRatio)
h = (int) (w*originalAspectRatio);
return new Dimension(w,h);
}
private Point normalizeScreenLocation(int x, int y) {
return normalizeScreenLocation(new Point(x,y));
}
//turned this off because it was buggy w/ multiple monitors
private Point normalizeScreenLocation(Point p) {
// int x = PApplet.constrain(p.x, -(APP_SIZE.width) + HEIGHT, screenSize().width - HEIGHT);
// int y = PApplet.constrain(p.y, screenInsets().top, screenSize().height - HEIGHT);
// Point newp = new Point(x,y);
// //System.out.println("p:"+p + " newp:"+newp);
// return newp;
return p;
}
private static final int MAGNETIC_RANGE = 15;
private Point magnetizeToScreenEdge(Point p) {
//if(!isOnPrimaryScreen(p)) {return p;}
//left
if((p.x < MAGNETIC_RANGE) && (p.x > -MAGNETIC_RANGE)) {p.x = 0;}
//top
if(xIsOnPrimaryScreen(p)) {
if(LibUtilities.isMac()) {
if(p.y < (MAC_CHROME_HEIGHT + MAGNETIC_RANGE)) {p.y = MAC_CHROME_HEIGHT;}
} else {
if(p.y < MAGNETIC_RANGE) {p.y = 0;}
}
}
//right
int w = screenSize().width - APP_SIZE.width;
if((p.x > (w - MAGNETIC_RANGE)) &&
(p.x < (w + MAGNETIC_RANGE))) {p.x = w;}
//bottom
int h = screenSize().height - APP_SIZE.height;
if((p.y > (h - MAGNETIC_RANGE)) &&
(p.y < (h + MAGNETIC_RANGE))) {p.y = h;}
return p;
}
private boolean xIsOnPrimaryScreen(Point p) {
int w = screenSize().width;
return (p.x < w) &&
((p.x + w) > 0);
}
//FULLSCREEN
public static void toggleFullscreen(PApplet papplet) {
GraphicsDevice defDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (FULL_SCREEN) {
defDevice.setFullScreenWindow(null);
} else {
defDevice.setFullScreenWindow(papplet.frame);
}
FULL_SCREEN = !FULL_SCREEN;
updateSize(papplet);
papplet.frame.toFront();
}
public static void minimize(PApplet papplet) {
if(Chrome.FULL_SCREEN) {
toggleFullscreen(papplet);
}
papplet.frame.setState(Frame.ICONIFIED);
papplet.mouseX = 0;
papplet.mouseY = 0;
// Tooltip.clear(); //have to do this manually for some reason
}
//UTILITIES
public static int white() {
return ColorUtilities.colorWithAlphaAsInt(Color.WHITE, alpha());
}
public static int black() {
return ColorUtilities.colorWithAlphaAsInt(Color.BLACK, alpha());
}
public static int yellow() {
return ColorUtilities.colorWithAlphaAsInt(Color.YELLOW, alpha());
}
public static int lightYellow() {
return new Color(255, 255, 202, alpha()).getRGB();
}
public static int gray() {
return ColorUtilities.colorWithAlphaAsInt(Color.GRAY, alpha());
}
public static int darkgray() {
return ColorUtilities.colorWithAlphaAsInt(Color.DARK_GRAY, alpha());
}
public static int lightgray() {
return ColorUtilities.colorWithAlphaAsInt(Color.LIGHT_GRAY, alpha());
}
public static Dimension screenSize(){
return Toolkit.getDefaultToolkit().getScreenSize();
}
public static Insets screenInsets() {
//TODO get this more effectively using a Toolkit call like this?
// return Toolkit.getDefaultToolkit().getScreenInsets(GraphicsConfiguration gc);
if(LibUtilities.isMac()) {
return new Insets(MAC_CHROME_HEIGHT, 0, 0, 0);
} else {
return new Insets(0, 0, 0, 0);
}
}
public static void updateSize(PApplet papplet) {
Dimension d = new Dimension(papplet.frame.getSize());
updateSize(papplet, d);
}
public static void updateSize(PApplet papplet, Dimension d) {
APP_SIZE = d;
}
private void initWithParent(PApplet papplet) {
initialized = true;
Chrome.papplet = papplet;
papplet.registerMouseEvent(this);
papplet.addComponentListener(this);
}
// private boolean isPicked(Rectangle target, MouseEvent e) {
// return target.contains(new Point(e.getX(), e.getY()));
// }
public int labelWindowWidth() {
return header.bounds().x - Header.SPACER_LEFT_LABEL;
}
public static String getLabel() {
return label;
}
public static void setLabel(String label) {
Chrome.label = label;
}
private static void resetAlpha() {
_alpha = alpha_start;
}
public static boolean isVisible() {
return !(_alpha == ALPHA_MAX);
}
public static int alpha() {
if (_alpha < ALPHA_MIN) {return ALPHA_MIN;}
if (_alpha > ALPHA_MAX) {return ALPHA_MAX;}
return _alpha;
}
public static void setAPP_SIZE(Dimension app_size) {
// APP_SIZE_DEFAULT.setSize(app_size);
originalAspectRatio = (float) app_size.height / app_size.width;
APP_SIZE.setSize(app_size);
// System.out.println("[Chrome] setAPP_SIZE("+app_size.width+","+app_size.height+") originalAspectRatio:"+originalAspectRatio);
}
// private static float getOriginalAspectRatio() {
// return APP_SIZE_DEFAULT.width / APP_SIZE_DEFAULT.height;
// }
public static void setShouldLockToAspectRatio(boolean shouldLockToAspectRatio) {
Chrome.shouldLockToAspectRatio = shouldLockToAspectRatio;
}
///////////////////////////////////////////////////////
//INTERFACE: ComponentListener
public void componentHidden(ComponentEvent arg0) {}
public void componentShown(ComponentEvent arg0) {}
public void componentMoved(ComponentEvent arg0) {}
public void componentResized(ComponentEvent arg0) {
// System.out.println("["+this.getClass().getSimpleName()+"] componentResized("+arg0+")");
updateSize(papplet, arg0.getComponent().getSize());
ChromeDispatcher.rescaleWindow();
papplet.smooth();
}
}