/*
Part of the GUI library for Processing
http://www.lagers.org.uk/g4p/index.html
http://sourceforge.net/projects/g4p/files/?source=navbar
Copyright (c) 2014 Peter Lager
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package automenta.vivisect.gui;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import processing.core.PApplet;
/**
* This class allows you to group GUI controls so that they can be treated as a single
* entity for common GUI actions. <br>
* Once grouped a single statement change the enabled status, visibility status, colour
* scheme and transparency for all the controls in the group. It is possible to
* specify a delay before the action is executed, and in the case of transparency, specify
* the amount of time the fade occurs.<br>
This is particularly useful if your sketch has a number of 'modes' and each mode has its
own set of GUI controls. Simply create a GGroup object for each mode then as the mode
changes it is easy to display the controls for the current mode and hide the others. <br>
* A GGroup is associated with a window (or GWindow) and can only be used with controls
* displayed on that window.
*
* @author Peter Lager
*
*/
public final class GGroup extends GControl {
// The possible group action types
private static final int INVALID_ACTION = 0;
private static final int ALPHA_TO = 1;
private static final int ENABLE = 2;
private static final int VISIBLE = 3;
private static final int COLOR_SCHEME = 4;
// Actions still to be processed.
private List<Action> actions = new LinkedList<Action>();
private int cTime, lTime, eTime;
private int currentAlpha = 255;
private int targetAlpha = 255;
private float alpha = 255;
private float speed = 255;
/**
* Create a control group for the indicated PApplet. Only controls
* created with this PApplet can be added to this group. <br>
*
* All controls added will be initialised as fully opaque. <br>
*
* @param theApplet the associated PApplet
*/
public GGroup(PApplet theApplet) {
this(theApplet, 255);
}
/**
* Create a control group for the indicated PApplet. Only controls
* created with this PApplet can be added to this group. <br>
*
* All controls added will be initialised as fully opaque. <br>
*
* @param theApplet the associated PApplet
* @param startAlpha the starting alpha level (must 0-255 incl)
*/
public GGroup(PApplet theApplet, int startAlpha) {
super(theApplet);
startAlpha &= 0xFF;
currentAlpha = targetAlpha = startAlpha;
children = new LinkedList<GControl>();
registeredMethods = PRE_METHOD;
cTime = lTime = eTime = 0;
GUI.addControl(this);
}
/**
* Create a control group for the indicated GWindow. Only controls
* displayed in this GWindow can be added to this group. <br>
*
* All controls added will be initialised as fully opaque. <br>
*
* @param theWindow the associated GWindow
*/
public GGroup(GWindow theWindow) {
this(theWindow.papplet, 255);
}
/**
* Create a control group for the indicated GWindow. Only controls
* displayed in this GWindow can be added to this group. <br>
*
* All controls added will be initialised as fully opaque. <br>
*
* @param theWindow the associated GWindow
* @param startAlpha the starting alpha level (must 0-255 incl)
*/
public GGroup(GWindow theWindow, int startAlpha) {
this(theWindow.papplet, 255);
}
/**
* Fade to invisible. (alpha = 0)
*
* @param delay time before starting fade (milli-seconds)
* @param duration time to fade over (milli-seconds)
*/
public void fadeOut(int delay, int duration){
actions.add(new Action(ALPHA_TO, delay, new Object[] { 0, duration }));
}
/**
* Fade to fully opaque. (alpha = 255)
*
* @param delay time before starting fade (milli-seconds)
* @param duration time to fade over (milli-seconds)
*/
public void fadeIn(int delay, int duration){
actions.add(new Action(ALPHA_TO, delay, new Object[] { 255, duration }));
}
/**
* Fade to an alpha value
*
* @param delay time before starting fade (milli-seconds)
* @param duration time to fade over (milli-seconds)
* @param alpha the target alpha value
*/
public void fadeTo(int delay, int duration, int alpha){
alpha &= 0xFF;
actions.add(new Action(ALPHA_TO, delay, new Object[] { alpha, duration }));
}
/**
* Enable / disable the controls.
* @param delay delay time before action is performed (milli-seconds)
*/
public void setEnabled(int delay, boolean enable){
actions.add(new Action(ENABLE, delay, new Object[] { enable }));
}
public boolean isFading(){
return currentAlpha != targetAlpha;
}
public int timeLeftFading(){
if(currentAlpha == targetAlpha)
return 0;
// calculate approximate time left
return Math.round((targetAlpha - currentAlpha)/speed);
}
/**
* Enable / disable the controls immediately.
*/
public void setEnabled(boolean enable){
setEnabled(0, enable);
}
/**
* Make the control visible or invisible. <br>
* If you simply want to change the controls' visibility status then
* use this in preference to fadeIn/fadeOut
* @param delay delay time before action is performed (milli-seconds)
*/
public void setVisible(int delay, boolean visible){
actions.add(new Action(VISIBLE, delay, new Object[] { visible }));
}
/**
* Make the control visible or invisible with immediate effect. <br>
* If you simply want to change the controls' visibility status then
* use this in preference to fadeIn/fadeOut
*/
public void setVisible(boolean visible){
setEnabled(0, visible);
}
/**
* Set the color scheme used by these controls with immediate effect. <br>
* @see GConstants for colour constants (e.g. BLUE_SCHEME) to use.
*
* @param colScheme the colour scheme (0-15) to be used.
*/
public void setLocalColorScheme(int colScheme){
setLocalColorScheme(0, colScheme);
}
/**
* Set the color scheme used by these controls after a delay. <br>
* @see GConstants for colour constants (e.g. BLUE_SCHEME) to use.
*
* @param delay time before colour change (milli-seconds)
* @param colScheme the colour scheme (0-15) to be used.
*/
public void setLocalColorScheme(int delay, int colScheme){
colScheme &= 0xf; // constrain to 0-15
actions.add(new Action(COLOR_SCHEME, delay, new Object[] { colScheme }));
}
public void pre(){
if(lTime == 0){ // First call to method
cTime = lTime = (int) System.currentTimeMillis();
return;
}
// Get elapsed time in millis
lTime = cTime;
cTime = (int) System.currentTimeMillis();
eTime = cTime - lTime;
if(currentAlpha != targetAlpha)
peformFading();
if(!actions.isEmpty())
processActions();
}
/**
* Executed
*/
private void peformFading(){
// Calculate the next alpha and constrain to 0-255 incl.
float nextAlpha = alpha + eTime * speed;
nextAlpha = (nextAlpha < 0) ? 0 : nextAlpha;
nextAlpha = (nextAlpha >255) ? 255 : nextAlpha;
// See if alpha and nextAlpha stradles target
if((alpha - targetAlpha)*(nextAlpha - targetAlpha) <= 0)
currentAlpha = targetAlpha;
else
currentAlpha = (int)nextAlpha;
alphaImpl(currentAlpha);
alpha = nextAlpha;
}
/*
* Loop through all the actions waiting to be processed reducing the time
* before they need to be implemented. If the action is ready remove it
* from the list and implement it.
*/
private void processActions(){
Iterator<Action> iter = actions.iterator();
while(iter.hasNext()){
Action a = iter.next();
a.delay -= eTime;
if(a.delay <= 0){
iter.remove();
switch(a.type){
case VISIBLE:
visibleImpl(a.bool);
break;
case ENABLE:
enableImpl(a.bool);
break;
case COLOR_SCHEME:
colorImpl(a.target);
break;
case ALPHA_TO:
if(a.duration <= 0)
alphaImpl(a.target);
else {
speed = ((float)(a.target - currentAlpha))/(float) a.duration;
alpha = currentAlpha;
targetAlpha = a.target;
}
break;
}
}
}
}
private void alphaImpl(int alpha){
for(GControl control : children)
control.setAlpha(alpha, true);
}
private void colorImpl(int col){
col &= 0xff; // make 0 -15
for(GControl control : children)
control.setLocalColorScheme(col, true);
}
private void enableImpl(boolean enable){
for(GControl control : children)
control.setEnabled(enable);
}
private void visibleImpl(boolean visible){
for(GControl control : children)
control.setVisible(visible);
}
/**
* Add one or more GUI controls to this group. <br>
* If a control is not a valid type for group control, or if the control and group are for different
* windows then the control is not added and a warning message is displayed.
*
* @param controls comma separated list of GUI controls to add to this group
*/
public void add(GControl... controls){
if(controls != null)
for(GControl control : controls)
add(control);
}
/**
* A single GUI control to add to this group. <br>
* If a control is not a valid type for group control, or if the control and group are for different
* windows then the control is not added and a warning message is displayed.
*
* @param control a GUI control to add to this group
*/
public void add(GControl control){
if(control != null){
if(!control.isSuitableForGroupControl(control)){
GMessenger.message(INVALID_TYPE, new Object[] { this, control } );
return;
}
if(this.winApp != control.winApp){
GMessenger.message(INVALID_PAPPLET, new Object[] { this, control } );
return;
}
control.setAlpha(currentAlpha, true);
children.add(control);
}
}
/**
* Remove one or more GUI controls from this group. <br>
*
* @param controls comma separated list of GUI controls to remove from this group
*/
public void removeControls(GControl... controls){
if(controls != null)
for(GControl control : controls)
removeControl(control);
}
/**
* Remove a single GUI control from this group. <br>
*
* @param control a GUI control to remove from this group
*/
public void removeControl(GControl control){
if(control != null)
children.remove(control);
}
private class Action {
int type;
long delay = 0;
// Extras
int duration = 0;
boolean bool = false;
int target = 0;
/**
* Create an action
*
* @param type
* @param delay
* @param extra
*/
public Action(int type, long delay, Object... extra) {
super();
this.type = type;
this.delay = delay;
switch(type){
case VISIBLE:
case ENABLE:
bool = (Boolean)extra[0];
break;
case COLOR_SCHEME:
target = (Integer)extra[0];
break;
case ALPHA_TO:
target = (Integer)extra[0];
duration = (Integer)extra[1];
break;
default:
this.type = INVALID_ACTION;
}
}
public String toString(){
StringBuilder sb = new StringBuilder();
switch(type){
case VISIBLE:
sb.append("VISIBLE ");
break;
case ENABLE:
sb.append("ENABLE ");
break;
case COLOR_SCHEME:
sb.append("COLOR_SCHEME ");
break;
case ALPHA_TO:
sb.append("ALPHA_TO over ");
sb.append(duration + " millis ");
break;
default:
sb.append("INVALID ");
break;
}
return sb.toString();
}
}
}