Home | History | Annotate | Download | only in font
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.font;
     33 
     34 import com.jme3.font.BitmapFont.Align;
     35 import com.jme3.font.BitmapFont.VAlign;
     36 import com.jme3.material.Material;
     37 import com.jme3.math.ColorRGBA;
     38 import com.jme3.renderer.RenderManager;
     39 import com.jme3.scene.Node;
     40 import java.util.regex.Matcher;
     41 import java.util.regex.Pattern;
     42 
     43 /**
     44  * @author YongHoon
     45  */
     46 public class BitmapText extends Node {
     47     private BitmapFont font;
     48     private StringBlock block;
     49     private boolean needRefresh = true;
     50     private final BitmapTextPage[] textPages;
     51     private Letters letters;
     52 
     53     public BitmapText(BitmapFont font) {
     54         this(font, false, false);
     55     }
     56 
     57     public BitmapText(BitmapFont font, boolean rightToLeft) {
     58         this(font, rightToLeft, false);
     59     }
     60 
     61     public BitmapText(BitmapFont font, boolean rightToLeft, boolean arrayBased) {
     62         textPages = new BitmapTextPage[font.getPageSize()];
     63         for (int page = 0; page < textPages.length; page++) {
     64             textPages[page] = new BitmapTextPage(font, arrayBased, page);
     65             attachChild(textPages[page]);
     66         }
     67 
     68         this.font = font;
     69         this.block = new StringBlock();
     70         block.setSize(font.getPreferredSize());
     71         letters = new Letters(font, block, rightToLeft);
     72     }
     73 
     74     @Override
     75     public BitmapText clone() {
     76         BitmapText clone = (BitmapText) super.clone();
     77         for (int i = 0; i < textPages.length; i++) {
     78             clone.textPages[i] = textPages[i].clone();
     79         }
     80         clone.block = block.clone();
     81         clone.needRefresh = true;
     82         return clone;
     83     }
     84 
     85     public BitmapFont getFont() {
     86         return font;
     87     }
     88 
     89     /**
     90      * Changes text size
     91      * @param size text size
     92      */
     93     public void setSize(float size) {
     94         block.setSize(size);
     95         needRefresh = true;
     96         letters.invalidate();
     97     }
     98 
     99     /**
    100      *
    101      * @param text charsequence to change text to
    102      */
    103     public void setText(CharSequence text) {
    104         // note: text.toString() is free if text is already a java.lang.String.
    105         setText( text != null ? text.toString() : null );
    106     }
    107 
    108     /**
    109      *
    110      * @param text String to change text to
    111      */
    112     public void setText(String text) {
    113         text = text == null ? "" : text;
    114         if (text == block.getText() || block.getText().equals(text)) {
    115             return;
    116         }
    117 
    118         block.setText(text);
    119         letters.setText(text);
    120         needRefresh = true;
    121     }
    122 
    123     /**
    124      * @return returns text
    125      */
    126     public String getText() {
    127         return block.getText();
    128     }
    129 
    130     /**
    131      * @return color of the text
    132      */
    133     public ColorRGBA getColor() {
    134         return letters.getBaseColor();
    135     }
    136 
    137     /**
    138      * changes text color. all substring colors are deleted.
    139      * @param color new color of text
    140      */
    141     public void setColor(ColorRGBA color) {
    142         letters.setColor(color);
    143         letters.invalidate(); // TODO: Don't have to align.
    144         needRefresh = true;
    145     }
    146 
    147     /**
    148      * Define area where bitmaptext will be rendered
    149      * @param rect position and size box where text is rendered
    150      */
    151     public void setBox(Rectangle rect) {
    152         block.setTextBox(rect);
    153         letters.invalidate();
    154         needRefresh = true;
    155     }
    156 
    157     /**
    158      * @return height of the line
    159      */
    160     public float getLineHeight() {
    161         return font.getLineHeight(block);
    162     }
    163 
    164     /**
    165      * @return height of whole textblock
    166      */
    167     public float getHeight() {
    168         if (needRefresh) {
    169             assemble();
    170         }
    171         float height = getLineHeight()*block.getLineCount();
    172         Rectangle textBox = block.getTextBox();
    173         if (textBox != null) {
    174             return Math.max(height, textBox.height);
    175         }
    176         return height;
    177     }
    178 
    179     /**
    180      * @return width of line
    181      */
    182     public float getLineWidth() {
    183         if (needRefresh) {
    184             assemble();
    185         }
    186         Rectangle textBox = block.getTextBox();
    187         if (textBox != null) {
    188             return Math.max(letters.getTotalWidth(), textBox.width);
    189         }
    190         return letters.getTotalWidth();
    191     }
    192 
    193     /**
    194      * @return line count
    195      */
    196     public int getLineCount() {
    197         if (needRefresh) {
    198             assemble();
    199         }
    200         return block.getLineCount();
    201     }
    202 
    203     public LineWrapMode getLineWrapMode() {
    204         return block.getLineWrapMode();
    205     }
    206 
    207     /**
    208      * Set horizontal alignment. Applicable only when text bound is set.
    209      * @param align
    210      */
    211     public void setAlignment(BitmapFont.Align align) {
    212         if (block.getTextBox() == null && align != Align.Left) {
    213             throw new RuntimeException("Bound is not set");
    214         }
    215         block.setAlignment(align);
    216         letters.invalidate();
    217         needRefresh = true;
    218     }
    219 
    220     /**
    221      * Set vertical alignment. Applicable only when text bound is set.
    222      * @param align
    223      */
    224     public void setVerticalAlignment(BitmapFont.VAlign align) {
    225         if (block.getTextBox() == null && align != VAlign.Top) {
    226             throw new RuntimeException("Bound is not set");
    227         }
    228         block.setVerticalAlignment(align);
    229         letters.invalidate();
    230         needRefresh = true;
    231     }
    232 
    233     public BitmapFont.Align getAlignment() {
    234         return block.getAlignment();
    235     }
    236 
    237     public BitmapFont.VAlign getVerticalAlignment() {
    238         return block.getVerticalAlignment();
    239     }
    240 
    241     /**
    242      * Set the font style of substring. If font doesn't contain style, default style is used
    243      * @param start start index to set style. inclusive.
    244      * @param end   end index to set style. EXCLUSIVE.
    245      * @param style
    246      */
    247     public void setStyle(int start, int end, int style) {
    248         letters.setStyle(start, end, style);
    249     }
    250 
    251     /**
    252      * Set the font style of substring. If font doesn't contain style, default style is applied
    253      * @param regexp regular expression
    254      * @param style
    255      */
    256     public void setStyle(String regexp, int style) {
    257         Pattern p = Pattern.compile(regexp);
    258         Matcher m = p.matcher(block.getText());
    259         while (m.find()) {
    260             setStyle(m.start(), m.end(), style);
    261         }
    262     }
    263 
    264     /**
    265      * Set the color of substring.
    266      * @param start start index to set style. inclusive.
    267      * @param end   end index to set style. EXCLUSIVE.
    268      * @param color
    269      */
    270     public void setColor(int start, int end, ColorRGBA color) {
    271         letters.setColor(start, end, color);
    272         letters.invalidate();
    273         needRefresh = true;
    274     }
    275 
    276     /**
    277      * Set the color of substring.
    278      * @param regexp regular expression
    279      * @param color
    280      */
    281     public void setColor(String regexp, ColorRGBA color) {
    282         Pattern p = Pattern.compile(regexp);
    283         Matcher m = p.matcher(block.getText());
    284         while (m.find()) {
    285             letters.setColor(m.start(), m.end(), color);
    286         }
    287         letters.invalidate();
    288         needRefresh = true;
    289     }
    290 
    291     /**
    292      * @param tabs tab positions
    293      */
    294     public void setTabPosition(float... tabs) {
    295         block.setTabPosition(tabs);
    296         letters.invalidate();
    297         needRefresh = false;
    298     }
    299 
    300     /**
    301      * used for the tabs over the last tab position.
    302      * @param width tab size
    303      */
    304     public void setTabWidth(float width) {
    305         block.setTabWidth(width);
    306         letters.invalidate();
    307         needRefresh = false;
    308     }
    309 
    310     /**
    311      * for setLineWrapType(LineWrapType.NoWrap),
    312      * set the last character when the text exceeds the bound.
    313      * @param c
    314      */
    315     public void setEllipsisChar(char c) {
    316         block.setEllipsisChar(c);
    317         letters.invalidate();
    318         needRefresh = false;
    319     }
    320 
    321     /**
    322      * Available only when bounding is set. <code>setBox()</code> method call is needed in advance.
    323      * true when
    324      * @param wrap NoWrap   : Letters over the text bound is not shown. the last character is set to '...'(0x2026)
    325      *             Character: Character is split at the end of the line.
    326      *             Word     : Word is split at the end of the line.
    327      */
    328     public void setLineWrapMode(LineWrapMode wrap) {
    329         if (block.getLineWrapMode() != wrap) {
    330             block.setLineWrapMode(wrap);
    331             letters.invalidate();
    332             needRefresh = true;
    333         }
    334     }
    335 
    336     @Override
    337     public void updateLogicalState(float tpf) {
    338         super.updateLogicalState(tpf);
    339         if (needRefresh) {
    340             assemble();
    341         }
    342     }
    343 
    344     private void assemble() {
    345         // first generate quadlist
    346         letters.update();
    347 
    348         for (int i = 0; i < textPages.length; i++) {
    349             textPages[i].assemble(letters);
    350         }
    351         needRefresh = false;
    352     }
    353 
    354     public void render(RenderManager rm) {
    355         for (BitmapTextPage page : textPages) {
    356             Material mat = page.getMaterial();
    357             mat.setTexture("Texture", page.getTexture());
    358             mat.render(page, rm);
    359         }
    360     }
    361 }
    362