/*******************************************************************************
* Copyright 2014 Rafael Garcia Moreno.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.bladecoder.engineeditor.ui;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.HashMap;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener.ChangeEvent;
import com.bladecoder.engine.actions.Param;
import com.bladecoder.engine.anim.AnimationDesc;
import com.bladecoder.engine.anim.AtlasAnimationDesc;
import com.bladecoder.engine.anim.SpineAnimationDesc;
import com.bladecoder.engine.anim.Tween;
import com.bladecoder.engine.anim.Tween.Type;
import com.bladecoder.engine.assets.EngineAssetManager;
import com.bladecoder.engine.i18n.I18N;
import com.bladecoder.engine.model.ActorRenderer;
import com.bladecoder.engine.model.AnimationRenderer;
import com.bladecoder.engine.model.AtlasRenderer;
import com.bladecoder.engine.model.ImageRenderer;
import com.bladecoder.engine.model.Sprite3DRenderer;
import com.bladecoder.engine.model.SpriteActor;
import com.bladecoder.engine.spine.SpineRenderer;
import com.bladecoder.engineeditor.Ctx;
import com.bladecoder.engineeditor.common.EditorLogger;
import com.bladecoder.engineeditor.model.Project;
import com.bladecoder.engineeditor.scneditor.AnimationWidget;
import com.bladecoder.engineeditor.ui.panels.EditModelDialog;
import com.bladecoder.engineeditor.ui.panels.InputPanel;
import com.bladecoder.engineeditor.ui.panels.InputPanelFactory;
public class EditAnimationDialog extends EditModelDialog<SpriteActor, AnimationDesc> {
public static final String ANIMATION_TYPES[] = { Tween.Type.NO_REPEAT.toString(), Tween.Type.REPEAT.toString(),
Tween.Type.YOYO.toString(), Tween.Type.REVERSE.toString() };
public static final String INFO = "Define sprites and animations";
InputPanel source;
InputPanel atlas;
InputPanel id;
InputPanel localizable;
InputPanel repeat;
InputPanel speed;
InputPanel count;
InputPanel in;
InputPanel out;
InputPanel sound;
InputPanel preload;
InputPanel dispose;
AnimationWidget spriteWidget = new AnimationWidget(this);
@SuppressWarnings("unchecked")
public EditAnimationDialog(Skin skin, SpriteActor p, AnimationDesc e) {
super(skin);
setInfo(INFO);
source = InputPanelFactory.createInputPanel(skin, "Source",
"Select the source where the sprite or animation is defined", new String[0], true);
atlas = InputPanelFactory.createInputPanel(skin, "Atlas", "Select the atlas for the selected Spine skeleton",
getAtlases(), true);
localizable = InputPanelFactory.createInputPanel(skin, "Localizable",
"True if the image is customizable per language.", Param.Type.BOOLEAN, true, "false");
id = InputPanelFactory.createInputPanel(skin, "ID", "Select the id of the animation", new String[0], true);
repeat = InputPanelFactory.createInputPanel(skin, "Animation type", "Select the type of the animation",
Param.Type.OPTION, true, Tween.Type.NO_REPEAT.toString(), Tween.Type.class.getEnumConstants());
speed = InputPanelFactory.createInputPanel(skin, "Speed", "Select the speed of the animation in secods",
Param.Type.FLOAT, true, "1.0");
count = InputPanelFactory.createInputPanel(skin, "Count", "Select the repeat times. -1 for infinity",
Param.Type.INTEGER, true, "-1");
in = InputPanelFactory.createInputPanel(skin, "In Dist",
"Select the distance in pixels to add to the actor position when the sprite is displayed",
Param.Type.VECTOR2, false);
out = InputPanelFactory.createInputPanel(skin, "Out Dist",
"Select the distance in pixels to add to the actor position when the sprite is changed",
Param.Type.VECTOR2, false);
sound = InputPanelFactory.createInputPanel(skin, "Sound",
"Select the sound ID that will be played when showing");
preload = InputPanelFactory.createInputPanel(skin, "Preload", "Preload the animation when the scene is loaded",
Param.Type.BOOLEAN, true, "true");
dispose = InputPanelFactory.createInputPanel(skin, "Dispose When Played", "Dispose de animation after playing",
Param.Type.BOOLEAN, true, "false");
((SelectBox<String>) repeat.getField()).addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
showHideFieldsDelayCountFields();
}
});
((SelectBox<String>) source.getField()).addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
EditorLogger.debug("EditAnimationDialog.setSource():" + source.getText());
setSource();
fillAnimations();
}
});
((SelectBox<String>) id.getField()).addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setAnimation();
}
});
((SelectBox<String>) atlas.getField()).addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setSource();
fillAnimations();
}
});
((TextField) speed.getField()).addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
setAnimation();
}
});
setInfoWidget(spriteWidget);
init(p, e, new InputPanel[] { source, localizable, atlas, id, repeat, speed, count, in, out, sound, preload,
dispose });
addSources();
// call modelToInputs again to set the correct source
if( e != null )
modelToInputs();
setVisible(count, false);
setVisible(atlas, false);
setVisible(localizable, false);
ActorRenderer renderer = parent.getRenderer();
if (renderer instanceof ImageRenderer) {
setVisible(localizable, true);
setVisible(speed, false);
setVisible(repeat, false);
setVisible(id, false);
}
if (e != null) {
source.setText(e.source);
}
if (source.getText() != null && !source.getText().isEmpty()) {
setSource();
fillAnimations();
if (e != null) {
id.setText(e.id);
}
}
}
private void showHideFieldsDelayCountFields() {
String type = repeat.getText();
if (type.equals(Tween.Type.REPEAT.toString()) || type.equals(Tween.Type.YOYO.toString())) {
setVisible(count, true);
} else {
setVisible(count, false);
}
}
private void setSource() {
AnimationDesc anim = null;
ActorRenderer renderer = parent.getRenderer();
String sourceStr = source.getText();
if (renderer instanceof SpineRenderer) {
anim = new SpineAnimationDesc();
if (spineAtlasExists(sourceStr)) {
((SpineAnimationDesc) anim).atlas = null;
setVisible(atlas, false);
} else {
if (!atlas.isVisible()) {
setVisible(atlas, true);
}
((SpineAnimationDesc) anim).atlas = atlas.getText();
}
} else if (renderer instanceof AtlasRenderer) {
anim = new AtlasAnimationDesc();
} else {
anim = new AnimationDesc();
}
anim.source = sourceStr;
anim.count = Tween.INFINITY;
anim.preload = true;
anim.disposeWhenPlayed = false;
if (renderer instanceof SpineRenderer) {
spriteWidget.setSource(Project.SPINE_RENDERER_STRING, anim);
} else if (renderer instanceof AtlasRenderer) {
spriteWidget.setSource(Project.ATLAS_RENDERER_STRING, anim);
} else if (renderer instanceof ImageRenderer) {
spriteWidget.setSource(Project.IMAGE_RENDERER_STRING, anim);
} else if (renderer instanceof Sprite3DRenderer) {
spriteWidget.setSource(Project.S3D_RENDERER_STRING, anim);
}
}
public boolean spineAtlasExists(String source) {
return EngineAssetManager.getInstance()
.assetExists(Ctx.project.getProjectPath() + "/" + Project.ATLASES_PATH + "/" + source + ".atlas");
}
private void setAnimation() {
String ids = id.getText();
String type = repeat.getText();
String speedStr = speed.getText();
@SuppressWarnings("unchecked")
SelectBox<String> cb = (SelectBox<String>) id.getField();
if (e != null || cb.getSelectedIndex() != 0)
spriteWidget.setAnimation(ids, speedStr, Tween.Type.valueOf(type));
}
private void fillAnimations() {
@SuppressWarnings("unchecked")
SelectBox<String> cb = (SelectBox<String>) id.getField();
cb.clearItems();
// When creating, give option to add all elements
if (e == null)
cb.getItems().add("<ADD ALL>");
String ids[] = spriteWidget.getAnimations();
for (String s : ids)
cb.getItems().add(s);
cb.getList().setItems(cb.getItems());
if (cb.getItems().size > 0)
cb.setSelectedIndex(0);
cb.invalidateHierarchy();
setAnimation();
}
String ext;
private void addSources() {
@SuppressWarnings("unchecked")
SelectBox<String> cb = (SelectBox<String>) source.getField();
String[] src = getSources();
cb.getItems().clear();
for (String s : src)
cb.getItems().add(s);
cb.getList().setItems(cb.getItems());
if (cb.getItems().size > 0)
cb.setSelectedIndex(0);
cb.invalidateHierarchy();
}
private String[] getSources() {
String path = null;
ActorRenderer renderer = parent.getRenderer();
if (renderer instanceof AtlasRenderer) {
path = Ctx.project.getProjectPath() + Project.ATLASES_PATH + "/" + Ctx.project.getResDir();
ext = EngineAssetManager.ATLAS_EXT;
} else if (renderer instanceof Sprite3DRenderer) {
path = Ctx.project.getProjectPath() + Project.SPRITE3D_PATH;
ext = EngineAssetManager.MODEL3D_EXT;
} else if (renderer instanceof SpineRenderer) {
path = Ctx.project.getProjectPath() + Project.SPINE_PATH;
ext = EngineAssetManager.SPINE_EXT;
} else if (renderer instanceof ImageRenderer) {
path = Ctx.project.getProjectPath() + Project.IMAGE_PATH + "/" + Ctx.project.getResDir();
ext = "";
}
File f = new File(path);
String sources[] = f.list(new FilenameFilter() {
@Override
public boolean accept(File arg0, String arg1) {
if (arg1.endsWith(ext))
return true;
return false;
}
});
if (sources != null) {
Arrays.sort(sources);
for (int i = 0; i < sources.length; i++)
sources[i] = sources[i].substring(0, sources[i].length() - ext.length());
} else {
sources = new String[0];
}
return sources;
}
private String[] getAtlases() {
String path = Ctx.project.getProjectPath() + Project.ATLASES_PATH + "/" + Ctx.project.getResDir();
File f = new File(path);
String atlases[] = f.list(new FilenameFilter() {
@Override
public boolean accept(File arg0, String arg1) {
if (arg1.endsWith(EngineAssetManager.ATLAS_EXT))
return true;
return false;
}
});
if (atlases != null) {
Arrays.sort(atlases);
for (int i = 0; i < atlases.length; i++)
atlases[i] = atlases[i].substring(0, atlases[i].length() - EngineAssetManager.ATLAS_EXT.length());
} else {
atlases = new String[0];
}
return atlases;
}
/**
* Override to append all animations if selected.
*/
@Override
protected void ok() {
@SuppressWarnings("unchecked")
SelectBox<String> cb = (SelectBox<String>) id.getField();
if (e == null && cb.getSelectedIndex() == 0) {
for (int i = 1; i < cb.getItems().size; i++) {
cb.setSelectedIndex(i);
inputsToModel(true);
// doc.setId(e, cb.getItems().get(i));
if (listener != null)
listener.changed(new ChangeEvent(), this);
}
} else {
super.ok();
}
}
@Override
protected void inputsToModel(boolean create) {
String sourceStr = source.getText();
if (create) {
ActorRenderer renderer = parent.getRenderer();
if (renderer instanceof SpineRenderer) {
e = new SpineAnimationDesc();
if (spineAtlasExists(sourceStr)) {
((SpineAnimationDesc) e).atlas = null;
setVisible(atlas, false);
} else {
if (!atlas.isVisible()) {
setVisible(atlas, true);
}
((SpineAnimationDesc) e).atlas = atlas.getText();
}
} else if (renderer instanceof AtlasRenderer) {
e = new AtlasAnimationDesc();
} else {
e = new AnimationDesc();
}
} else {
HashMap<String, AnimationDesc> animations = ((AnimationRenderer)parent.getRenderer()).getAnimations();
animations.remove(e.id);
}
e.id = id.getText();
e.sound = sound.getText();
e.source = sourceStr;
e.count = Integer.parseInt(count.getText());
e.preload = Boolean.parseBoolean(preload.getText());
e.disposeWhenPlayed = Boolean.parseBoolean(dispose.getText());
e.animationType = Type.valueOf(repeat.getText());
e.inD = Param.parseVector2(in.getText());
e.outD = Param.parseVector2(out.getText());
e.duration = Float.parseFloat(speed.getText());
((AnimationRenderer)parent.getRenderer()).addAnimation(e);
ActorRenderer renderer = parent.getRenderer();
if (renderer instanceof ImageRenderer && Boolean.parseBoolean(localizable.getText()) && e.source != null
&& e.source.length() > 0) {
e.source = I18N.PREFIX + e.source;
}
// TODO UNDO OP
// UndoOp undoOp = new UndoAddElement(doc, e);
// Ctx.project.getUndoStack().add(undoOp);
Ctx.project.setModified();
}
@Override
protected void modelToInputs() {
source.setText(e.source);
if (atlas.isVisible() && e instanceof SpineAnimationDesc)
atlas.setText(((SpineAnimationDesc) e).atlas);
id.setText(e.id);
repeat.setText(e.animationType.toString());
speed.setText(Float.toString(e.duration));
count.setText(Integer.toString(e.count));
in.setText(Param.toStringParam(e.inD));
out.setText(Param.toStringParam(e.outD));
sound.setText(e.sound);
preload.setText(Boolean.toString(e.preload));
dispose.setText(Boolean.toString(e.disposeWhenPlayed));
showHideFieldsDelayCountFields();
ActorRenderer renderer = parent.getRenderer();
if (renderer instanceof ImageRenderer && e.source != null && e.source.length() > 1
&& e.source.charAt(0) == I18N.PREFIX) {
localizable.setText("true");
source.setText(e.source.substring(1));
}
}
}