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 * Unlike most scene2d.ui widgets, label can be scaled and rotated using the actor's scale, rotation, and origin. This only
\r
32 * affects drawing, other scene2d.ui widgets will still use the unscaled and unrotated bounds of the label. Note that a scaled or
\r
33 * rotated label causes a SpriteBatch flush when it is drawn, so should be used relatively sparingly.
\r
35 * The preferred size of the label is determined by the actual text bounds, unless {@link #setWrap(boolean) word wrap} is enabled.
\r
36 * @author Nathan Sweet */
\r
37 public class Label extends Widget {
\r
38 private LabelStyle style;
\r
39 private final TextBounds bounds = new TextBounds();
\r
40 private final StringBuilder text = new StringBuilder();
\r
41 private StringBuilder tempText;
\r
42 private BitmapFontCache cache;
\r
43 private int labelAlign = Align.left;
\r
44 private HAlignment lineAlign = HAlignment.LEFT;
\r
45 private boolean wrap;
\r
46 private float lastPrefHeight;
\r
47 private boolean sizeInvalid = true;
\r
48 private float fontScaleX = 1, fontScaleY = 1;
\r
50 public Label (CharSequence text, Skin skin) {
\r
51 this(text, skin.get(LabelStyle.class));
\r
54 public Label (CharSequence text, Skin skin, String styleName) {
\r
55 this(text, skin.get(styleName, LabelStyle.class));
\r
58 /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name from the skin and the specified
\r
60 public Label (CharSequence text, Skin skin, String fontName, Color color) {
\r
61 this(text, new LabelStyle(skin.getFont(fontName), color));
\r
64 /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name and the specified color from the
\r
66 public Label (CharSequence text, Skin skin, String fontName, String colorName) {
\r
67 this(text, new LabelStyle(skin.getFont(fontName), skin.getColor(colorName)));
\r
70 public Label (CharSequence text, LabelStyle style) {
\r
71 if (text != null) this.text.append(text);
\r
73 setWidth(getPrefWidth());
\r
74 setHeight(getPrefHeight());
\r
77 public void setStyle (LabelStyle style) {
\r
78 if (style == null) throw new IllegalArgumentException("style cannot be null.");
\r
79 if (style.font == null) throw new IllegalArgumentException("Missing LabelStyle font.");
\r
81 cache = new BitmapFontCache(style.font, style.font.usesIntegerPositions());
\r
82 invalidateHierarchy();
\r
85 /** Returns the label's style. Modifying the returned style may not have an effect until {@link #setStyle(LabelStyle)} is
\r
87 public LabelStyle getStyle () {
\r
91 /** @param newText May be null. */
\r
92 public void setText (CharSequence newText) {
\r
93 if (newText instanceof StringBuilder) {
\r
94 if (text.equals(newText)) return;
\r
96 text.append((StringBuilder)newText);
\r
98 if (newText == null) newText = "";
\r
99 if (textEquals(newText)) return;
\r
101 text.append(newText);
\r
103 invalidateHierarchy();
\r
106 public boolean textEquals (CharSequence other) {
\r
107 int length = text.length;
\r
108 char[] chars = text.chars;
\r
109 if (length != other.length()) return false;
\r
110 for (int i = 0; i < length; i++)
\r
111 if (chars[i] != other.charAt(i)) return false;
\r
115 public CharSequence getText () {
\r
119 public void invalidate () {
\r
120 super.invalidate();
\r
121 sizeInvalid = true;
\r
124 private void computeSize () {
\r
125 sizeInvalid = false;
\r
127 float width = getWidth();
\r
128 if (style.background != null) width -= style.background.getLeftWidth() + style.background.getRightWidth();
\r
129 bounds.set(cache.getFont().getWrappedBounds(text, width));
\r
131 bounds.set(cache.getFont().getMultiLineBounds(text));
\r
132 bounds.width *= fontScaleX;
\r
133 bounds.height *= fontScaleY;
\r
136 public void layout () {
\r
137 if (sizeInvalid) computeSize();
\r
140 float prefHeight = getPrefHeight();
\r
141 if (prefHeight != lastPrefHeight) {
\r
142 lastPrefHeight = prefHeight;
\r
143 invalidateHierarchy();
\r
147 BitmapFont font = cache.getFont();
\r
148 float oldScaleX = font.getScaleX();
\r
149 float oldScaleY = font.getScaleY();
\r
150 if (fontScaleX != 1 || fontScaleY != 1) font.setScale(fontScaleX, fontScaleY);
\r
152 float width = getWidth(), height = getHeight();
\r
153 StringBuilder text;
\r
154 if (width < bounds.width) {
\r
155 float ellipseWidth = font.getBounds("...").width;
\r
156 text = tempText != null ? tempText : (tempText = new StringBuilder());
\r
158 if (width > ellipseWidth) {
\r
159 text.append(this.text, 0, font.computeVisibleGlyphs(this.text, 0, this.text.length, width - ellipseWidth));
\r
160 text.append("...");
\r
165 Drawable background = style.background;
\r
166 float x = 0, y = 0;
\r
167 if (background != null) {
\r
168 x = background.getLeftWidth();
\r
169 y = background.getBottomHeight();
\r
170 width -= background.getLeftWidth() + background.getRightWidth();
\r
171 height -= background.getBottomHeight() + background.getTopHeight();
\r
173 if ((labelAlign & Align.top) != 0) {
\r
174 y += cache.getFont().isFlipped() ? 0 : height - bounds.height;
\r
175 y += style.font.getDescent();
\r
176 } else if ((labelAlign & Align.bottom) != 0) {
\r
177 y += cache.getFont().isFlipped() ? height - bounds.height : 0;
\r
178 y -= style.font.getDescent();
\r
180 y += (int)((height - bounds.height) / 2);
\r
181 if (!cache.getFont().isFlipped()) y += bounds.height;
\r
183 if ((labelAlign & Align.left) == 0) {
\r
184 if ((labelAlign & Align.right) != 0)
\r
185 x += width - bounds.width;
\r
187 x += (int)((width - bounds.width) / 2);
\r
191 cache.setWrappedText(text, x, y, bounds.width, lineAlign);
\r
193 cache.setMultiLineText(text, x, y, bounds.width, lineAlign);
\r
195 if (fontScaleX != 1 || fontScaleY != 1) font.setScale(oldScaleX, oldScaleY);
\r
198 public void draw (SpriteBatch batch, float parentAlpha) {
\r
200 Color color = getColor();
\r
201 if (style.background != null) {
\r
202 batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
\r
203 style.background.draw(batch, getX(), getY(), getWidth(), getHeight());
\r
205 cache.setColor(style.fontColor == null ? color : Color.tmp.set(color).mul(style.fontColor));
\r
206 cache.setPosition(getX(), getY());
\r
207 cache.draw(batch, color.a * parentAlpha);
\r
210 public float getPrefWidth () {
\r
211 if (wrap) return 0;
\r
212 if (sizeInvalid) computeSize();
\r
213 float width = bounds.width;
\r
214 Drawable background = style.background;
\r
215 if (background != null) width += background.getLeftWidth() + background.getRightWidth();
\r
219 public float getPrefHeight () {
\r
220 if (sizeInvalid) computeSize();
\r
221 float height = bounds.height - style.font.getDescent() * 2;
\r
222 Drawable background = style.background;
\r
223 if (background != null) height += background.getTopHeight() + background.getBottomHeight();
\r
227 public TextBounds getTextBounds () {
\r
228 if (sizeInvalid) computeSize();
\r
232 /** If false, the text will only wrap where it contains newlines (\n). The preferred size of the label will be the text bounds.
\r
233 * 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
234 * that the something external will set the width of the label. Default is false. */
\r
235 public void setWrap (boolean wrap) {
\r
237 invalidateHierarchy();
\r
240 /** @param wrapAlign Aligns each line of text horizontally and all the text vertically.
\r
242 public void setAlignment (int wrapAlign) {
\r
243 setAlignment(wrapAlign, wrapAlign);
\r
246 /** @param labelAlign Aligns all the text with the label widget.
\r
247 * @param lineAlign Aligns each line of text (left, right, or center).
\r
249 public void setAlignment (int labelAlign, int lineAlign) {
\r
250 this.labelAlign = labelAlign;
\r
252 if ((lineAlign & Align.left) != 0)
\r
253 this.lineAlign = HAlignment.LEFT;
\r
254 else if ((lineAlign & Align.right) != 0)
\r
255 this.lineAlign = HAlignment.RIGHT;
\r
257 this.lineAlign = HAlignment.CENTER;
\r
262 public void setFontScale (float fontScale) {
\r
263 this.fontScaleX = fontScale;
\r
264 this.fontScaleY = fontScale;
\r
265 invalidateHierarchy();
\r
268 public void setFontScale (float fontScaleX, float fontScaleY) {
\r
269 this.fontScaleX = fontScaleX;
\r
270 this.fontScaleY = fontScaleY;
\r
271 invalidateHierarchy();
\r
274 public float getFontScaleX () {
\r
278 public void setFontScaleX (float fontScaleX) {
\r
279 this.fontScaleX = fontScaleX;
\r
280 invalidateHierarchy();
\r
283 public float getFontScaleY () {
\r
287 public void setFontScaleY (float fontScaleY) {
\r
288 this.fontScaleY = fontScaleY;
\r
289 invalidateHierarchy();
\r
292 /** The style for a label, see {@link Label}.
\r
293 * @author Nathan Sweet */
\r
294 static public class LabelStyle {
\r
295 public BitmapFont font;
\r
297 public Color fontColor;
\r
299 public Drawable background;
\r
301 public LabelStyle () {
\r
304 public LabelStyle (BitmapFont font, Color fontColor) {
\r
306 this.fontColor = fontColor;
\r
309 public LabelStyle (LabelStyle style) {
\r
310 this.font = style.font;
\r
311 if (style.fontColor != null) fontColor = new Color(style.fontColor);
\r
312 background = style.background;
\r