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