blob: 76013246240d27fd45338523be3032c714d95280 [file] [log] [blame]
/*
* 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. <code>setBox()</code> 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);
}
}
}