OSDN Git Service

Ellipse for cut off labels.
[mikumikustudio/libgdx-mikumikustudio.git] / gdx / src / com / badlogic / gdx / scenes / scene2d / ui / Label.java
1 /*******************************************************************************\r
2  * Copyright 2011 See AUTHORS file.\r
3  * \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
7  * \r
8  *   http://www.apache.org/licenses/LICENSE-2.0\r
9  * \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
16 \r
17 package com.badlogic.gdx.scenes.scene2d.ui;\r
18 \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
28 \r
29 /** A text label, with optional word wrapping.\r
30  * <p>\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
34  * <p>\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
49 \r
50         public Label (CharSequence text, Skin skin) {\r
51                 this(text, skin.get(LabelStyle.class));\r
52         }\r
53 \r
54         public Label (CharSequence text, Skin skin, String styleName) {\r
55                 this(text, skin.get(styleName, LabelStyle.class));\r
56         }\r
57 \r
58         /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name from the skin and the specified\r
59          * color. */\r
60         public Label (CharSequence text, Skin skin, String fontName, Color color) {\r
61                 this(text, new LabelStyle(skin.getFont(fontName), color));\r
62         }\r
63 \r
64         /** Creates a label, using a {@link LabelStyle} that has a BitmapFont with the specified name and the specified color from the\r
65          * skin. */\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
68         }\r
69 \r
70         public Label (CharSequence text, LabelStyle style) {\r
71                 if (text != null) this.text.append(text);\r
72                 setStyle(style);\r
73                 setWidth(getPrefWidth());\r
74                 setHeight(getPrefHeight());\r
75         }\r
76 \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
80                 this.style = style;\r
81                 cache = new BitmapFontCache(style.font, style.font.usesIntegerPositions());\r
82                 invalidateHierarchy();\r
83         }\r
84 \r
85         /** Returns the label's style. Modifying the returned style may not have an effect until {@link #setStyle(LabelStyle)} is\r
86          * called. */\r
87         public LabelStyle getStyle () {\r
88                 return style;\r
89         }\r
90 \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
95                         text.setLength(0);\r
96                         text.append((StringBuilder)newText);\r
97                 } else {\r
98                         if (newText == null) newText = "";\r
99                         if (textEquals(newText)) return;\r
100                         text.setLength(0);\r
101                         text.append(newText);\r
102                 }\r
103                 invalidateHierarchy();\r
104         }\r
105 \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
112                 return true;\r
113         }\r
114 \r
115         public CharSequence getText () {\r
116                 return text;\r
117         }\r
118 \r
119         public void invalidate () {\r
120                 super.invalidate();\r
121                 sizeInvalid = true;\r
122         }\r
123 \r
124         private void computeSize () {\r
125                 sizeInvalid = false;\r
126                 if (wrap) {\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
130                 } else\r
131                         bounds.set(cache.getFont().getMultiLineBounds(text));\r
132                 bounds.width *= fontScaleX;\r
133                 bounds.height *= fontScaleY;\r
134         }\r
135 \r
136         public void layout () {\r
137                 if (sizeInvalid) computeSize();\r
138 \r
139                 if (wrap) {\r
140                         float prefHeight = getPrefHeight();\r
141                         if (prefHeight != lastPrefHeight) {\r
142                                 lastPrefHeight = prefHeight;\r
143                                 invalidateHierarchy();\r
144                         }\r
145                 }\r
146 \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
151 \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
157                         text.setLength(0);\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
161                         }\r
162                 } else\r
163                         text = this.text;\r
164 \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
172                 }\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
179                 } else\r
180                         y += (int)((height - bounds.height) / 2);\r
181                 if (!cache.getFont().isFlipped()) y += bounds.height;\r
182 \r
183                 if ((labelAlign & Align.left) == 0) {\r
184                         if ((labelAlign & Align.right) != 0)\r
185                                 x += width - bounds.width;\r
186                         else\r
187                                 x += (int)((width - bounds.width) / 2);\r
188                 }\r
189 \r
190                 if (wrap)\r
191                         cache.setWrappedText(text, x, y, bounds.width, lineAlign);\r
192                 else\r
193                         cache.setMultiLineText(text, x, y, bounds.width, lineAlign);\r
194 \r
195                 if (fontScaleX != 1 || fontScaleY != 1) font.setScale(oldScaleX, oldScaleY);\r
196         }\r
197 \r
198         public void draw (SpriteBatch batch, float parentAlpha) {\r
199                 validate();\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
204                 }\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
208         }\r
209 \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
216                 return width;\r
217         }\r
218 \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
224                 return height;\r
225         }\r
226 \r
227         public TextBounds getTextBounds () {\r
228                 if (sizeInvalid) computeSize();\r
229                 return bounds;\r
230         }\r
231 \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
236                 this.wrap = wrap;\r
237                 invalidateHierarchy();\r
238         }\r
239 \r
240         /** @param wrapAlign Aligns each line of text horizontally and all the text vertically.\r
241          * @see Align */\r
242         public void setAlignment (int wrapAlign) {\r
243                 setAlignment(wrapAlign, wrapAlign);\r
244         }\r
245 \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
248          * @see Align */\r
249         public void setAlignment (int labelAlign, int lineAlign) {\r
250                 this.labelAlign = labelAlign;\r
251 \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
256                 else\r
257                         this.lineAlign = HAlignment.CENTER;\r
258 \r
259                 invalidate();\r
260         }\r
261 \r
262         public void setFontScale (float fontScale) {\r
263                 this.fontScaleX = fontScale;\r
264                 this.fontScaleY = fontScale;\r
265                 invalidateHierarchy();\r
266         }\r
267 \r
268         public void setFontScale (float fontScaleX, float fontScaleY) {\r
269                 this.fontScaleX = fontScaleX;\r
270                 this.fontScaleY = fontScaleY;\r
271                 invalidateHierarchy();\r
272         }\r
273 \r
274         public float getFontScaleX () {\r
275                 return fontScaleX;\r
276         }\r
277 \r
278         public void setFontScaleX (float fontScaleX) {\r
279                 this.fontScaleX = fontScaleX;\r
280                 invalidateHierarchy();\r
281         }\r
282 \r
283         public float getFontScaleY () {\r
284                 return fontScaleY;\r
285         }\r
286 \r
287         public void setFontScaleY (float fontScaleY) {\r
288                 this.fontScaleY = fontScaleY;\r
289                 invalidateHierarchy();\r
290         }\r
291 \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
296                 /** Optional. */\r
297                 public Color fontColor;\r
298                 /** Optional. */\r
299                 public Drawable background;\r
300 \r
301                 public LabelStyle () {\r
302                 }\r
303 \r
304                 public LabelStyle (BitmapFont font, Color fontColor) {\r
305                         this.font = font;\r
306                         this.fontColor = fontColor;\r
307                 }\r
308 \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
313                 }\r
314         }\r
315 }\r