/* * Copyright (c) 2009-2010 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.font; import com.jme3.font.BitmapFont.Align; import com.jme3.font.BitmapFont.VAlign; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.renderer.RenderManager; import com.jme3.scene.Node; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author YongHoon */ public class BitmapText extends Node { private BitmapFont font; private StringBlock block; private boolean needRefresh = true; private final BitmapTextPage[] textPages; private Letters letters; public BitmapText(BitmapFont font) { this(font, false, false); } public BitmapText(BitmapFont font, boolean rightToLeft) { this(font, rightToLeft, false); } public BitmapText(BitmapFont font, boolean rightToLeft, boolean arrayBased) { textPages = new BitmapTextPage[font.getPageSize()]; for (int page = 0; page < textPages.length; page++) { textPages[page] = new BitmapTextPage(font, arrayBased, page); attachChild(textPages[page]); } this.font = font; this.block = new StringBlock(); block.setSize(font.getPreferredSize()); letters = new Letters(font, block, rightToLeft); } @Override public BitmapText clone() { BitmapText clone = (BitmapText) super.clone(); for (int i = 0; i < textPages.length; i++) { clone.textPages[i] = textPages[i].clone(); } clone.block = block.clone(); clone.needRefresh = true; return clone; } public BitmapFont getFont() { return font; } /** * Changes text size * @param size text size */ public void setSize(float size) { block.setSize(size); needRefresh = true; letters.invalidate(); } /** * * @param text charsequence to change text to */ public void setText(CharSequence text) { // note: text.toString() is free if text is already a java.lang.String. setText( text != null ? text.toString() : null ); } /** * * @param text String to change text to */ public void setText(String text) { text = text == null ? "" : text; if (text == block.getText() || block.getText().equals(text)) { return; } block.setText(text); letters.setText(text); needRefresh = true; } /** * @return returns text */ public String getText() { return block.getText(); } /** * @return color of the text */ public ColorRGBA getColor() { return letters.getBaseColor(); } /** * changes text color. all substring colors are deleted. * @param color new color of text */ public void setColor(ColorRGBA color) { letters.setColor(color); letters.invalidate(); // TODO: Don't have to align. needRefresh = true; } /** * Define area where bitmaptext will be rendered * @param rect position and size box where text is rendered */ public void setBox(Rectangle rect) { block.setTextBox(rect); letters.invalidate(); needRefresh = true; } /** * @return height of the line */ public float getLineHeight() { return font.getLineHeight(block); } /** * @return height of whole textblock */ public float getHeight() { if (needRefresh) { assemble(); } float height = getLineHeight()*block.getLineCount(); Rectangle textBox = block.getTextBox(); if (textBox != null) { return Math.max(height, textBox.height); } return height; } /** * @return width of line */ public float getLineWidth() { if (needRefresh) { assemble(); } Rectangle textBox = block.getTextBox(); if (textBox != null) { return Math.max(letters.getTotalWidth(), textBox.width); } return letters.getTotalWidth(); } /** * @return line count */ public int getLineCount() { if (needRefresh) { assemble(); } return block.getLineCount(); } public LineWrapMode getLineWrapMode() { return block.getLineWrapMode(); } /** * Set horizontal alignment. Applicable only when text bound is set. * @param align */ public void setAlignment(BitmapFont.Align align) { if (block.getTextBox() == null && align != Align.Left) { throw new RuntimeException("Bound is not set"); } block.setAlignment(align); letters.invalidate(); needRefresh = true; } /** * Set vertical alignment. Applicable only when text bound is set. * @param align */ public void setVerticalAlignment(BitmapFont.VAlign align) { if (block.getTextBox() == null && align != VAlign.Top) { throw new RuntimeException("Bound is not set"); } block.setVerticalAlignment(align); letters.invalidate(); needRefresh = true; } public BitmapFont.Align getAlignment() { return block.getAlignment(); } public BitmapFont.VAlign getVerticalAlignment() { return block.getVerticalAlignment(); } /** * Set the font style of substring. If font doesn't contain style, default style is used * @param start start index to set style. inclusive. * @param end end index to set style. EXCLUSIVE. * @param style */ public void setStyle(int start, int end, int style) { letters.setStyle(start, end, style); } /** * Set the font style of substring. If font doesn't contain style, default style is applied * @param regexp regular expression * @param style */ public void setStyle(String regexp, int style) { Pattern p = Pattern.compile(regexp); Matcher m = p.matcher(block.getText()); while (m.find()) { setStyle(m.start(), m.end(), style); } } /** * Set the color of substring. * @param start start index to set style. inclusive. * @param end end index to set style. EXCLUSIVE. * @param color */ public void setColor(int start, int end, ColorRGBA color) { letters.setColor(start, end, color); letters.invalidate(); needRefresh = true; } /** * Set the color of substring. * @param regexp regular expression * @param color */ public void setColor(String regexp, ColorRGBA color) { Pattern p = Pattern.compile(regexp); Matcher m = p.matcher(block.getText()); while (m.find()) { letters.setColor(m.start(), m.end(), color); } letters.invalidate(); needRefresh = true; } /** * @param tabs tab positions */ public void setTabPosition(float... tabs) { block.setTabPosition(tabs); letters.invalidate(); needRefresh = false; } /** * used for the tabs over the last tab position. * @param width tab size */ public void setTabWidth(float width) { block.setTabWidth(width); letters.invalidate(); needRefresh = false; } /** * for setLineWrapType(LineWrapType.NoWrap), * set the last character when the text exceeds the bound. * @param c */ public void setEllipsisChar(char c) { block.setEllipsisChar(c); letters.invalidate(); needRefresh = false; } /** * Available only when bounding is set. setBox() method call is needed in advance. * true when * @param wrap NoWrap : Letters over the text bound is not shown. the last character is set to '...'(0x2026) * Character: Character is split at the end of the line. * Word : Word is split at the end of the line. */ public void setLineWrapMode(LineWrapMode wrap) { if (block.getLineWrapMode() != wrap) { block.setLineWrapMode(wrap); letters.invalidate(); needRefresh = true; } } @Override public void updateLogicalState(float tpf) { super.updateLogicalState(tpf); if (needRefresh) { assemble(); } } private void assemble() { // first generate quadlist letters.update(); for (int i = 0; i < textPages.length; i++) { textPages[i].assemble(letters); } needRefresh = false; } public void render(RenderManager rm) { for (BitmapTextPage page : textPages) { Material mat = page.getMaterial(); mat.setTexture("Texture", page.getTexture()); mat.render(page, rm); } } }