1 /*******************************************************************************
\r
2 * Copyright 2011 See AUTHORS file.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
15 ******************************************************************************/
\r
17 package com.badlogic.gdx.scenes.scene2d.ui;
\r
19 import com.badlogic.gdx.graphics.Color;
\r
20 import com.badlogic.gdx.graphics.g2d.BitmapFont;
\r
21 import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
\r
22 import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds;
\r
23 import com.badlogic.gdx.graphics.g2d.BitmapFontCache;
\r
24 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
\r
25 import com.badlogic.gdx.scenes.scene2d.utils.Align;
\r
26 import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
\r
27 import com.badlogic.gdx.utils.StringBuilder;
\r
29 /** A text label, with optional word wrapping.
\r
31 * The preferred size of the label is determined by the actual text bounds, unless {@link #setWrap(boolean) word wrap} is enabled.
\r
32 * @author Nathan Sweet */
\r
33 public class Label extends Widget {
\r
34 private LabelStyle style;
\r
35 private final TextBounds bounds = new TextBounds();
\r
36 private final StringBuilder text = new StringBuilder();
\r
37 private StringBuilder tempText;
\r
38 private BitmapFontCache cache;
\r
39 private int labelAlign = Align.left;
\r
40 private HAlignment lineAlign = HAlignment.LEFT;
\r
41 private boolean wrap;
\r
42 private float lastPrefHeight;
\r
43 private boolean sizeInvalid = true;
\r
44 private float fontScaleX = 1, fontScaleY = 1;
\r
45 private boolean ellipse;
\r
47 public Label (CharSequence text, Skin skin) {
\r
48 this(text, skin.get(LabelStyle.class));
\r
51 public Label (CharSequence text, Skin skin, String styleName) {
\r
52 this(text, skin.get(styleName, LabelStyle.class));
\r
55 /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name from the skin and the specified
\r
57 public Label (CharSequence text, Skin skin, String fontName, Color color) {
\r
58 this(text, new LabelStyle(skin.getFont(fontName), color));
\r
61 /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name and the specified color from the
\r
63 public Label (CharSequence text, Skin skin, String fontName, String colorName) {
\r
64 this(text, new LabelStyle(skin.getFont(fontName), skin.getColor(colorName)));
\r
67 public Label (CharSequence text, LabelStyle style) {
\r
68 if (text != null) this.text.append(text);
\r
70 setWidth(getPrefWidth());
\r
71 setHeight(getPrefHeight());
\r
74 public void setStyle (LabelStyle style) {
\r
75 if (style == null) throw new IllegalArgumentException("style cannot be null.");
\r
76 if (style.font == null) throw new IllegalArgumentException("Missing LabelStyle font.");
\r
78 cache = new BitmapFontCache(style.font, style.font.usesIntegerPositions());
\r
79 invalidateHierarchy();
\r
82 /** Returns the label's style. Modifying the returned style may not have an effect until {@link #setStyle(LabelStyle)} is
\r
84 public LabelStyle getStyle () {
\r
88 /** @param newText May be null. */
\r
89 public void setText (CharSequence newText) {
\r
90 if (newText instanceof StringBuilder) {
\r
91 if (text.equals(newText)) return;
\r
93 text.append((StringBuilder)newText);
\r
95 if (newText == null) newText = "";
\r
96 if (textEquals(newText)) return;
\r
98 text.append(newText);
\r
100 invalidateHierarchy();
\r
103 public boolean textEquals (CharSequence other) {
\r
104 int length = text.length;
\r
105 char[] chars = text.chars;
\r
106 if (length != other.length()) return false;
\r
107 for (int i = 0; i < length; i++)
\r
108 if (chars[i] != other.charAt(i)) return false;
\r
112 public CharSequence getText () {
\r
116 public void invalidate () {
\r
117 super.invalidate();
\r
118 sizeInvalid = true;
\r
121 private void computeSize () {
\r
122 sizeInvalid = false;
\r
124 float width = getWidth();
\r
125 if (style.background != null) width -= style.background.getLeftWidth() + style.background.getRightWidth();
\r
126 bounds.set(cache.getFont().getWrappedBounds(text, width));
\r
128 bounds.set(cache.getFont().getMultiLineBounds(text));
\r
129 bounds.width *= fontScaleX;
\r
130 bounds.height *= fontScaleY;
\r
133 public void layout () {
\r
134 if (sizeInvalid) computeSize();
\r
137 float prefHeight = getPrefHeight();
\r
138 if (prefHeight != lastPrefHeight) {
\r
139 lastPrefHeight = prefHeight;
\r
140 invalidateHierarchy();
\r
144 BitmapFont font = cache.getFont();
\r
145 float oldScaleX = font.getScaleX();
\r
146 float oldScaleY = font.getScaleY();
\r
147 if (fontScaleX != 1 || fontScaleY != 1) font.setScale(fontScaleX, fontScaleY);
\r
149 float width = getWidth(), height = getHeight();
\r
150 StringBuilder text;
\r
151 if (ellipse && width < bounds.width) {
\r
152 float ellipseWidth = font.getBounds("...").width;
\r
153 text = tempText != null ? tempText : (tempText = new StringBuilder());
\r
155 if (width > ellipseWidth) {
\r
156 text.append(this.text, 0, font.computeVisibleGlyphs(this.text, 0, this.text.length, width - ellipseWidth));
\r
157 text.append("...");
\r
162 Drawable background = style.background;
\r
163 float x = 0, y = 0;
\r
164 if (background != null) {
\r
165 x = background.getLeftWidth();
\r
166 y = background.getBottomHeight();
\r
167 width -= background.getLeftWidth() + background.getRightWidth();
\r
168 height -= background.getBottomHeight() + background.getTopHeight();
\r
170 if ((labelAlign & Align.top) != 0) {
\r
171 y += cache.getFont().isFlipped() ? 0 : height - bounds.height;
\r
172 y += style.font.getDescent();
\r
173 } else if ((labelAlign & Align.bottom) != 0) {
\r
174 y += cache.getFont().isFlipped() ? height - bounds.height : 0;
\r
175 y -= style.font.getDescent();
\r
177 y += (int)((height - bounds.height) / 2);
\r
178 if (!cache.getFont().isFlipped()) y += bounds.height;
\r
180 if ((labelAlign & Align.left) == 0) {
\r
181 if ((labelAlign & Align.right) != 0)
\r
182 x += width - bounds.width;
\r
184 x += (int)((width - bounds.width) / 2);
\r
188 cache.setWrappedText(text, x, y, bounds.width, lineAlign);
\r
190 cache.setMultiLineText(text, x, y, bounds.width, lineAlign);
\r
192 if (fontScaleX != 1 || fontScaleY != 1) font.setScale(oldScaleX, oldScaleY);
\r
195 public void draw (SpriteBatch batch, float parentAlpha) {
\r
197 Color color = getColor();
\r
198 if (style.background != null) {
\r
199 batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
\r
200 style.background.draw(batch, getX(), getY(), getWidth(), getHeight());
\r
202 cache.setColor(style.fontColor == null ? color : Color.tmp.set(color).mul(style.fontColor));
\r
203 cache.setPosition(getX(), getY());
\r
204 cache.draw(batch, color.a * parentAlpha);
\r
207 public float getPrefWidth () {
\r
208 if (wrap) return 0;
\r
209 if (sizeInvalid) computeSize();
\r
210 float width = bounds.width;
\r
211 Drawable background = style.background;
\r
212 if (background != null) width += background.getLeftWidth() + background.getRightWidth();
\r
216 public float getPrefHeight () {
\r
217 if (sizeInvalid) computeSize();
\r
218 float height = bounds.height - style.font.getDescent() * 2;
\r
219 Drawable background = style.background;
\r
220 if (background != null) height += background.getTopHeight() + background.getBottomHeight();
\r
224 public TextBounds getTextBounds () {
\r
225 if (sizeInvalid) computeSize();
\r
229 /** If false, the text will only wrap where it contains newlines (\n). The preferred size of the label will be the text bounds.
\r
230 * If true, the text will word wrap using the width of the label. The preferred width of the label will be 0, it is expected
\r
231 * that the something external will set the width of the label. Default is false. */
\r
232 public void setWrap (boolean wrap) {
\r
234 invalidateHierarchy();
\r
237 /** @param wrapAlign Aligns each line of text horizontally and all the text vertically.
\r
239 public void setAlignment (int wrapAlign) {
\r
240 setAlignment(wrapAlign, wrapAlign);
\r
243 /** @param labelAlign Aligns all the text with the label widget.
\r
244 * @param lineAlign Aligns each line of text (left, right, or center).
\r
246 public void setAlignment (int labelAlign, int lineAlign) {
\r
247 this.labelAlign = labelAlign;
\r
249 if ((lineAlign & Align.left) != 0)
\r
250 this.lineAlign = HAlignment.LEFT;
\r
251 else if ((lineAlign & Align.right) != 0)
\r
252 this.lineAlign = HAlignment.RIGHT;
\r
254 this.lineAlign = HAlignment.CENTER;
\r
259 public void setFontScale (float fontScale) {
\r
260 this.fontScaleX = fontScale;
\r
261 this.fontScaleY = fontScale;
\r
262 invalidateHierarchy();
\r
265 public void setFontScale (float fontScaleX, float fontScaleY) {
\r
266 this.fontScaleX = fontScaleX;
\r
267 this.fontScaleY = fontScaleY;
\r
268 invalidateHierarchy();
\r
271 public float getFontScaleX () {
\r
275 public void setFontScaleX (float fontScaleX) {
\r
276 this.fontScaleX = fontScaleX;
\r
277 invalidateHierarchy();
\r
280 public float getFontScaleY () {
\r
284 public void setFontScaleY (float fontScaleY) {
\r
285 this.fontScaleY = fontScaleY;
\r
286 invalidateHierarchy();
\r
289 /** When true the text will be truncated with an ellipse if it does not fit within the width of the label. Default is false. */
\r
290 public void setEllipse (boolean ellipse) {
\r
291 this.ellipse = ellipse;
\r
294 /** The style for a label, see {@link Label}.
\r
295 * @author Nathan Sweet */
\r
296 static public class LabelStyle {
\r
297 public BitmapFont font;
\r
299 public Color fontColor;
\r
301 public Drawable background;
\r
303 public LabelStyle () {
\r
306 public LabelStyle (BitmapFont font, Color fontColor) {
\r
308 this.fontColor = fontColor;
\r
311 public LabelStyle (LabelStyle style) {
\r
312 this.font = style.font;
\r
313 if (style.fontColor != null) fontColor = new Color(style.fontColor);
\r
314 background = style.background;
\r