/**
*
*/
package logbook.gui.logic;
import java.util.ArrayList;
import java.util.List;
import logbook.config.AppConfig;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
/**
* @author Nekopanda
* 透明度アニメーションのロジック
*/
public class OpacityAnimation {
private static int ALPHA_MAX = 255;
private static int STEP_TIME = 30;
private static int NUM_STEPS = 10;
private static OpacityAnimationServer server = new OpacityAnimationServer();
private final OpacityAnimationClient client;
private int baseAlpha; // 設定されたアルファ値
private boolean mouseHoverAware = false; // マウスホバーに反応する?
private boolean enabled = false;
private boolean mouseLeaved = false;
private boolean activated = false;
private boolean nowAnimating = false;
private int remainSteps = 0;
private int currentAlpha;
private int alphaStep;
private int targetAlpha;
private static class OpacityAnimationServer {
private final List<OpacityAnimation> clients = new ArrayList<OpacityAnimation>();
private Listener mouseEventFilter;
private boolean nowAnimating = false;
public void register(OpacityAnimation client) {
if (this.clients.size() == 0) {
// start;
this.mouseEventFilter = new Listener() {
@Override
public void handleEvent(Event event) {
if (OpacityAnimationServer.this.shouldActivateByMouseEvent()) {
OpacityAnimationServer.this.startAnimation();
}
}
};
Display.getDefault().addFilter(SWT.MouseMove, this.mouseEventFilter);
}
this.clients.add(client);
}
public void deregister(OpacityAnimation client) {
if (this.clients.remove(client)) {
if (this.clients.size() == 0) {
// end
Display.getDefault().removeFilter(
SWT.MouseMove, this.mouseEventFilter);
}
}
}
/** マウスイベントでアクティブにすべきクライアントがいるか */
private boolean shouldActivateByMouseEvent() {
boolean ret = false;
for (OpacityAnimation client : this.clients) {
if ((client.activated == false) && client.mouseHoverAware && client.client.isMouseHovering()) {
ret = client.nowAnimating = true;
}
}
//System.out.println(ret);
return ret;
}
public void startAnimation() {
// 2つ以上立ち上げない
if (this.nowAnimating == false) {
this.nowAnimating = true;
Display.getDefault().timerExec(STEP_TIME, new Runnable() {
@Override
public void run() {
boolean active = false;
for (OpacityAnimation client : OpacityAnimationServer.this.clients) {
if (client.nowAnimating) {
client.animateProc();
active = true;
}
}
if (active) {
Display.getDefault().timerExec(STEP_TIME, this);
}
else {
OpacityAnimationServer.this.nowAnimating = false;
}
}
});
}
}
}
public OpacityAnimation(OpacityAnimationClient client) {
this.client = client;
}
public void setEnabled(boolean enabled) {
if (this.enabled != enabled) {
this.enabled = enabled;
if (enabled) {
server.register(this);
}
else {
server.deregister(this);
}
}
}
/** 初期アルファ値設定(client.setAlpha()も呼び出す) */
public void setInitialAlpha(int initAlpha, boolean mouseHoverAware) {
this.baseAlpha = this.currentAlpha = this.targetAlpha = initAlpha;
this.mouseHoverAware = mouseHoverAware;
this.client.setAlpha(initAlpha);
}
/** 基本アルファ値を設定 */
public void setAlpha(int newAlpah) {
if (this.baseAlpha != newAlpah) {
this.baseAlpha = newAlpah;
if (this.enabled) {
this.nowAnimating = true;
server.startAnimation();
}
}
}
public int getAlpha() {
return this.baseAlpha;
}
public int getCurrentAlpha() {
return this.currentAlpha;
}
/** マウスに対応するかを設定する */
public void setHoverAware(boolean aware) {
if (this.mouseHoverAware != aware) {
this.mouseHoverAware = aware;
if (this.enabled) {
this.nowAnimating = true;
//System.out.println("mouse hover -> " + aware);
server.startAnimation();
}
}
}
public boolean getHoverAware() {
return this.mouseHoverAware;
}
private void animateProc() {
boolean continueAnimation = false;
boolean mouseHovering = this.client.isMouseHovering();
if (mouseHovering) {
// ウィンドウからのMouseLeaveイベントが取得できないのでマウスがあるときは常に監視する
continueAnimation = (this.mouseHoverAware && (this.baseAlpha != ALPHA_MAX));
this.mouseLeaved = false;
this.activated = true;
}
else {
if (this.mouseLeaved == false) {
// マウスが離れた処理を開始する
this.mouseLeaved = true;
this.remainSteps = (((AppConfig.get().getOpaqueInterval() * 100) + STEP_TIME) - 1) / STEP_TIME;
}
if (this.remainSteps > 0) {
continueAnimation = true;
this.remainSteps--;
//System.out.println("Holding=" + this.remainSteps);
}
else {
// カウントがゼロになったので非アクティブ
this.activated = false;
}
}
int newAlpha;
if (this.activated && this.mouseHoverAware) {
newAlpha = ALPHA_MAX;
}
else {
newAlpha = this.baseAlpha;
}
if (this.targetAlpha != newAlpha) {
this.targetAlpha = newAlpha;
this.alphaStep = ((Math.abs(this.targetAlpha - this.currentAlpha) + NUM_STEPS) - 1)
/ NUM_STEPS;
if (this.targetAlpha < this.currentAlpha) {
this.alphaStep = -this.alphaStep;
}
else {
// 見える化は早くする
this.alphaStep *= 2;
}
}
if (this.currentAlpha != this.targetAlpha) {
this.currentAlpha += this.alphaStep;
// 目標に到達したか
if ((this.alphaStep > 0) && (this.currentAlpha > this.targetAlpha)) {
this.currentAlpha = this.targetAlpha;
}
else if ((this.alphaStep < 0) && (this.currentAlpha < this.targetAlpha)) {
this.currentAlpha = this.targetAlpha;
}
else {
continueAnimation = true;
}
//System.out.println("Alpha=" + this.currentAlpha);
this.client.setAlpha(this.currentAlpha);
}
if (continueAnimation == false) {
this.activated = false;
}
this.nowAnimating = continueAnimation;
}
}