001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.Gdx;
004import com.badlogic.gdx.assets.AssetDescriptor;
005import com.badlogic.gdx.assets.AssetManager;
006import com.badlogic.gdx.graphics.Color;
007import com.badlogic.gdx.graphics.Pixmap;
008import com.badlogic.gdx.graphics.Texture;
009import com.badlogic.gdx.graphics.g2d.Batch;
010import com.badlogic.gdx.graphics.g2d.BitmapFont;
011import com.badlogic.gdx.graphics.g2d.SpriteBatch;
012import com.badlogic.gdx.graphics.g2d.TextureRegion;
013import com.badlogic.gdx.graphics.glutils.ShaderProgram;
014import com.badlogic.gdx.scenes.scene2d.Actor;
015import com.badlogic.gdx.scenes.scene2d.ui.Image;
016import com.badlogic.gdx.scenes.scene2d.ui.Label;
017import com.badlogic.gdx.utils.Align;
018import com.badlogic.gdx.utils.Disposable;
019import squidpony.IColorCenter;
020
021import java.util.*;
022
023/**
024 * Class for creating text blocks.
025 *
026 * This class defaults to having no padding and having no font set. You can use a
027 * default square or narrow font by calling the appropriate method, or set the font
028 * to any AngelCode bitmap font on the classpath (typically in libGDX, this would be
029 * in the assets folder; these fonts can be created by Hiero in the libGDX tools,
030 * see https://github.com/libgdx/libgdx/wiki/Hiero for more)
031 *
032 * After all settings are set, one of the initialization methods must be called
033 * before the factory can be used.
034 *
035 * In order to easily support Unicode, strings are treated as a series of code
036 * points.
037 *
038 * All images have transparent backgrounds.
039 *
040 * @author Eben Howard - http://squidpony.com - howard@squidpony.com
041 * @author Tommy Ettinger
042 */
043public class TextCellFactory implements Disposable {
044
045    /**
046     * The commonly used symbols in roguelike games.
047     */
048    public static final String DEFAULT_FITTING = "@!#$%^&*()_+1234567890-=~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;:,'\"{}?/\\ ",
049    LINE_FITTING = "┼├┤┴┬┌┐└┘│─", SQUID_FITTING = DEFAULT_FITTING + LINE_FITTING;
050
051        /**
052         * The {@link AssetManager} from where to load the font. Use it to share
053         * loading of a font's file across multiple factories.
054         */
055        protected /* Nullable */AssetManager assetManager;
056    public BitmapFont bmpFont = null;
057    protected Texture block = null;
058    protected String fitting = SQUID_FITTING;
059    protected IColorCenter<Color> scc;
060    protected int leftPadding = 0, rightPadding = 0, topPadding = 0, bottomPadding = 0;
061    protected int width = 1, height = 1;
062    protected float actualCellWidth = 1, actualCellHeight = 1;
063    protected float distanceFieldScaleX = 36f, distanceFieldScaleY = 36f;
064    private boolean initialized = false, initializedByFont = false, initializedBySize = false;
065    protected boolean distanceField = false;
066    protected ShaderProgram shader;
067    protected float smoothingMultiplier = 1f;
068    protected float descent, lineHeight;
069    private Label.LabelStyle style;
070    protected java.util.LinkedHashMap<String, String> swap = new LinkedHashMap<>(32);
071
072
073    /**
074     * Creates a default valued factory. One of the initialization methods must
075     * be called before this factory can be used!
076     */
077    public TextCellFactory() {
078        this(null);
079    }
080
081        /**
082         * A default valued factory that uses the given {@link AssetManager} to load
083         * the font file. Use this constructor if you are likely to load the same
084         * font over and over (recall that, without an {@link AssetManager}, each
085         * instance of {@link TextCellFactory} will load its font from disk). This
086     * primarily matters if you are using fonts not bundled with SquidLib, since
087     * accessing a BitmapFont with a method (not a String) from DefaultResources
088     * caches the BitmapFont already.
089         *
090         * @param assetManager an ordinary libGDX AssetManager
091         */
092    public TextCellFactory(/* Nullable */ AssetManager assetManager) {
093        this.assetManager = assetManager;
094        scc = DefaultResources.getSCC();
095    }
096
097    public TextCellFactory copy()
098    {
099        TextCellFactory next = new TextCellFactory(assetManager);
100        //next.bmpFont = bmpFont;
101        next.bmpFont = new BitmapFont(new BitmapFont.BitmapFontData(bmpFont.getData().getFontFile(), false),
102                bmpFont.getRegions(), bmpFont.usesIntegerPositions());
103        next.block = block;
104        next.swap = new LinkedHashMap<>(swap);
105        next.distanceField = distanceField;
106        next.distanceFieldScaleX = distanceFieldScaleX;
107        next.distanceFieldScaleY = distanceFieldScaleY;
108        next.shader = null;
109        next.fitting = fitting;
110        next.height = height;
111        next.width = width;
112        next.actualCellWidth = actualCellWidth;
113        next.actualCellHeight = actualCellHeight;
114        next.descent = descent;
115        next.lineHeight = lineHeight;
116        //next.modifiedHeight = modifiedHeight;
117        next.smoothingMultiplier = smoothingMultiplier;
118        next.scc = scc;
119        if(initializedBySize)
120            next.initBySize();
121        else if(initializedByFont)
122            next.initByFont();
123        return next;
124    }
125    /**
126     * Initializes the factory to then be able to create text cells on demand.
127     *
128     * Will match the width and height to 12 and 12, scaling the font to fit.
129     *
130     * Calling this after the factory has already been initialized will
131     * re-initialize it.
132     *
133     * @return this for method chaining
134     */
135    public TextCellFactory initByFont() {
136        bmpFont.setFixedWidthGlyphs(fitting);
137        width = (int)bmpFont.getSpaceWidth();
138        lineHeight = bmpFont.getLineHeight();
139        height = (int)(lineHeight);
140        descent = bmpFont.getDescent();
141
142        actualCellWidth = width;
143        actualCellHeight = height;
144        //modifiedHeight = height;
145        Pixmap temp = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
146        temp.setColor(Color.WHITE);
147        temp.fill();
148        block = new Texture(1, 1, Pixmap.Format.RGBA8888);
149        block.draw(temp, 0, 0);
150        temp.dispose();
151        style = new Label.LabelStyle(bmpFont, null);
152        initialized = true;
153        initializedByFont = true;
154        return this;
155    }
156
157    /**
158     * Initializes the factory to then be able to create text cells on demand.
159     *
160     * Will strictly use the provided width and height values to size the cells.
161     *
162     * Calling this after the factory has already been initialized will
163     * re-initialize it.
164     *
165     * @return this for method chaining
166     */
167    public TextCellFactory initBySize() {
168        //bmpFont.setFixedWidthGlyphs(fitting);
169        Pixmap temp = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
170        temp.setColor(Color.WHITE);
171        temp.fill();
172        block = new Texture(1, 1, Pixmap.Format.RGBA8888);
173        block.draw(temp, 0, 0);
174        temp.dispose();
175        if(distanceField)
176        {
177            bmpFont.getData().setScale(width / distanceFieldScaleX, height / distanceFieldScaleY);
178
179            shader = new ShaderProgram(DefaultResources.vertexShader, DefaultResources.fragmentShader);
180            if (!shader.isCompiled()) {
181                Gdx.app.error("shader", "Distance Field font shader compilation failed:\n" + shader.getLog());
182            }
183            lineHeight = bmpFont.getLineHeight();
184            //lineTweak = lineHeight / 20f;
185            //distanceFieldScaleX *= (((float)width) / height) / (distanceFieldScaleX / distanceFieldScaleY);
186        }
187        else {
188            shader = SpriteBatch.createDefaultShader();
189            lineHeight = bmpFont.getLineHeight();
190            //lineTweak = lineHeight * 0.0625f;
191        }
192        descent = bmpFont.getDescent();
193        style = new Label.LabelStyle(bmpFont, null);
194        initialized = true;
195        initializedBySize = true;
196        return this;
197    }
198
199    /**
200     * Initializes the factory to then be able to create text cells on demand.
201     *
202     * (This is identical to initBySize() when using libGDX.)
203     *
204     * @return this for method chaining
205     */
206    public TextCellFactory initVerbatim() {
207        return initBySize();
208    }
209
210    /**
211     * Returns the font used by this factory.
212     *
213     * @return the font
214     */
215    public BitmapFont font() {
216        return bmpFont;
217    }
218
219    /**
220     * Sets this factory to use the provided font.
221     *
222     * This is a way to complete a needed step; the font must be set before initializing, which can be done by a few
223     * methods in this class.
224     *
225     * This should be called with an argument such as "Rogue-Zodiac-6x12.fnt", that is, it should have the .fnt
226     * extension as opposed to the .png that accompanies such a bitmap font. The bitmap font should be either in the
227     * internal folder that libGDX knows about, which means it is in the assets folder of your project usually, or it
228     * can be on the classpath, which mostly applies to these resources bundled with SquidLib:
229     * <ul>
230     *     <li>DefaultResources.squareName = "Zodiac-Square-12x12.fnt"</li>
231     *     <li>DefaultResources.narrowName = "Rogue-Zodiac-6x12.fnt"</li>
232     *     <li>DefaultResources.unicodeName = "Mandrill-6x16.fnt"</li>
233     *     <li>DefaultResources.smoothName = "Inconsolata-LGC-8x18.fnt"</li>
234     *     <li>DefaultResources.squareNameLarge = "Zodiac-Square-24x24.fnt"</li>
235     *     <li>DefaultResources.narrowNameLarge = "Rogue-Zodiac-12x24.fnt"</li>
236     *     <li>DefaultResources.unicodeNameLarge = "Mandrill-12x32.fnt"</li>
237     *     <li>DefaultResources.smoothNameLarge = "Inconsolata-LGC-12x24.fnt"</li>
238     *     <li>DefaultResources.narrowNameExtraLarge = "Rogue-Zodiac-18x36.fnt"</li>
239     *     <li>There is also a sequence of resized versions of Inconsolata LGC, altered to fit in a square area. These
240     *     don't have names in DefaultResources; you should use the overload of font() that takes a BitmapFont if you
241     *     want to use the multiple, increasingly-resized versions.</li>
242     * </ul>
243     * "Rogue-Zodiac-12x24.fnt", which is easily accessed by the field DefaultResources.narrowNameLarge , can also
244     * be set using TextCellFactory.defaultNarrowFont() instead of font(). "Zodiac-Square-12x12.fnt", also accessible
245     * as DefaultResources.squareName , can be set using TextCellFactory.defaultSquareFont() instead of font().
246     * "Inconsolata-LGC-12x24.fnt", also accessible as DefaultResources.smoothNameLarge , can be set using
247     * TextCellFactory.defaultFont() instead of font(). All three of these alternatives will cache the BitmapFont if
248     * the same one is requested later, but this font() method will not.
249     * <br>
250     * See https://github.com/libgdx/libgdx/wiki/Hiero for some ways to create a bitmap font this can use. Several fonts
251     * in this list were created using Hiero (not Hiero4), and several were created with AngelCode's BMFont tool.
252     *
253     * @param fontpath the path to the font to use
254     * @return this factory for method chaining
255     */
256    public TextCellFactory font(String fontpath) {
257        if (assetManager == null) {
258            if (Gdx.files.internal(fontpath).exists())
259                bmpFont = new BitmapFont(Gdx.files.internal(fontpath));
260            else if (Gdx.files.classpath(fontpath).exists())
261                bmpFont = new BitmapFont(Gdx.files.classpath(fontpath));
262            else
263                bmpFont = DefaultResources.getDefaultFont();
264        }
265        else {
266            assetManager.load(new AssetDescriptor<>(fontpath, BitmapFont.class));
267                        /*
268                         * We're using the AssetManager not be asynchronous, but to avoid
269                         * loading a file twice (because that takes some time (tens of
270                         * milliseconds)). Hence this KISS code to avoid having to handle a
271                         * not-yet-loaded font:
272                         */
273            assetManager.finishLoading();
274            bmpFont = assetManager.get(fontpath, BitmapFont.class);
275        }
276        return this;
277    }
278    /**
279     * Sets this factory to use the provided BitmapFont as its font without re-constructing anything.
280     *
281     * This is a way to complete a needed step; the font must be set before initializing, which can be done by a few
282     * methods in this class.
283     *
284     * This should be called with an argument such as {@code DefaultResources.getDefaultFont()} or any other variable
285     * with BitmapFont as its type. The bitmap font will not be loaded from file with this method, which it would be if
286     * you called the overload of font() that takes a String more than once. These BitmapFont resources are already
287     * bundled with SquidLib:
288     * <ul>
289     *     <li>DefaultResources.getDefaultFont() = "Zodiac-Square-12x12.fnt"</li>
290     *     <li>DefaultResources.getDefaultNarrowFont() = "Rogue-Zodiac-6x12.fnt"</li>
291     *     <li>DefaultResources.getDefaultUnicodeFont() = "Mandrill-6x16.fnt"</li>
292     *     <li>DefaultResources.getSmoothFont() = "Inconsolata-LGC-8x18.fnt"</li>
293     *     <li>DefaultResources.getLargeFont() = "Zodiac-Square-24x24.fnt"</li>
294     *     <li>DefaultResources.getLargeNarrowFont() = "Rogue-Zodiac-12x24.fnt"</li>
295     *     <li>DefaultResources.getLargeUnicodeFont() = "Mandrill-12x32.fnt"</li>
296     *     <li>DefaultResources.getLargeSmoothFont() = "Inconsolata-LGC-12x24.fnt"</li>
297     *     <li>DefaultResources.getExtraLargeNarrowFont() = "Rogue-Zodiac-18x36.fnt"</li>
298     *     <li>There is also a sequence of resized versions of Inconsolata LGC, altered to fit in a square area. These
299     *     can be accessed with DefaultResources.getZoomedFont(), passing an int between 0 and 11 inclusive.</li>
300     * </ul>
301     * "Rogue-Zodiac-12x24.fnt", which is easily accessed by the method DefaultResources.getLargeNarrowFont() , can also
302     * be set using TextCellFactory.defaultNarrowFont() instead of font(). "Zodiac-Square-12x12.fnt", also accessible
303     * with DefaultResources.getDefaultFont() , can be set using TextCellFactory.defaultSquareFont() instead of font().
304     * "Inconsolata-LGC-12x24.fnt", also accessible with DefaultResources.getLargeSmoothFont() , can be set using
305     * TextCellFactory.defaultFont() instead of font(). All three of these alternatives will cache the BitmapFont if
306     * the same one is requested later, but this font() method will not.
307     * <br>
308     * See https://github.com/libgdx/libgdx/wiki/Hiero for some ways to create a bitmap font this can use. Several fonts
309     * in this list were created using Hiero (not Hiero4), and several were created with AngelCode's BMFont tool.
310     *
311     * @param bitmapFont the BitmapFont this should use
312     * @return this factory for method chaining
313     */
314    public TextCellFactory font(BitmapFont bitmapFont) {
315        if (bitmapFont == null) {
316            bmpFont = DefaultResources.getDefaultFont();
317        }
318        else {
319            bmpFont = bitmapFont;
320        }
321        return this;
322    }
323
324    /**
325     * Sets the font to a distance field font with the given String path to a .fnt file and String path to a texture.
326     * Distance field fonts should scale cleanly to multiple resolutions without artifacts. Does not use AssetManager
327     * since you shouldn't need to reload the font if it scales with one image. You need to configure the shader to use
328     * distance field fonts unless a class already does this for you (SquidLayers handles shader configuration
329     * internally, for example). TextCellFactory has a method, configureShader(Batch), that does this and should be
330     * called while that Batch has begun rendering, typically in an override of some containing Scene2D Group's
331     * draw(Batch, float) method.
332     * <br>
333     * At least two distance field fonts are included in SquidLib; one is square, one is narrow, and they can both be
334     * accessed using either the predefined TextCellFactory objects in DefaultResources, accessible with
335     * getStretchableFont() for narrow or getStretchableSquareFont() for square, or the setter methods in this class,
336     * defaultDistanceFieldFont() for square and defaultNarrowDistanceFieldFont() for narrow.
337     * <br>
338     * To create distance field fonts that work well with monospace layout is... time-consuming and error-prone, though
339     * not especially difficult for most fonts. The process is documented as well as we can, given how differently all
340     * fonts are made, in a file not included in the distribution JAR but present on GitHub:
341     * https://github.com/SquidPony/SquidLib/blob/master/squidlib/etc/making-distance-field-fonts.txt
342     * @param fontPath the path to a .fnt bitmap font file with distance field effects applied, which requires a complex
343     *                 process to create.
344     * @param texturePath the path to the texture used by the bitmap font
345     * @return this factory for method chaining
346     */
347    public TextCellFactory fontDistanceField(String fontPath, String texturePath) {
348        Texture tex;
349        if (Gdx.files.internal(texturePath).exists()) {
350            Gdx.app.debug("font", "Using internal font texture at " + texturePath);
351            tex = new Texture(Gdx.files.internal(texturePath), true);
352            tex.setFilter(Texture.TextureFilter.MipMapLinearNearest, Texture.TextureFilter.Linear);
353        } else if (Gdx.files.classpath(texturePath).exists()) {
354            Gdx.app.debug("font", "Using classpath font texture at " + texturePath);
355            tex = new Texture(Gdx.files.classpath(texturePath), true);
356            tex.setFilter(Texture.TextureFilter.MipMapLinearNearest, Texture.TextureFilter.Linear);
357        } else {
358                        bmpFont = DefaultResources.getDefaultFont();
359                        Gdx.app.error("TextCellFactory", "Could not find font file: " + texturePath + ", using defaults");
360                        return this;
361        }
362        if (Gdx.files.internal(fontPath).exists()) {
363            Gdx.app.debug("font", "Using internal font at " + fontPath);
364            bmpFont = new BitmapFont(Gdx.files.internal(fontPath), new TextureRegion(tex), false);
365            distanceField = true;
366        } else if (Gdx.files.classpath(fontPath).exists()) {
367            Gdx.app.debug("font", "Using classpath font at " + fontPath);
368            bmpFont = new BitmapFont(Gdx.files.classpath(fontPath), new TextureRegion(tex), false);
369            distanceField = true;
370        } else {
371            bmpFont = DefaultResources.getDefaultFont();
372                        Gdx.app.error("TextCellFactory", "Could not find font file: " + fontPath + ", using defaults");
373                }
374        //bmpFont.getData().padBottom = bmpFont.getDescent();
375        distanceFieldScaleX = bmpFont.getData().getGlyph(' ').xadvance - 1f;
376        distanceFieldScaleY = bmpFont.getLineHeight() - 1f;
377        return this;
378    }
379    /**
380     * Sets this factory to use a default 12x24 font that supports Latin, Greek, Cyrillic, and many more, including
381     * box-drawing characters, zodiac signs, playing-card suits, and chess piece symbols. This is enough to support the
382     * output of anything that DungeonUtility can make for a dungeon or FakeLanguageGen can make for text with its
383     * defaults, which is difficult for any font to do. The box-drawing characters in this don't quite line up, and in
384     * some colors there may appear to be gaps (white text on black backgrounds will show it, but not much else).
385     *
386     * This is a way to complete a needed step; the font must be set before initializing, which can be done by a few
387     * methods in this class.
388     *
389     * @return this factory for method chaining
390     */
391    public TextCellFactory defaultFont()
392    {
393        bmpFont = DefaultResources.getLargeSmoothFont();
394        return this;
395    }
396    /**
397     * Sets this factory to use a default 12x24 font that renders very accurately, with no gaps between box-drawing
398     * characters and very geometric lines.
399     *
400     * This is a way to complete a needed step; the font must be set before initializing, which can be done by a few
401     * methods in this class.
402     *
403     * @return this factory for method chaining
404     */
405    public TextCellFactory defaultNarrowFont()
406    {
407        bmpFont = DefaultResources.getLargeNarrowFont();
408        return this;
409    }
410
411    /**
412     * Sets this factory to use a default 12x12 font, which... is square, and doesn't look as bad as many square fonts
413     * do, plus it supports box-drawing characters with no gaps.
414     *
415     * This is a way to complete a needed step; the font must be set before initializing, which can be done by a few
416     * methods in this class.
417     *
418     * @return this factory for method chaining
419     */
420    public TextCellFactory defaultSquareFont()
421    {
422        bmpFont = DefaultResources.getDefaultFont();
423        return this;
424    }
425
426    /**
427     * Sets the TextCellFactory to use a square distance field font that will resize to whatever size you request.
428     * You must configure the shader if you use a distance field font, unless a class does it for you, like SquidLayers.
429     * The configureShader(Batch) method of this class can be used to set up the shader if you don't use SquidLayers;
430     * see its docs for more information.
431     * @return this TextCellFactory set to use a square distance field font
432     */
433    public TextCellFactory defaultDistanceFieldFont()
434    {
435        fontDistanceField(DefaultResources.distanceFieldSquare, DefaultResources.distanceFieldSquareTexture);
436        return this;
437    }
438    /**
439     * Sets the TextCellFactory to use a half-square distance field font that will resize to whatever size you request.
440     * You must configure the shader if you use a distance field font, unless a class does it for you, like SquidLayers.
441     * The configureShader(Batch) method of this class can be used to set up the shader if you don't use SquidLayers;
442     * see its docs for more information.
443     * @return this TextCellFactory set to use a half-square distance field font
444     */
445    public TextCellFactory defaultNarrowDistanceFieldFont()
446    {
447        fontDistanceField(DefaultResources.distanceFieldNarrow, DefaultResources.distanceFieldNarrowTexture);
448        return this;
449    }
450
451    /**
452     * Returns the width of a single cell.
453     *
454     * @return the width
455     */
456    public int width() {
457        return width;
458    }
459
460    /**
461     * Sets the factory's cell width to the provided value. Clamps at 1 on the
462     * lower bound to ensure valid calculations.
463     *
464     * @param width the desired width
465     * @return this factory for method chaining
466     */
467    public TextCellFactory width(int width) {
468        this.width = Math.max(1, width);
469        actualCellWidth = this.width;
470        return this;
471    }
472
473    /**
474     * Returns the height of a single cell.
475     *
476     * @return
477     */
478    public int height() {
479        return height;
480    }
481
482    /**
483     * Sets the factory's cell height to the provided value. Clamps at 1 on the
484     * lower bound to ensure valid calculations.
485     *
486     * @param height the desired width
487     * @return this factory for method chaining
488     */
489    public TextCellFactory height(int height) {
490        this.height = Math.max(1, height);
491        //modifiedHeight = this.height;
492        actualCellHeight = this.height;
493        return this;
494    }
495    /**
496     * Sets the factory's height used for text to the provided value, but does not change the size of a cell. Clamps at
497     * 1 on the lower bound to ensure valid calculations.
498     *
499     * @param width the desired width
500     * @return this factory for method chaining
501     */
502    public TextCellFactory tweakWidth(int width) {
503        this.width = Math.max(1, width);
504        return this;
505    }
506
507    /**
508     * Sets the factory's height used for text to the provided value, but does not change the size of a cell. Clamps at
509     * 1 on the lower bound to ensure valid calculations.
510     *
511     * @param height the desired height
512     * @return this factory for method chaining
513     */
514    public TextCellFactory tweakHeight(int height) {
515        this.height = Math.max(1, height);
516        //modifiedHeight = this.height;
517        return this;
518    }
519
520    /**
521     * Returns the current String of code points that are used for sizing the
522     * cells.
523     *
524     * Note that this is actually a set of codepoints and treating them as an
525     * array of chars might give undesired results.
526     *
527     * @return the String used for sizing calculations
528     */
529    public String fit() {
530        return fitting;
531    }
532
533    /**
534     * Sets the characters that will be guaranteed to fit to the provided ones.
535     * This will override any previously set string.
536     *
537     * @param fit the String of code points to size to
538     * @return this factory for method chaining
539     */
540    public TextCellFactory fit(String fit) {
541        fitting = fit;
542        bmpFont.setFixedWidthGlyphs(fitting);
543        width = (int)bmpFont.getSpaceWidth();
544        return this;
545    }
546
547    /**
548     * Adds the code points in the string to the list of characters that will be
549     * guaranteed to fit.
550     *
551     * @param fit the String of code points to size to
552     * @return this factory for method chaining
553     */
554    public TextCellFactory addFit(String fit) {
555        fitting += fit;
556        bmpFont.setFixedWidthGlyphs(fitting);
557        width = (int)bmpFont.getSpaceWidth();
558        return this;
559    }
560
561    /**
562     * Returns whether this factory is currently set to do antialiasing on the
563     * characters rendered, which is always true.
564     *
565     * @return true if antialiasing is set
566     */
567    public boolean antialias() {
568        return true;
569    }
570
571    /**
572     * All fonts will be rendered with antialiasing, this doesn't do anything.
573     *
574     * @param antialias ignored, will always use antialiasing
575     * @return this factory for method chaining
576     * @deprecated AA is the wave of the future!
577     */
578    public TextCellFactory antialias(boolean antialias) {
579        return this;
580    }
581
582    /**
583     * Sets the amount of padding on all sides to the provided value.
584     *
585     * @param padding how much padding in pixels
586     * @return this for method chaining
587     */
588    public TextCellFactory padding(int padding) {
589        leftPadding = padding;
590        rightPadding = padding;
591        topPadding = padding;
592        bottomPadding = padding;
593        return this;
594    }
595
596    /**
597     * Returns the padding on the left side.
598     *
599     * @return amount of padding in pixels
600     */
601    public int leftPadding() {
602        return leftPadding;
603    }
604
605    /**
606     * Sets the amount of padding on the left side to the provided value.
607     *
608     * @param padding how much padding in pixels
609     * @return this for method chaining
610     */
611    public TextCellFactory leftPadding(int padding) {
612        leftPadding = padding;
613        return this;
614    }
615
616    /**
617     * Returns the padding on the right side.
618     *
619     * @return amount of padding in pixels
620     */
621    public int rightPadding() {
622        return rightPadding;
623    }
624
625    /**
626     * Sets the amount of padding on the right side to the provided value.
627     *
628     * @param padding how much padding in pixels
629     * @return this for method chaining
630     */
631    public TextCellFactory rightPadding(int padding) {
632        rightPadding = padding;
633        return this;
634    }
635
636    /**
637     * Returns the padding on the top side.
638     *
639     * @return amount of padding in pixels
640     */
641    public int topPadding() {
642        return topPadding;
643    }
644
645    /**
646     * Sets the amount of padding on the top side to the provided value.
647     *
648     * @param padding how much padding in pixels
649     * @return this for method chaining
650     */
651    public TextCellFactory topPadding(int padding) {
652        topPadding = padding;
653        return this;
654    }
655
656    /**
657     * Returns the padding on the bottom side.
658     *
659     * @return amount of padding in pixels
660     */
661    public int bottomPadding() {
662        return bottomPadding;
663    }
664
665    /**
666     * Sets the amount of padding on the bottom side to the provided value.
667     *
668     * @param padding how much padding in pixels
669     * @return this for method chaining
670     */
671    public TextCellFactory bottomPadding(int padding) {
672        bottomPadding = padding;
673        return this;
674    }
675
676        /**
677         * @param icc
678         *            The color center to use. Should not be {@code null}.
679         * @return {@code this}
680         * @throws NullPointerException
681         *             If {@code icc} is {@code null}.
682         */
683        public TextCellFactory setColorCenter(IColorCenter<Color> icc) {
684                if (icc == null)
685                        /* Better fail now than later */
686                        throw new NullPointerException(
687                                        "The color center should not be null in " + getClass().getSimpleName());
688        scc = icc;
689                return this;
690        }
691
692    /**
693     * Returns true if this factory is fully initialized and ready to build text cells.
694     *
695     * @return true if initialized
696     */
697    public boolean initialized() {
698        return initialized;
699    }
700
701    /**
702     * Returns true if the given character will fit inside the current cell
703     * dimensions with the current font.
704     *
705     * ISO Control characters, non-printing characters and invalid unicode
706     * characters are all considered by definition to fit.
707     *
708     * @param codepoint
709     * @return
710     */
711    public boolean willFit(int codepoint) {
712        if (!initialized) {
713            throw new IllegalStateException("This factory has not yet been initialized!");
714        }
715
716        if (!Character.isValidCodePoint(codepoint) ||
717                (codepoint <= 0x001F) || (codepoint >= 0x007F && codepoint <= 0x009F)) // same as isIsoControl
718        {
719            return true;
720        }
721
722        return fitting.contains(String.valueOf(Character.toChars(codepoint)));
723    }
724
725    /**
726     * Use the specified Batch to draw a String (often just one char long) with the default color (white), with x and y
727     * determining the world-space coordinates for the upper-left corner.
728     *
729     * @param batch the LibGDX Batch to do the drawing
730     * @param s the string to draw, often but not necessarily one char. Can be null to draw a solid block instead.
731     * @param x x of the upper-left corner of the region of text in world coordinates.
732     * @param y y of the upper-left corner of the region of text in world coordinates.
733     */
734    public void draw(Batch batch, String s, float x, float y) {
735        if (!initialized) {
736            throw new IllegalStateException("This factory has not yet been initialized!");
737        }
738
739        // + descent * 3 / 2f
740        // - distanceFieldScaleY / 12f
741
742        //height - lineTweak * 2f
743        if (s == null) {
744            batch.setColor(1f,1f,1f,1f);
745            batch.draw(block, x, y - actualCellHeight, actualCellWidth, actualCellHeight); // + descent * 1 / 3f
746        } else if(s.length() > 0 && s.charAt(0) == '\0') {
747            batch.setColor(1f,1f,1f,1f);
748            batch.draw(block, x, y - actualCellHeight, actualCellWidth * s.length(), actualCellHeight); // descent * 1 / 3f
749        } else {
750            bmpFont.setColor(1f,1f,1f,1f);
751            if(swap.containsKey(s))
752                bmpFont.draw(batch, swap.get(s), x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
753            else
754                bmpFont.draw(batch, s, x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
755        }
756    }
757    /**
758     * Use the specified Batch to draw a String (often just one char long) in the specified rgba color, with x and y
759     * determining the world-space coordinates for the upper-left corner.
760     *
761     * @param batch the LibGDX Batch to do the drawing
762     * @param s the string to draw, often but not necessarily one char. Can be null to draw a solid block instead.
763     * @param r 0.0 to 0.1 red value
764     * @param g 0.0 to 0.1 green value
765     * @param b 0.0 to 0.1 blue value
766     * @param a 0.0 to 0.1 alpha value
767     * @param x x of the upper-left corner of the region of text in world coordinates.
768     * @param y y of the upper-left corner of the region of text in world coordinates.
769     */
770    public void draw(Batch batch, String s, float r, float g, float b, float a, float x, float y) {
771        if (!initialized) {
772            throw new IllegalStateException("This factory has not yet been initialized!");
773        }
774
775        if (s == null) {
776            Color orig = scc.filter(batch.getColor());
777            batch.setColor(r, g, b, a);
778            batch.draw(block, x, y - actualCellHeight, actualCellWidth, actualCellHeight); // descent * 1 / 3f
779            batch.setColor(orig);
780        } else if(s.length() > 0 && s.charAt(0) == '\0') {
781            Color orig = scc.filter(batch.getColor());
782            batch.setColor(r, g, b, a);
783            batch.draw(block, x, y - actualCellHeight, actualCellWidth * s.length(), actualCellHeight); // descent * 1 / 3f
784            batch.setColor(orig);
785        } else {
786            bmpFont.setColor(r, g, b, a);
787            if(swap.containsKey(s))
788                bmpFont.draw(batch, swap.get(s), x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
789            else
790                bmpFont.draw(batch, s, x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
791        }
792    }
793
794    /**
795     * Use the specified Batch to draw a String (often just one char long) in the specified LibGDX Color, with x and y
796     * determining the world-space coordinates for the upper-left corner.
797     *
798     * @param batch the LibGDX Batch to do the drawing
799     * @param s the string to draw, often but not necessarily one char. Can be null to draw a solid block instead.
800     * @param color the LibGDX Color to draw the char(s) with, all the same color
801     * @param x x of the upper-left corner of the region of text in world coordinates.
802     * @param y y of the upper-left corner of the region of text in world coordinates.
803     */
804    public void draw(Batch batch, String s, Color color, float x, float y) {
805        if (!initialized) {
806            throw new IllegalStateException("This factory has not yet been initialized!");
807        }
808
809        if (s == null) {
810            Color orig = batch.getColor();
811            batch.setColor(scc.filter(color));
812            batch.draw(block, x, y - actualCellHeight, actualCellWidth, actualCellHeight); // descent * 1 / 3f
813            batch.setColor(orig);
814        } else if(s.length() > 0 && s.charAt(0) == '\0') {
815            Color orig = batch.getColor();
816            batch.setColor(scc.filter(color));
817            batch.draw(block, x, y - actualCellHeight, actualCellWidth * s.length(), actualCellHeight); // descent * 1 / 3f
818            batch.setColor(orig);
819        } else {
820            bmpFont.setColor(scc.filter(color));
821            if(swap.containsKey(s))
822                bmpFont.draw(batch, swap.get(s), x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
823            else
824                bmpFont.draw(batch, s, x, y - descent + 1/* * 1.5f*//* - lineHeight * 0.2f */ /* + descent*/, width * s.length(), Align.center, false);
825        }
826    }
827
828    /**
829     * Use the specified Batch to draw a TextureRegion with the default tint color (white, so un-tinted), with x and y
830     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
831     * if its size does not match what this TextCellFactory uses for width and height.
832     *
833     * @param batch the LibGDX Batch to do the drawing
834     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
835     * @param x x of the upper-left corner of the region of text in world coordinates.
836     * @param y y of the upper-left corner of the region of text in world coordinates.
837     */
838    public void draw(Batch batch, TextureRegion tr, float x, float y) {
839        if (!initialized) {
840            throw new IllegalStateException("This factory has not yet been initialized!");
841        }
842
843        if (tr == null) {
844            batch.draw(block, x, y - height, actualCellWidth, actualCellHeight);
845        } else {
846            batch.draw(tr, x, y - height, width, height);
847        }
848    }
849    /**
850     * Use the specified Batch to draw a TextureRegion tinted with the specified rgba color, with x and y
851     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
852     * if its size does not match what this TextCellFactory uses for width and height.
853     *
854     * @param batch the LibGDX Batch to do the drawing
855     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
856     * @param r 0.0 to 0.1 red value
857     * @param g 0.0 to 0.1 green value
858     * @param b 0.0 to 0.1 blue value
859     * @param a 0.0 to 0.1 alpha value
860     * @param x x of the upper-left corner of the region of text in world coordinates.
861     * @param y y of the upper-left corner of the region of text in world coordinates.
862     */
863    public void draw(Batch batch, TextureRegion tr, float r, float g, float b, float a, float x, float y) {
864        if (!initialized) {
865            throw new IllegalStateException("This factory has not yet been initialized!");
866        }
867
868        if (tr == null) {
869            Color orig = batch.getColor();
870            batch.setColor(r, g, b, a);
871            batch.draw(block, x, y - height, actualCellWidth, actualCellHeight);
872            batch.setColor(orig);
873        } else {
874            Color orig = batch.getColor();
875            batch.setColor(r, g, b, a);
876            batch.draw(tr, x, y - height, width, height);
877            batch.setColor(orig);
878        }
879    }
880
881    /**
882     * Use the specified Batch to draw a TextureRegion tinted with the specified LibGDX Color, with x and y
883     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
884     * if its size does not match what this TextCellFactory uses for width and height.
885     *
886     * @param batch the LibGDX Batch to do the drawing
887     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
888     * @param color the LibGDX Color to draw the char(s) with, all the same color
889     * @param x x of the upper-left corner of the region of text in world coordinates.
890     * @param y y of the upper-left corner of the region of text in world coordinates.
891     */
892    public void draw(Batch batch, TextureRegion tr, Color color, float x, float y) {
893        if (!initialized) {
894            throw new IllegalStateException("This factory has not yet been initialized!");
895        }
896
897        if (tr == null) {
898            Color orig = batch.getColor();
899            batch.setColor(scc.filter(color));
900            batch.draw(block, x, y - height, actualCellWidth, actualCellHeight);
901            batch.setColor(orig);
902        } else {
903            Color orig = batch.getColor();
904            batch.setColor(scc.filter(color));
905            batch.draw(tr, x, y - height, width, height);
906            batch.setColor(orig);
907        }
908    }
909
910    /**
911     * Use the specified Batch to draw a TextureRegion with the default tint color (white, so un-tinted), with x and y
912     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
913     * only if the supplied width and height do not match what its own dimensions are.
914     *
915     * @param batch the LibGDX Batch to do the drawing
916     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
917     * @param x x of the upper-left corner of the region of text in world coordinates.
918     * @param y y of the upper-left corner of the region of text in world coordinates.
919     * @param width the width of the TextureRegion or solid block in pixels.
920     * @param height the height of the TextureRegion or solid block in pixels.
921     */
922    public void draw(Batch batch, TextureRegion tr, float x, float y, float width, float height) {
923        if (!initialized) {
924            throw new IllegalStateException("This factory has not yet been initialized!");
925        }
926
927        if (tr == null) {
928            batch.draw(block, x, y - height, width, height);
929        } else {
930            batch.draw(tr, x, y - height, width, height);
931        }
932    }
933    /**
934     * Use the specified Batch to draw a TextureRegion tinted with the specified rgba color, with x and y
935     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
936     * only if the supplied width and height do not match what its own dimensions are.
937     *
938     * @param batch the LibGDX Batch to do the drawing
939     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
940     * @param r 0.0 to 0.1 red value
941     * @param g 0.0 to 0.1 green value
942     * @param b 0.0 to 0.1 blue value
943     * @param a 0.0 to 0.1 alpha value
944     * @param x x of the upper-left corner of the region of text in world coordinates.
945     * @param y y of the upper-left corner of the region of text in world coordinates.
946     * @param width the width of the TextureRegion or solid block in pixels.
947     * @param height the height of the TextureRegion or solid block in pixels.
948     */
949    public void draw(Batch batch, TextureRegion tr, float r, float g, float b, float a, float x, float y, float width, float height) {
950        if (!initialized) {
951            throw new IllegalStateException("This factory has not yet been initialized!");
952        }
953
954        if (tr == null) {
955            Color orig = batch.getColor();
956            batch.setColor(r, g, b, a);
957            batch.draw(block, x, y - height, width, height);
958            batch.setColor(orig);
959        } else {
960            Color orig = batch.getColor();
961            batch.setColor(r, g, b, a);
962            batch.draw(tr, x, y - height, width, height);
963            batch.setColor(orig);
964        }
965    }
966
967    /**
968     * Use the specified Batch to draw a TextureRegion tinted with the specified LibGDX Color, with x and y
969     * determining the world-space coordinates for the upper-left corner. The TextureRegion will be stretched
970     * only if the supplied width and height do not match what its own dimensions are.
971     *
972     * @param batch the LibGDX Batch to do the drawing
973     * @param tr the TextureRegion to draw. Can be null to draw a solid block instead.
974     * @param color the LibGDX Color to draw the char(s) with, all the same color
975     * @param x x of the upper-left corner of the region of text in world coordinates.
976     * @param y y of the upper-left corner of the region of text in world coordinates.
977     * @param width the width of the TextureRegion or solid block in pixels.
978     * @param height the height of the TextureRegion or solid block in pixels.
979     */
980    public void draw(Batch batch, TextureRegion tr, Color color, float x, float y, float width, float height) {
981        if (!initialized) {
982            throw new IllegalStateException("This factory has not yet been initialized!");
983        }
984
985        if (tr == null) {
986            Color orig = batch.getColor();
987            batch.setColor(scc.filter(color));
988            batch.draw(block, x, y - height, width, height);
989            batch.setColor(orig);
990        } else {
991            Color orig = batch.getColor();
992            batch.setColor(scc.filter(color));
993            batch.draw(tr, x, y - height, width, height);
994            batch.setColor(orig);
995        }
996    }
997
998    /**
999     * Converts a String into a Label, or if the argument s is null, creates an Image of a solid block. Can be used
1000     * for preparing glyphs for animation effects.
1001     * @param s a String to make into an Actor, which can be null for a solid block.
1002     * @return the Actor, with no position set.
1003     */
1004    public Label makeWrappingString(String s) {
1005        if (!initialized) {
1006            throw new IllegalStateException("This factory has not yet been initialized!");
1007        }
1008        if (s == null) {
1009            s = "";
1010        }
1011        Label lb = new Label(s, style);
1012        lb.setWrap(true);
1013        // lb.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1014        return lb;
1015
1016    }
1017
1018    /**
1019     * Converts a String into a Label, or if the argument s is null, creates an Image of a solid block. Can be used
1020     * for preparing glyphs for animation effects, and is used internally for this purpose.
1021     * @param s a String to make into an Actor, which can be null for a solid block.
1022     * @param color a Color to tint s with.
1023     * @return the Actor, with no position set.
1024     */
1025    public Actor makeActor(String s, Color color) {
1026        if (!initialized) {
1027            throw new IllegalStateException("This factory has not yet been initialized!");
1028        }
1029        if (s == null) {
1030            Image im = new Image(block);
1031            im.setColor(scc.filter(color));
1032            //im.setSize(width, height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1033            im.setSize(actualCellWidth, actualCellHeight + (distanceField ? 1 : 0)); //  - lineHeight / actualCellHeight //+ lineTweak * 1f
1034            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1035            return im;
1036        } else if(s.length() > 0 && s.charAt(0) == '\0') {
1037            Image im = new Image(block);
1038            im.setColor(scc.filter(color));
1039            //im.setSize(width * s.length(), height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1040            im.setSize(actualCellWidth * s.length(), actualCellHeight + (distanceField ? 1 : 0)); //   - lineHeight / actualCellHeight //+ lineTweak * 1f
1041            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1042            return im;
1043        } else {
1044            Label lb;
1045            if(swap.containsKey(s))
1046                lb = new Label(swap.get(s), style);
1047            else
1048                lb = new Label(s, style);
1049            lb.setSize(width * s.length(), height - descent); //+ lineTweak * 1f
1050            lb.setColor(scc.filter(color));
1051            // lb.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1052            return lb;
1053        }
1054    }
1055
1056    /**
1057     * Converts a String into a ColorChangeLabel, or if the argument s is null, creates a ColorChangeImage of a solid
1058     * block. Can be used for preparing glyphs for animation effects, and is used internally for this purpose. The
1059     * ColorChange classes will rotate between all colors given in the List each second, and are not affected by setColor,
1060     * though they are affected by their setColors methods. Their color change is not considered an animation for the
1061     * purposes of things like SquidPanel.hasActiveAnimations() .
1062     * @param s a String to make into an Actor, which can be null for a solid block.
1063     * @param colors a List of Color to tint s with, looping through all elements in the list each second
1064     * @return the Actor, with no position set.
1065     */
1066    public Actor makeActor(String s, Collection<Color> colors) {
1067        if (!initialized) {
1068            throw new IllegalStateException("This factory has not yet been initialized!");
1069        }
1070        ArrayList<Color> colors2;
1071        if(colors == null || colors.isEmpty())
1072            colors2 = null;
1073        else {
1074            colors2 = new ArrayList<>(colors.size());
1075            for (Color c : colors) {
1076                colors2.add(scc.filter(c));
1077            }
1078        }
1079        if (s == null) {
1080            ColorChangeImage im = new ColorChangeImage(block, colors2);
1081            //im.setSize(width, height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1082            im.setSize(actualCellWidth, actualCellHeight + (distanceField ? 1 : 0)); //  - lineHeight / actualCellHeight //+ lineTweak * 1f
1083            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1084            return im;
1085        } else if(s.length() > 0 && s.charAt(0) == '\0') {
1086            ColorChangeImage im = new ColorChangeImage(block, colors2);
1087            //im.setSize(width * s.length(), height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1088            im.setSize(actualCellWidth * s.length(), actualCellHeight + (distanceField ? 1 : 0)); //   - lineHeight / actualCellHeight //+ lineTweak * 1f
1089            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1090            return im;
1091        } else {
1092            ColorChangeLabel lb;
1093            if(swap.containsKey(s))
1094                lb = new ColorChangeLabel(swap.get(s), style, colors2);
1095            else
1096                lb = new ColorChangeLabel(s, style, colors2);
1097            lb.setSize(width * s.length(), height - descent); //+ lineTweak * 1f
1098            // lb.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1099            return lb;
1100        }
1101    }
1102    /**
1103     * Converts a String into a ColorChangeLabel, or if the argument s is null, creates a ColorChangeImage of a solid
1104     * block. Can be used for preparing glyphs for animation effects, and is used internally for this purpose. The
1105     * ColorChange classes will rotate between all colors given in the List each second, and are not affected by setColor,
1106     * though they are affected by their setColors methods. Their color change is not considered an animation for the
1107     * purposes of things like SquidPanel.hasActiveAnimations() .
1108     * @param s a String to make into an Actor, which can be null for a solid block.
1109     * @param colors a List of Color to tint s with, looping through all elements in the list each second
1110     * @param loopTime the amount of time, in seconds, to spend looping through all colors in the list
1111     * @return the Actor, with no position set.
1112     */
1113    public Actor makeActor(String s, Collection<Color> colors, float loopTime) {
1114        if (!initialized) {
1115            throw new IllegalStateException("This factory has not yet been initialized!");
1116        }
1117        ArrayList<Color> colors2;
1118        if(colors == null || colors.isEmpty())
1119            colors2 = null;
1120        else {
1121            colors2 = new ArrayList<>(colors.size());
1122            for (Color c : colors) {
1123                colors2.add(scc.filter(c));
1124            }
1125        }
1126        if (s == null) {
1127            ColorChangeImage im = new ColorChangeImage(block, loopTime, colors2);
1128            //im.setSize(width, height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1129            im.setSize(actualCellWidth, actualCellHeight + (distanceField ? 1 : 0)); //  - lineHeight / actualCellHeight //+ lineTweak * 1f
1130            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1131            return im;
1132        } else if(s.length() > 0 && s.charAt(0) == '\0') {
1133            ColorChangeImage im = new ColorChangeImage(block, loopTime, colors2);
1134            //im.setSize(width * s.length(), height - MathUtils.ceil(bmpFont.getDescent() / 2f));
1135            im.setSize(actualCellWidth * s.length(), actualCellHeight + (distanceField ? 1 : 0)); //   - lineHeight / actualCellHeight //+ lineTweak * 1f
1136            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1137            return im;
1138        } else {
1139            ColorChangeLabel lb;
1140            if(swap.containsKey(s))
1141                lb = new ColorChangeLabel(swap.get(s), style, loopTime, colors2);
1142            else
1143                lb = new ColorChangeLabel(s, style, loopTime, colors2);
1144            lb.setSize(width * s.length(), height - descent); //+ lineTweak * 1f
1145            // lb.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1146            return lb;
1147        }
1148    }
1149
1150    /**
1151     * Converts a TextureRegion into an Image, or if the argument s is null, creates an Image of a solid block. Can be
1152     * used for preparing images for animation effects. Stretches the TextureRegion to match a single cell's dimensions.
1153     * @param tr a TextureRegion to make into an Actor, which can be null for a solid block.
1154     * @param color a Color to tint tr with.
1155     * @return the Actor, with no position set.
1156     */
1157    public Actor makeActor(TextureRegion tr, Color color) {
1158        if (!initialized) {
1159            throw new IllegalStateException("This factory has not yet been initialized!");
1160        }
1161        if (tr == null) {
1162            Image im = new Image(block);
1163            im.setColor(scc.filter(color));
1164            im.setSize(width, height);
1165            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1166            return im;
1167        } else {
1168            Image im = new Image(tr);
1169            im.setColor(scc.filter(color));
1170            im.setSize(width, height);
1171            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1172            return im;
1173        }
1174    }
1175
1176    /**
1177     * Converts a TextureRegion into an Image, or if the argument s is null, creates an Image of a solid block. Can be
1178     * used for preparing images for animation effects. Ensures the returned Image has the given width and height.
1179     * @param tr a TextureRegion to make into an Actor, which can be null for a solid block.
1180     * @param color a Color to tint tr with.
1181     * @return the Actor, with no position set.
1182     */
1183    public Actor makeActor(TextureRegion tr, Color color, float width, float height) {
1184        if (!initialized) {
1185            throw new IllegalStateException("This factory has not yet been initialized!");
1186        }
1187        if (tr == null) {
1188            Image im = new Image(block);
1189            im.setColor(scc.filter(color));
1190            im.setSize(width, height);
1191            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1192            return im;
1193        } else {
1194            Image im = new Image(tr);
1195            im.setColor(scc.filter(color));
1196            im.setSize(width, height);
1197            // im.setPosition(x - width * 0.5f, y - height * 0.5f, Align.center);
1198            return im;
1199        }
1200    }
1201
1202    /**
1203     * Returns a solid block of white, 1x1 pixel in size; can be drawn at other sizes by Batch.
1204     *
1205     * @return a white 1x1 pixel Texture.
1206     */
1207    public Texture getSolid() {
1208        if (!initialized) {
1209            throw new IllegalStateException("This factory has not yet been initialized!");
1210        }
1211        return block;
1212    }
1213
1214    public boolean isDistanceField() {
1215        return distanceField;
1216    }
1217
1218    public float getDistanceFieldScaleX() {
1219        return distanceFieldScaleX;
1220    }
1221    public float getDistanceFieldScaleY() {
1222        return distanceFieldScaleY;
1223    }
1224
1225    /**
1226     * If this uses a distance field font, the smoothing multiplier affects how crisp or blurry lines are, with higher
1227     * numbers generally resulting in more crisp fonts, but numbers that are too high cause jagged aliasing.
1228     * @return the current smoothing multiplier as a float, which starts at 1f.
1229     */
1230    public float getSmoothingMultiplier() {
1231        return smoothingMultiplier;
1232    }
1233
1234    /**
1235     * If this uses a distance field font, the smoothing multiplier affects how crisp or blurry lines are, with higher
1236     * numbers generally resulting in more crisp fonts, but numbers that are too high cause jagged aliasing.
1237     * @param smoothingMultiplier the new value for the smoothing multiplier as a float; should be fairly close to 1f.
1238     * @return this for chaining
1239     */
1240    public TextCellFactory setSmoothingMultiplier(float smoothingMultiplier) {
1241        this.smoothingMultiplier = smoothingMultiplier;
1242        return this;
1243    }
1244
1245    /**
1246     * If using a distance field font, you MUST call this at some point while the batch has begun, or use code that
1247     * calls it for you (which is now much of SquidLib). A typical point to call it is in the
1248     * "void draw(Batch batch, float parentAlpha)" method or an overriding method for a Scene2D class. You should call
1249     * configureShader rarely, typically only a few times per frame if there are no images to render, and this means the
1250     * logical place to call it is in the outermost Group that contains any SquidPanel objects or other widgets. If you
1251     * have multipleTextCellFactory objects, each one needs to have configureShader called before it is used to draw.
1252     * <br>
1253     * SquidLayers and SquidPanel already call this method in their draw overrides, so you don't need to call this
1254     * manually if you use SquidLayers or SquidPanel.
1255     * <br>
1256     * If you don't use a distance field font, you don't need to call this, but calling it won't cause problems.
1257     *
1258     * @param batch the Batch, such as a SpriteBatch, to configure to render distance field fonts if necessary.
1259     */
1260    public void configureShader(Batch batch) {
1261        if (initialized && distanceField) {
1262            batch.setShader(shader);
1263            shader.setUniformf("u_smoothing", 3.5f * smoothingMultiplier * bmpFont.getData().scaleX);
1264
1265        }
1266    }
1267    /**
1268     * Releases all resources of this object.
1269     */
1270    @Override
1271    public void dispose() {
1272        if(bmpFont != null) bmpFont.dispose();
1273        if(block != null) block.dispose();
1274    }
1275
1276    /**
1277     * Gets the descent of this TextCellFactory's BitmapFont, which may be useful for layout outside this class.
1278     * @return the descent of the BitmapFont this object uses
1279     */
1280    public float getDescent() {
1281        return descent;
1282    }
1283
1284    /**
1285     * Adds a pair of Strings (typically both with length 1) as a replacement pair, so when the find String is requested
1286     * to be drawn, the replace String is used instead. These are used when drawing text in each cell in SquidPanel and
1287     * related classes, so Strings longer than 1 char are rare, if they occur at all.
1288     * <br>
1289     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1290     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1291     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1292     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1293     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1294     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1295     * @param find the requested String that will be changed
1296     * @param replace the replacement String that will be used in place of find
1297     * @return this for chaining
1298     */
1299    public TextCellFactory addSwap(String find, String replace)
1300    {
1301        if(find.startsWith("\0"))
1302            return this;
1303        swap.put(find, replace);
1304        return this;
1305    }
1306
1307    /**
1308     * Adds a pair of chars as a replacement pair, so when the find char is requested to be drawn, the replace char is
1309     * used instead.
1310     * <br>
1311     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1312     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1313     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap('^', ',')} and also
1314     * {@code addSwap(',', ':')}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1315     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1316     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1317     * @param find the requested char that will be changed (converted to a length-1 String)
1318     * @param replace the replacement char that will be used in place of find (converted to a length-1 String)
1319     * @return this for chaining
1320     */
1321    public TextCellFactory addSwap(char find, char replace)
1322    {
1323        if(find == '\0')
1324            return this;
1325        swap.put(String.valueOf(find), String.valueOf(replace));
1326        return this;
1327    }
1328
1329    /**
1330     * Removes the replacement pair, if present, that searches for the given key, find.
1331     * <br>
1332     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1333     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1334     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1335     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1336     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1337     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1338     * @param find the String that would be changed in the replacement pair
1339     * @return this for chaining
1340     */
1341    public TextCellFactory removeSwap(String find)
1342    {
1343        swap.remove(find);
1344        return this;
1345    }
1346
1347    /**
1348     * Removes the replacement pair, if present, that searches for the given key, find.
1349     * <br>
1350     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1351     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1352     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap('^', ',')} and also
1353     * {@code addSwap(',', ':')}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1354     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1355     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1356     * @return this for chaining
1357     */
1358    public TextCellFactory removeSwap(char find)
1359    {
1360        swap.remove(String.valueOf(find));
1361        return this;
1362    }
1363
1364    /**
1365     * Gets the current mapping of "swaps", or replacement pairs, to replace keys requested for drawing with their
1366     * values in the LinkedHashMap.
1367     * <br>
1368     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1369     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1370     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1371     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1372     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1373     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1374     * @return the mapping of replacement pairs
1375     */
1376    public LinkedHashMap<String, String> getAllSwaps() {
1377        return swap;
1378    }
1379
1380    /**
1381     * Sets the mapping of replacement pairs to a different one as a Map of String keys to String values.
1382     * <br>
1383     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1384     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1385     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1386     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1387     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1388     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1389     * @param swaps the Map of replacement pairs; keys requested for drawing will be replaced with their values
1390     * @return this for chaining
1391     */
1392    public TextCellFactory setAllSwaps(Map<String, String> swaps) {
1393        this.swap = new LinkedHashMap<>(swaps.size());
1394        for(Map.Entry<String, String> kv : swaps.entrySet())
1395        {
1396            if(!kv.getKey().startsWith("\0"))
1397                this.swap.put(kv.getKey(), kv.getValue());
1398        }
1399        return this;
1400    }
1401
1402    /**
1403     * Appends to the mapping of replacement pairs, adding or replacing any entries in the current mapping with the
1404     * entries in a Map of String keys to String values.
1405     * <br>
1406     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1407     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1408     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1409     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1410     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1411     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1412     * @param swaps the Map of replacement pairs to add; keys requested for drawing will be replaced with their values
1413     * @return this for chaining
1414     */
1415    public TextCellFactory addSwaps(Map<String, String> swaps) {
1416
1417        for(Map.Entry<String, String> kv : swaps.entrySet())
1418        {
1419            if(!kv.getKey().startsWith("\0"))
1420                swap.put(kv.getKey(), kv.getValue());
1421        }
1422        return this;
1423    }
1424
1425    /**
1426     * Clears all replacement pairs this has been told to swap.
1427     * <br>
1428     * This can be useful when you want to use certain defaults in squidlib-util's dungeon generation, like '~' for deep
1429     * water, but not others, like ',' for shallow water, and would rather have a glyph of your choice replace something
1430     * that would be drawn. Replacements will not be chained; that is, if you {@code addSwap("^", ",")} and also
1431     * {@code addSwap(",", ":")}, then a requested '^' will be drawn as ',', not ':', but a requested ',' will be drawn
1432     * as ':' (only one swap will be performed). Typically you want a different TextCellFactory for UI elements that use
1433     * swapping, like a top-down char-based map, and elements that should not, like those that display normal text.
1434     * @return this for chaining
1435     */
1436    public TextCellFactory clearSwaps()
1437    {
1438        swap.clear();
1439        return this;
1440    }
1441}