--- /dev/null
+/*******************************************************************************\r
+ * Copyright 2011 See AUTHORS file.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ ******************************************************************************/\r
+\r
+package com.badlogic.gdx.scenes.scene2d.ui;\r
+\r
+import java.lang.reflect.Method;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.files.FileHandle;\r
+import com.badlogic.gdx.graphics.Color;\r
+import com.badlogic.gdx.graphics.Texture;\r
+import com.badlogic.gdx.graphics.g2d.BitmapFont;\r
+import com.badlogic.gdx.graphics.g2d.NinePatch;\r
+import com.badlogic.gdx.graphics.g2d.Sprite;\r
+import com.badlogic.gdx.graphics.g2d.TextureAtlas;\r
+import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;\r
+import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasSprite;\r
+import com.badlogic.gdx.graphics.g2d.TextureRegion;\r
+import com.badlogic.gdx.scenes.scene2d.Actor;\r
+import com.badlogic.gdx.scenes.scene2d.utils.Drawable;\r
+import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable;\r
+import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable;\r
+import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;\r
+import com.badlogic.gdx.utils.Array;\r
+import com.badlogic.gdx.utils.Disposable;\r
+import com.badlogic.gdx.utils.GdxRuntimeException;\r
+import com.badlogic.gdx.utils.Json;\r
+import com.badlogic.gdx.utils.Json.ReadOnlySerializer;\r
+import com.badlogic.gdx.utils.ObjectMap;\r
+import com.badlogic.gdx.utils.ObjectMap.Entry;\r
+import com.badlogic.gdx.utils.SerializationException;\r
+import com.badlogic.gwtref.client.ReflectionCache;\r
+\r
+/** A skin has a {@link TextureAtlas} and stores resources for UI widgets to use (texture regions, ninepatches, fonts, colors,\r
+ * etc). Resources are named and can be looked up by name and type. Skin provides useful conversions, such as allowing access to\r
+ * regions in the atlas as ninepatches, sprites, drawables, etc.\r
+ * <p>\r
+ * Resources can be added to a skin using code, or defined in JSON. Names can be used in JSON to reference already defined\r
+ * resources or regions in the atlas. The JSON format is:\r
+ * \r
+ * <pre>\r
+ * {\r
+ * className: {\r
+ * name: value,\r
+ * ...\r
+ * },\r
+ * className: {\r
+ * name: value,\r
+ * ...\r
+ * },\r
+ * ...\r
+ * }\r
+ * </pre>\r
+ * \r
+ * The class name is the fully qualified Java class name for the type of resource. The name is the name of the resource for that\r
+ * class, and the value is the serialized resource or style.\r
+ * @author Nathan Sweet */\r
+public class Skin implements Disposable {\r
+ ObjectMap<Class, ObjectMap<String, Object>> resources = new ObjectMap();\r
+ TextureAtlas atlas;\r
+\r
+ public Skin (TextureAtlas atlas) {\r
+ this.atlas = atlas;\r
+ add(atlas);\r
+ }\r
+\r
+ public Skin (FileHandle skinFile, TextureAtlas atlas) {\r
+ this.atlas = atlas;\r
+ add(atlas);\r
+ load(skinFile);\r
+ }\r
+\r
+ public Skin (FileHandle skinFile) {\r
+ this.atlas = new TextureAtlas(skinFile.sibling(skinFile.nameWithoutExtension() + ".atlas"));\r
+ add(atlas);\r
+ load(skinFile);\r
+ }\r
+\r
+ public void load (FileHandle skinFile) {\r
+ try {\r
+ getJsonLoader(skinFile).fromJson(Skin.class, skinFile);\r
+ } catch (SerializationException ex) {\r
+ throw new SerializationException("Error reading file: " + skinFile, ex);\r
+ }\r
+ }\r
+\r
+ private void add (TextureAtlas atlas) {\r
+ Array<AtlasRegion> regions = atlas.getRegions();\r
+ for (int i = 0, n = regions.size; i < n; i++) {\r
+ AtlasRegion region = regions.get(i);\r
+ add(region.name, region, TextureRegion.class);\r
+ }\r
+ }\r
+\r
+ public void add (String name, Object resource) {\r
+ add(name, resource, resource.getClass());\r
+ }\r
+\r
+ public void add (String name, Object resource, Class type) {\r
+ if (name == null) throw new IllegalArgumentException("name cannot be null.");\r
+ if (resource == null) throw new IllegalArgumentException("resource cannot be null.");\r
+ ObjectMap<String, Object> typeResources = resources.get(type);\r
+ if (typeResources == null) {\r
+ typeResources = new ObjectMap();\r
+ resources.put(type, typeResources);\r
+ }\r
+ typeResources.put(name, resource);\r
+ }\r
+\r
+ public <T> T get (Class<T> type) {\r
+ return get("default", type);\r
+ }\r
+\r
+ public <T> T get (String name, Class<T> type) {\r
+ if (name == null) throw new IllegalArgumentException("name cannot be null.");\r
+ if (type == null) throw new IllegalArgumentException("type cannot be null.");\r
+\r
+ if (type == Drawable.class) return (T)getDrawable(name);\r
+ if (type == TextureRegion.class) return (T)getRegion(name);\r
+ if (type == NinePatch.class) return (T)getPatch(name);\r
+ if (type == Sprite.class) return (T)getSprite(name);\r
+\r
+ ObjectMap<String, Object> typeResources = resources.get(type);\r
+ if (typeResources == null) throw new GdxRuntimeException("No " + type.getName() + " registered with name: " + name);\r
+ Object resource = typeResources.get(name);\r
+ if (resource == null) throw new GdxRuntimeException("No " + type.getName() + " registered with name: " + name);\r
+ return (T)resource;\r
+ }\r
+\r
+ public <T> T optional (String name, Class<T> type) {\r
+ if (name == null) throw new IllegalArgumentException("name cannot be null.");\r
+ if (type == null) throw new IllegalArgumentException("type cannot be null.");\r
+ ObjectMap<String, Object> typeResources = resources.get(type);\r
+ if (typeResources == null) return null;\r
+ return (T)typeResources.get(name);\r
+ }\r
+\r
+ public boolean has (String name, Class type) {\r
+ ObjectMap<String, Object> typeResources = resources.get(type);\r
+ if (typeResources == null) return false;\r
+ return typeResources.containsKey(name);\r
+ }\r
+\r
+ /** Returns the name to resource mapping for the specified type, or null if no resources of that type exist. */\r
+ public <T> ObjectMap<String, T> getAll (Class<T> type) {\r
+ return (ObjectMap<String, T>)resources.get(type);\r
+ }\r
+\r
+ public Color getColor (String name) {\r
+ return get(name, Color.class);\r
+ }\r
+\r
+ public BitmapFont getFont (String name) {\r
+ return get(name, BitmapFont.class);\r
+ }\r
+\r
+ public TextureRegion getRegion (String name) {\r
+ TextureRegion region = optional(name, TextureRegion.class);\r
+ if (region != null) return region;\r
+\r
+ Texture texture = optional(name, Texture.class);\r
+ if (texture == null) throw new GdxRuntimeException("No TextureRegion or Texture registered with name: " + name);\r
+ region = new TextureRegion(texture);\r
+ add(name, region, Texture.class);\r
+ return region;\r
+ }\r
+\r
+ /** Returns a registered ninepatch. If no ninepatch is found but a region exists with the name, the region is returned as a\r
+ * ninepatch. If the region is an {@link AtlasRegion} then the {@link AtlasRegion#splits} are used. */\r
+ public NinePatch getPatch (String name) {\r
+ NinePatch patch = optional(name, NinePatch.class);\r
+ if (patch != null) return patch;\r
+\r
+ try {\r
+ TextureRegion region = getRegion(name);\r
+ if (region instanceof AtlasRegion) {\r
+ int[] splits = ((AtlasRegion)region).splits;\r
+ if (splits != null) patch = new NinePatch(region, splits[0], splits[1], splits[2], splits[3]);\r
+ }\r
+ if (patch == null) patch = new NinePatch(region);\r
+ add(name, patch, NinePatch.class);\r
+ return patch;\r
+ } catch (GdxRuntimeException ex) {\r
+ throw new GdxRuntimeException("No NinePatch, TextureRegion, or Texture registered with name: " + name);\r
+ }\r
+ }\r
+\r
+ public Sprite getSprite (String name) {\r
+ Sprite sprite = optional(name, Sprite.class);\r
+ if (sprite != null) return sprite;\r
+\r
+ try {\r
+ TextureRegion textureRegion = getRegion(name);\r
+ if (textureRegion instanceof AtlasRegion) {\r
+ AtlasRegion region = (AtlasRegion)textureRegion;\r
+ if (region.rotate || region.packedWidth != region.originalWidth || region.packedHeight != region.originalHeight)\r
+ sprite = new AtlasSprite(region);\r
+ }\r
+ if (sprite == null) sprite = new Sprite(textureRegion);\r
+ add(name, sprite, NinePatch.class);\r
+ return sprite;\r
+ } catch (GdxRuntimeException ex) {\r
+ throw new GdxRuntimeException("No NinePatch, TextureRegion, or Texture registered with name: " + name);\r
+ }\r
+ }\r
+\r
+ public Drawable getDrawable (String name) {\r
+ Drawable drawable = optional(name, Drawable.class);\r
+ if (drawable != null) return drawable;\r
+\r
+ // Use texture or texture region. If it has splits, use ninepatch. If it has rotation or whitespace stripping, use sprite.\r
+ try {\r
+ TextureRegion textureRegion = getRegion(name);\r
+ if (textureRegion instanceof AtlasRegion) {\r
+ AtlasRegion region = (AtlasRegion)textureRegion;\r
+ if (region.splits != null)\r
+ drawable = new NinePatchDrawable(getPatch(name));\r
+ else if (region.rotate || region.packedWidth != region.originalWidth || region.packedHeight != region.originalHeight)\r
+ drawable = new SpriteDrawable(getSprite(name));\r
+ }\r
+ if (drawable == null) drawable = new TextureRegionDrawable(textureRegion);\r
+ } catch (GdxRuntimeException ignored) {\r
+ }\r
+\r
+ // Check for explicit registration of ninepatch or sprite.\r
+ if (drawable == null) {\r
+ NinePatch patch = optional(name, NinePatch.class);\r
+ if (patch != null)\r
+ drawable = new NinePatchDrawable(patch);\r
+ else {\r
+ Sprite sprite = optional(name, Sprite.class);\r
+ if (sprite != null)\r
+ drawable = new SpriteDrawable(sprite);\r
+ else\r
+ throw new GdxRuntimeException("No Drawable, NinePatch, TextureRegion, Texture, or Sprite registered with name: "\r
+ + name);\r
+ }\r
+ }\r
+\r
+ add(name, drawable, Drawable.class);\r
+ return drawable;\r
+ }\r
+\r
+ /** Returns the name of the specified style object, or null if it is not in the skin. This compares potentially every style\r
+ * object in the skin of the same type as the specified style, which may be a somewhat expensive operation. */\r
+ public String find (Object resource) {\r
+ if (resource == null) throw new IllegalArgumentException("style cannot be null.");\r
+ ObjectMap<String, Object> typeResources = resources.get(resource.getClass());\r
+ if (typeResources == null) return null;\r
+ return typeResources.findKey(resource, true);\r
+ }\r
+\r
+ public Drawable newDrawable (String name) {\r
+ Drawable drawable = getDrawable(name);\r
+ if (drawable instanceof TextureRegionDrawable) return new TextureRegionDrawable((TextureRegionDrawable)drawable);\r
+ if (drawable instanceof NinePatchDrawable) return new NinePatchDrawable((NinePatchDrawable)drawable);\r
+ if (drawable instanceof SpriteDrawable) return new SpriteDrawable((SpriteDrawable)drawable);\r
+ throw new GdxRuntimeException("Unable to copy, unknown drawable type: " + drawable.getClass());\r
+ }\r
+\r
+ public Drawable newDrawable (String name, Color tint) {\r
+ Drawable drawable = getDrawable(name);\r
+ if (drawable instanceof TextureRegionDrawable) {\r
+ Sprite sprite = new Sprite(((TextureRegionDrawable)drawable).getRegion());\r
+ sprite.setColor(tint);\r
+ return new SpriteDrawable(sprite);\r
+ }\r
+ if (drawable instanceof NinePatchDrawable) {\r
+ NinePatchDrawable patchDrawable = new NinePatchDrawable((NinePatchDrawable)drawable);\r
+ patchDrawable.setPatch(new NinePatch(patchDrawable.getPatch(), tint));\r
+ return patchDrawable;\r
+ }\r
+ if (drawable instanceof SpriteDrawable) {\r
+ SpriteDrawable spriteDrawable = new SpriteDrawable((SpriteDrawable)drawable);\r
+ Sprite sprite = new Sprite(spriteDrawable.getSprite());\r
+ sprite.setColor(tint);\r
+ spriteDrawable.setSprite(sprite);\r
+ return spriteDrawable;\r
+ }\r
+ throw new GdxRuntimeException("Unable to copy, unknown drawable type: " + drawable.getClass());\r
+ }\r
+\r
+ /** Sets the style on the actor to disabled or enabled. This is done by appending "-disabled" to the style name when enabled is\r
+ * false, and removing "-disabled" from the style name when enabled is true. A method named "getStyle" is called the actor via\r
+ * reflection and the name of that style is found in the skin. If the actor doesn't have a "getStyle" method or the style was\r
+ * not found in the skin, no exception is thrown and the actor is left unchanged. */\r
+ public void setEnabled (Actor actor, boolean enabled) {\r
+ actor.setTouchable(enabled);\r
+ // Get current style.\r
+ com.badlogic.gwtref.client.Method method = findMethod(actor.getClass(), "getStyle");\r
+ if (method == null) return;\r
+ Object style;\r
+ try {\r
+ style = method.invoke(actor);\r
+ } catch (Exception ignored) {\r
+ return;\r
+ }\r
+ // Determine new style.\r
+ String name = find(style);\r
+ if (name == null) return;\r
+ name = name.replace("-disabled", "") + (enabled ? "" : "-disabled");\r
+ style = get(name, style.getClass());\r
+ // Set new style.\r
+ method = findMethod(actor.getClass(), "setStyle");\r
+ if (method == null) return;\r
+ try {\r
+ method.invoke(actor, style);\r
+ } catch (Exception ignored) {\r
+ }\r
+ }\r
+\r
+ /** Returns the {@link TextureAtlas} that resources in this skin reference. */\r
+ public TextureAtlas getAtlas () {\r
+ return atlas;\r
+ }\r
+\r
+ /** Disposes the {@link TextureAtlas} and all {@link Disposable} resources in the skin. */\r
+ public void dispose () {\r
+ atlas.dispose();\r
+ for (ObjectMap<String, Object> entry : resources.values()) {\r
+ for (Object resource : entry.values())\r
+ if (resource instanceof Disposable) ((Disposable)resource).dispose();\r
+ }\r
+ }\r
+\r
+ protected Json getJsonLoader (final FileHandle skinFile) {\r
+ final Skin skin = this;\r
+\r
+ final Json json = new Json() {\r
+ @Override\r
+ public <T> T readValue (Class<T> type, Class elementType, Object jsonData) {\r
+ // If the JSON is a string but the type is not, look up the actual value by name. \r
+ if (jsonData instanceof String && !ReflectionCache.getType(type).isAssignableFrom(ReflectionCache.getType(CharSequence.class))) return get((String)jsonData, type);\r
+ return super.readValue(type, elementType, jsonData);\r
+ }\r
+ };\r
+ json.setTypeName(null);\r
+ json.setUsePrototypes(false);\r
+\r
+ json.setSerializer(Skin.class, new ReadOnlySerializer<Skin>() {\r
+ public Skin read (Json json, Object jsonData, Class ignored) {\r
+ ObjectMap<String, ObjectMap> typeToValueMap = (ObjectMap)jsonData;\r
+ for (Entry<String, ObjectMap> typeEntry : typeToValueMap.entries()) {\r
+ String className = typeEntry.key;\r
+ ObjectMap<String, ObjectMap> valueMap = (ObjectMap)typeEntry.value;\r
+ try {\r
+ readNamedObjects(json, ReflectionCache.forName(className).getClassOfType(), valueMap);\r
+ } catch (ClassNotFoundException ex) {\r
+ throw new SerializationException(ex);\r
+ }\r
+ }\r
+ return skin;\r
+ }\r
+\r
+ private void readNamedObjects (Json json, Class type, ObjectMap<String, ObjectMap> valueMap) {\r
+ Class addType = type == TintedDrawable.class ? Drawable.class : type;\r
+ for (Entry<String, ObjectMap> valueEntry : valueMap.entries()) {\r
+ String name = valueEntry.key;\r
+ Object object = json.readValue(type, valueEntry.value);\r
+ if (object == null) continue;\r
+ try {\r
+ add(name, object, addType);\r
+ } catch (Exception ex) {\r
+ throw new SerializationException("Error reading " + type.getName() + ": " + valueEntry.key, ex);\r
+ }\r
+ }\r
+ }\r
+ });\r
+\r
+ json.setSerializer(BitmapFont.class, new ReadOnlySerializer<BitmapFont>() {\r
+ public BitmapFont read (Json json, Object jsonData, Class type) {\r
+ String path = json.readValue("file", String.class, jsonData);\r
+\r
+ FileHandle fontFile = skinFile.parent().child(path);\r
+ if (!fontFile.exists()) fontFile = Gdx.files.internal(path);\r
+ if (!fontFile.exists()) throw new SerializationException("Font file not found: " + fontFile);\r
+\r
+ // Use a region with the same name as the font, else use a PNG file in the same directory as the FNT file.\r
+ String regionName = fontFile.nameWithoutExtension();\r
+ try {\r
+ TextureRegion region = skin.optional(regionName, TextureRegion.class);\r
+ if (region != null)\r
+ return new BitmapFont(fontFile, region, false);\r
+ else {\r
+ FileHandle imageFile = fontFile.parent().child(regionName + ".png");\r
+ if (imageFile.exists())\r
+ return new BitmapFont(fontFile, imageFile, false);\r
+ else\r
+ return new BitmapFont(fontFile, false);\r
+ }\r
+ } catch (RuntimeException ex) {\r
+ throw new SerializationException("Error loading bitmap font: " + fontFile, ex);\r
+ }\r
+ }\r
+ });\r
+\r
+ json.setSerializer(Color.class, new ReadOnlySerializer<Color>() {\r
+ public Color read (Json json, Object jsonData, Class type) {\r
+ if (jsonData instanceof String) return get((String)jsonData, Color.class);\r
+ ObjectMap map = (ObjectMap)jsonData;\r
+ float r = json.readValue("r", float.class, 0f, jsonData);\r
+ float g = json.readValue("g", float.class, 0f, jsonData);\r
+ float b = json.readValue("b", float.class, 0f, jsonData);\r
+ float a = json.readValue("a", float.class, 1f, jsonData);\r
+ return new Color(r, g, b, a);\r
+ }\r
+ });\r
+\r
+ json.setSerializer(TintedDrawable.class, new ReadOnlySerializer() {\r
+ public Object read (Json json, Object jsonData, Class type) {\r
+ String name = json.readValue("name", String.class, jsonData);\r
+ Color color = json.readValue("color", Color.class, jsonData);\r
+ return newDrawable(name, color);\r
+ }\r
+ });\r
+\r
+ return json;\r
+ }\r
+\r
+ static private com.badlogic.gwtref.client.Method findMethod (Class type, String name) {\r
+ com.badlogic.gwtref.client.Method[] methods = ReflectionCache.getType(type).getMethods();\r
+ for (int i = 0, n = methods.length; i < n; i++) {\r
+ com.badlogic.gwtref.client.Method method = methods[i];\r
+ if (method.getName().equals(name)) return method;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /** @author Nathan Sweet */\r
+ static public class TintedDrawable {\r
+ public String name;\r
+ public Color color;\r
+ }\r
+}\r
--- /dev/null
+/*******************************************************************************\r
+ * Copyright 2011 See AUTHORS file.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ ******************************************************************************/\r
+\r
+package com.badlogic.gdx.scenes.scene2d.ui;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.math.Rectangle;\r
+import com.badlogic.gdx.scenes.scene2d.Actor;\r
+import com.badlogic.gdx.scenes.scene2d.Group;\r
+import com.badlogic.gdx.scenes.scene2d.utils.Layout;\r
+import com.badlogic.gdx.utils.Array;\r
+import com.badlogic.gwtref.client.ReflectionCache;\r
+import com.esotericsoftware.tablelayout.BaseTableLayout.Debug;\r
+import com.esotericsoftware.tablelayout.Toolkit;\r
+\r
+/** The libgdx implementation of the table layout functionality.\r
+ * @author Nathan Sweet */\r
+class TableToolkit extends Toolkit<Actor, Table, TableLayout> {\r
+ static boolean drawDebug;\r
+\r
+ public void addChild (Actor parent, Actor child) {\r
+ child.remove();\r
+ try {\r
+ ReflectionCache.getType(parent.getClass()).getMethod("setWidget", Actor.class).invoke(parent, child);\r
+ return;\r
+ } catch (Exception ignored) {\r
+ // FIXME this is bad\r
+ Gdx.app.log("TableToolkit", "Couldn't call setWidget", ignored);\r
+ }\r
+ ((Group)parent).addActor(child);\r
+ }\r
+\r
+ public void removeChild (Actor parent, Actor child) {\r
+ ((Group)parent).removeActor(child);\r
+ }\r
+\r
+ public float getMinWidth (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getMinWidth();\r
+ return actor.getWidth();\r
+ }\r
+\r
+ public float getMinHeight (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getMinHeight();\r
+ return actor.getHeight();\r
+ }\r
+\r
+ public float getPrefWidth (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getPrefWidth();\r
+ return actor.getWidth();\r
+ }\r
+\r
+ public float getPrefHeight (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getPrefHeight();\r
+ return actor.getHeight();\r
+ }\r
+\r
+ public float getMaxWidth (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getMaxWidth();\r
+ return 0;\r
+ }\r
+\r
+ public float getMaxHeight (Actor actor) {\r
+ if (actor instanceof Layout) return ((Layout)actor).getMaxHeight();\r
+ return 0;\r
+ }\r
+\r
+ public float getWidth (Actor widget) {\r
+ return widget.getWidth();\r
+ }\r
+\r
+ public float getHeight (Actor widget) {\r
+ return widget.getHeight();\r
+ }\r
+\r
+ public void clearDebugRectangles (TableLayout layout) {\r
+ if (layout.debugRects != null) layout.debugRects.clear();\r
+ }\r
+\r
+ public void addDebugRectangle (TableLayout layout, Debug type, float x, float y, float w, float h) {\r
+ drawDebug = true;\r
+ if (layout.debugRects == null) layout.debugRects = new Array();\r
+ layout.debugRects.add(new DebugRect(type, x, layout.getTable().getHeight() - y, w, h));\r
+ }\r
+\r
+ static class DebugRect extends Rectangle {\r
+ final Debug type;\r
+\r
+ public DebugRect (Debug type, float x, float y, float width, float height) {\r
+ super(x, y, width, height);\r
+ this.type = type;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*******************************************************************************\r
+ * Copyright 2011 See AUTHORS file.\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ ******************************************************************************/\r
+\r
+package com.badlogic.gdx.utils;\r
+\r
+import java.io.IOException;\r
+import java.io.Writer;\r
+import java.util.regex.Pattern;\r
+\r
+/** Builder style API for emitting JSON.\r
+ * @author Nathan Sweet */\r
+public class JsonWriter extends Writer {\r
+ Writer writer;\r
+ private final Array<JsonObject> stack = new Array();\r
+ private JsonObject current;\r
+ private boolean named;\r
+ private OutputType outputType = OutputType.json;\r
+\r
+ public JsonWriter (Writer writer) {\r
+ this.writer = writer;\r
+ }\r
+\r
+ public void setOutputType (OutputType outputType) {\r
+ this.outputType = outputType;\r
+ }\r
+\r
+ public JsonWriter name (String name) throws IOException {\r
+ if (current == null || current.array) throw new IllegalStateException("Current item must be an object.");\r
+ if (!current.needsComma)\r
+ current.needsComma = true;\r
+ else\r
+ writer.write(',');\r
+ writer.write(outputType.quoteName(name));\r
+ writer.write(':');\r
+ named = true;\r
+ return this;\r
+ }\r
+\r
+ public JsonWriter object () throws IOException {\r
+ if (current != null) {\r
+ if (current.array) {\r
+ if (!current.needsComma)\r
+ current.needsComma = true;\r
+ else\r
+ writer.write(',');\r
+ } else {\r
+ if (!named && !current.array) throw new IllegalStateException("Name must be set.");\r
+ named = false;\r
+ }\r
+ }\r
+ stack.add(current = new JsonObject(false));\r
+ return this;\r
+ }\r
+\r
+ public JsonWriter array () throws IOException {\r
+ if (current != null) {\r
+ if (current.array) {\r
+ if (!current.needsComma)\r
+ current.needsComma = true;\r
+ else\r
+ writer.write(',');\r
+ } else {\r
+ if (!named && !current.array) throw new IllegalStateException("Name must be set.");\r
+ named = false;\r
+ }\r
+ }\r
+ stack.add(current = new JsonObject(true));\r
+ return this;\r
+ }\r
+\r
+ public JsonWriter value (Object value) throws IOException {\r
+ if (current != null) {\r
+ if (current.array) {\r
+ if (!current.needsComma)\r
+ current.needsComma = true;\r
+ else\r
+ writer.write(',');\r
+ } else {\r
+ if (!named) throw new IllegalStateException("Name must be set.");\r
+ named = false;\r
+ }\r
+ }\r
+ if (value == null || value instanceof Number || value instanceof Boolean) {\r
+ writer.write(String.valueOf(value));\r
+ } else {\r
+ writer.write(outputType.quoteValue(value.toString()));\r
+ }\r
+ return this;\r
+ }\r
+\r
+ public JsonWriter object (String name) throws IOException {\r
+ return name(name).object();\r
+ }\r
+\r
+ public JsonWriter array (String name) throws IOException {\r
+ return name(name).array();\r
+ }\r
+\r
+ public JsonWriter set (String name, Object value) throws IOException {\r
+ return name(name).value(value);\r
+ }\r
+\r
+ public JsonWriter pop () throws IOException {\r
+ if (named) throw new IllegalStateException("Expected an object, array, or value since a name was set.");\r
+ stack.pop().close();\r
+ current = stack.size == 0 ? null : stack.peek();\r
+ return this;\r
+ }\r
+\r
+ public void write (char[] cbuf, int off, int len) throws IOException {\r
+ writer.write(cbuf, off, len);\r
+ }\r
+\r
+ public void flush () throws IOException {\r
+ writer.flush();\r
+ }\r
+\r
+ public void close () throws IOException {\r
+ while (stack.size > 0)\r
+ pop();\r
+ writer.close();\r
+ }\r
+\r
+ private class JsonObject {\r
+ final boolean array;\r
+ boolean needsComma;\r
+\r
+ JsonObject (boolean array) throws IOException {\r
+ this.array = array;\r
+ writer.write(array ? '[' : '{');\r
+ }\r
+\r
+ void close () throws IOException {\r
+ writer.write(array ? ']' : '}');\r
+ }\r
+ }\r
+\r
+ static public enum OutputType {\r
+ /** Normal JSON, with all its quotes. */\r
+ json,\r
+ /** Like JSON, but names are only quoted if necessary. */\r
+ javascript,\r
+ /** Like JSON, but names and values are only quoted if necessary. */\r
+ minimal;\r
+\r
+ // FIXME Avian regex matcher can't do that...\r
+// static private Pattern javascriptPattern = Pattern.compile("[a-zA-Z_$][a-zA-Z_$0-9]*");\r
+// static private Pattern minimalPattern = Pattern.compile("[a-zA-Z_$][^:}\\], ]*");\r
+\r
+ public String quoteValue (String value) {\r
+ value = value.replace("\\", "\\\\");\r
+// if (this == OutputType.minimal && !value.equals("true") && !value.equals("false") && !value.equals("null")\r
+// && minimalPattern.matcher(value).matches()) return value;\r
+ return '"' + value.replace("\"", "\\\"") + '"';\r
+ }\r
+\r
+ public String quoteName (String value) {\r
+ value = value.replace("\\", "\\\\");\r
+// switch (this) {\r
+// case minimal:\r
+// if (minimalPattern.matcher(value).matches()) return value;\r
+// return '"' + value.replace("\"", "\\\"") + '"';\r
+// case javascript:\r
+// if (javascriptPattern.matcher(value).matches()) return value;\r
+// return '"' + value.replace("\"", "\\\"") + '"';\r
+// default:\r
+ return '"' + value.replace("\"", "\\\"") + '"';\r
+// }\r
+ }\r
+ }\r
+}\r