package com.github.czyzby.tests.reflected; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.scenes.scene2d.Action; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.actions.Actions; import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.ui.CheckBox; import com.badlogic.gdx.scenes.scene2d.ui.Container; import com.badlogic.gdx.scenes.scene2d.ui.Dialog; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextArea; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.github.czyzby.kiwi.util.common.Strings; import com.github.czyzby.kiwi.util.gdx.GdxUtilities; import com.github.czyzby.kiwi.util.gdx.collection.GdxArrays; import com.github.czyzby.kiwi.util.gdx.scene2d.Actors; import com.github.czyzby.lml.annotation.LmlAction; import com.github.czyzby.lml.annotation.LmlActor; import com.github.czyzby.lml.annotation.LmlAfter; import com.github.czyzby.lml.annotation.LmlBefore; import com.github.czyzby.lml.annotation.LmlInject; import com.github.czyzby.lml.parser.LmlParser; import com.github.czyzby.lml.parser.LmlView; import com.github.czyzby.lml.parser.action.ActionContainer; import com.github.czyzby.lml.parser.impl.AbstractLmlView; import com.github.czyzby.lml.parser.impl.tag.macro.NewAttributeLmlMacroTag.AttributeParsingData; import com.github.czyzby.lml.scene2d.ui.reflected.ReflectedLmlDialog; import com.github.czyzby.lml.util.Lml; import com.github.czyzby.lml.util.LmlUtilities; import com.github.czyzby.tests.reflected.widgets.BlinkingLabel; import com.github.czyzby.tests.reflected.widgets.CodeTextArea; import com.github.czyzby.tests.reflected.widgets.LmlSourceHighlighter; import com.github.czyzby.tests.reflected.widgets.MockHighlighter; import com.kotcrab.vis.ui.util.highlight.BaseHighlighter; /** Main view of the application. Since it extends {@link AbstractLmlView}, it is both {@link LmlView} (allowing its * {@link Stage} to be filled) and {@link ActionContainer} (allowing it methods to be reflected and available in LML * templates. Thanks to {@link LmlParser#createView(Class, com.badlogic.gdx.files.FileHandle)} method, parsed root * actors go directly into this view's {@link #getStage()}, and an instance of this class is registered as an action * container with "main" ID (returned by {@link #getViewId()}). * * @author MJ */ public class MainView extends AbstractLmlView { // Contains template to parse: @LmlActor("templateInput") private CodeTextArea templateInput; // Is filled with parsed actors after template processing: @LmlActor("resultTable") private Table resultTable; // Manages buttons. Will be created and filled by the parser. @LmlInject private ButtonManager buttonManager; // Used to create this view. @LmlInject-ing this field would also work. private LmlParser parser; /** Creates a new main view with default stage. */ public MainView() { // You'd generally want to construct the stage with a custom SpriteBatch, sharing it across all views. super(new Stage(new ScreenViewport())); } @Override public String getViewId() { return "main"; } /* Method annotation examples: */ @LmlBefore public void example(final LmlParser parser) { // This method will be invoked before parsing of the main.lml template. You can uncomment the log or some other // method to see how it works. // Gdx.app.log(Lml.LOGGER_TAG, "About to parse main.lml."); // Assigning parser - if you want to use @LmlInject instead to fill the parser field, try commenting this line: this.parser = parser; // Note that both LmlBefore- and LmlAfter-annotated methods can have either no arguments or a single argument: // LmlParser; parser argument will never be null - the parser used to process template will be injected. } @LmlAfter public void example() { // This method will be invoked after main.lml is parsed and MainView's fields are fully injected and processed. // Gdx.app.log(Lml.LOGGER_TAG, "Parsed main.lml with: " + parser); } /* Reflected methods, available in LML views: */ /* templates/main.lml */ /** @return action that sets the action invisible and slowly fades it in. */ public Action fadeIn() { // Used by main window just after view show. return Actions.sequence(Actions.alpha(0f), Actions.fadeIn(0.5f, Interpolation.fade)); } /** Parses template currently stored in template text area and adds the created actors to result table. */ public void parseTemplate() { final String template = templateInput.getText(); parser.getData().addActionContainer(getViewId(), this); // Making our methods available in templates. try { final Array<Actor> actors = parser.parseTemplate(template); resultTable.clear(); for (final Actor actor : actors) { resultTable.add(actor).row(); } } catch (final Exception exception) { onParsingError(exception); } } private void onParsingError(final Exception exception) { // Printing the message without stack trace - we don't want to completely flood the console and its usually not // relevant anyway. Change to '(...), "Unable to parse LML template:", exception);' for stacks. Gdx.app.error(Lml.LOGGER_TAG, "Unable to parse LML template: " + exception); resultTable.clear(); resultTable.add("Error occurred. Sorry."); parser.fillStage(getStage(), Gdx.files.internal("templates/dialogs/error.lml")); } /** Switches the current content of template input to the content of a chosen file. * * @param actor invokes the action. Expected to have an ID that points to a template. */ @LmlAction("switch") public void switchTemplate(final Button actor) { buttonManager.switchCheckedButton(actor); // In LML template, we set each button's ID to a template name. Now we extract these: final String templateName = LmlUtilities.getActorId(actor); templateInput.setText(Gdx.files.internal(toExamplePath(templateName)).readString()); parseTemplate(); } // Converts template name to a example template path. private static String toExamplePath(final String templateName) { return "templates/examples/" + templateName + ".lml"; } /** @return true if current platform is not GWT. */ @LmlAction("isNotGwt") public boolean isNotRunningOnGwt() { return !GdxUtilities.isRunningOnGwt(); } /** @param button if checked, will highlight LML source code. */ @LmlAction("toggleSyntaxHighlight") public void toggleSyntaxHighlight(Button button) { templateInput.setHighlighter(button.isChecked() ? new LmlSourceHighlighter() : new MockHighlighter()); templateInput.processHighlighter(); } /* templates/dialogs/error.lml */ @LmlAction("onErrorApprove") public void acceptError(final Dialog dialog) { ((Label) LmlUtilities.getActorWithId(dialog, "resultMessage")).setText("Thanks!"); } @LmlAction("onErrorDecline") public boolean declineError(final Dialog dialog) { ((Label) LmlUtilities.getActorWithId(dialog, "resultMessage")).setText("It's not like you have a choice."); // Returning true boolean cancels dialog hiding: return ReflectedLmlDialog.CANCEL_HIDING; } /* templates/examples/arrays.lml */ @LmlAction("someMethodReturningArray") public Array<Float> getSomeNumbers() { return GdxArrays.newArray(4.2f, 3f); } /* templates/examples/checkBox.lml */ /** @param checkBox will have its text changed. */ public void switchCase(final CheckBox checkBox) { if (checkBox.isChecked()) { checkBox.setText(checkBox.getText().toString().toUpperCase()); } else { checkBox.setText(checkBox.getText().toString().toLowerCase()); } } /* templates/examples/progressBar.lml */ @LmlAction("load") public void advanceLoadingProgress(final ProgressBar progressBar) { progressBar.setValue(progressBar.getValue() + progressBar.getStepSize() * 5f); } /* templates/examples/actions.lml */ public String someAction() { return "Extracted from method of MainView."; } @LmlAction("someNamedAction") public String getSomeText() { return "@LmlAction-annotated method result."; } /** @param container has to be sized. * @return semi-random size depending on length of container's toString() result. */ @LmlAction({ "size", "getRandomSize" }) public float getSize(final Container<?> container) { return container.toString().length() * 30f * MathUtils.random(); } /** @param button its text will be modified if it's not too long. */ public void pressButton(final TextButton button) { if (button.getText().length() < 30) { button.setText(button.getText() + "!"); } else { button.setText("Isn't it enough?!"); } } /** @param bar will have a random initial value set. */ public void setInitialValue(final ProgressBar bar) { bar.setValue(MathUtils.random(bar.getMinValue(), bar.getMaxValue())); } /* templates/examples/actorMacro.lml */ /** @return a new instance of customized actor. */ public Label getSomeActor() { final Label actor = new Label("Actor created in plain Java.", parser.getData().getDefaultSkin()); actor.setColor(Color.PINK); return actor; } /* templates/examples/evaluate.lml */ @LmlAction("stringConsumingMethod") public String toUpperCase(final String value) { return value.toUpperCase(); } /* templates/examples/while.lml */ /** @return returns a random value between 0 and 1. */ @LmlAction("random") public float getRandomValue() { return MathUtils.random(); } /* templates/examples/newAttribute.lml */ /** @param data contains data necessary to parse LML attribute. */ @LmlAction("upperCaseAttribute") public void processUpperCaseAttribute(final AttributeParsingData data) { if (data.getActor() instanceof Label) { final Label label = (Label) data.getActor(); label.setText( label.getText() + data.getParser().parseString(data.getRawAttributeData(), label).toUpperCase()); } else { data.getParser().throwErrorIfStrict("My attribute supports only labels."); } } /* templates/examples/newTag.lml */ /** @return a new instance of customized widget: {@link BlinkingLabel}; */ public BlinkingLabel getBlinkingLabel() { return new BlinkingLabel(Strings.EMPTY_STRING, parser.getData().getDefaultSkin(), Actors.DEFAULT_STYLE); } @Override public String toString() { // Printing all example buttons and current playground content: return resultTable + buttonManager.printButtons(); } /* templates/examples/importStyleSheet.lml */ @LmlAction("clearLss") public void clearStyleSheets() { parser.getStyleSheet().clearStyles(); } }