2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.gui2
;
21 import com
.googlecode
.lanterna
.TerminalTextUtils
;
22 import com
.googlecode
.lanterna
.SGR
;
23 import com
.googlecode
.lanterna
.TerminalSize
;
24 import com
.googlecode
.lanterna
.TextColor
;
25 import com
.googlecode
.lanterna
.graphics
.ThemeDefinition
;
27 import java
.util
.EnumSet
;
28 import java
.util
.List
;
31 * Label is a simple read-only text display component. It supports customized colors and multi-line text.
34 public class Label
extends AbstractComponent
<Label
> {
35 private String
[] lines
;
36 private Integer labelWidth
;
37 private TerminalSize labelSize
;
38 private TextColor foregroundColor
;
39 private TextColor backgroundColor
;
40 private final EnumSet
<SGR
> additionalStyles
;
43 * Main constructor, creates a new Label displaying a specific text.
44 * @param text Text the label will display
46 public Label(String text
) {
48 this.labelSize
= TerminalSize
.ZERO
;
50 this.foregroundColor
= null;
51 this.backgroundColor
= null;
52 this.additionalStyles
= EnumSet
.noneOf(SGR
.class);
57 * Protected access to set the internal representation of the text in this label, to be used by sub-classes of label
58 * in certain cases where {@code setText(..)} doesn't work. In general, you probably want to stick to
59 * {@code setText(..)} instead of this method unless you have a good reason not to.
60 * @param lines New lines this label will display
62 protected void setLines(String
[] lines
) {
67 * Updates the text this label is displaying
68 * @param text New text to display
70 public synchronized void setText(String text
) {
71 setLines(splitIntoMultipleLines(text
));
72 this.labelSize
= getBounds(lines
, labelSize
);
77 * Returns the text this label is displaying. Multi-line labels will have their text concatenated with \n, even if
78 * they were originally set using multi-line text having \r\n as line terminators.
79 * @return String of the text this label is displaying
81 public synchronized String
getText() {
82 if(lines
.length
== 0) {
85 StringBuilder bob
= new StringBuilder(lines
[0]);
86 for(int i
= 1; i
< lines
.length
; i
++) {
87 bob
.append("\n").append(lines
[i
]);
89 return bob
.toString();
93 * Utility method for taking a string and turning it into an array of lines. This method is used in order to deal
94 * with line endings consistently.
95 * @param text Text to split
96 * @return Array of strings that forms the lines of the original string
98 protected String
[] splitIntoMultipleLines(String text
) {
99 return text
.replace("\r", "").split("\n");
103 * Returns the area, in terminal columns and rows, required to fully draw the lines passed in.
104 * @param lines Lines to measure the size of
105 * @param currentBounds Optional (can pass {@code null}) terminal size to use for storing the output values. If the
106 * method is called many times and always returning the same value, passing in an external
107 * reference of this size will avoid creating new {@code TerminalSize} objects every time
108 * @return Size that is required to draw the lines
110 protected TerminalSize
getBounds(String
[] lines
, TerminalSize currentBounds
) {
111 if(currentBounds
== null) {
112 currentBounds
= TerminalSize
.ZERO
;
114 currentBounds
= currentBounds
.withRows(lines
.length
);
115 if(labelWidth
== null || labelWidth
== 0) {
116 int preferredWidth
= 0;
117 for(String line
: lines
) {
118 int lineWidth
= TerminalTextUtils
.getColumnWidth(line
);
119 if(preferredWidth
< lineWidth
) {
120 preferredWidth
= lineWidth
;
123 currentBounds
= currentBounds
.withColumns(preferredWidth
);
126 List
<String
> wordWrapped
= TerminalTextUtils
.getWordWrappedText(labelWidth
, lines
);
127 currentBounds
= currentBounds
.withColumns(labelWidth
).withRows(wordWrapped
.size());
129 return currentBounds
;
133 * Overrides the current theme's foreground color and use the one specified. If called with {@code null}, the
134 * override is cleared and the theme is used again.
135 * @param foregroundColor Foreground color to use when drawing the label, if {@code null} then use the theme's
139 public synchronized Label
setForegroundColor(TextColor foregroundColor
) {
140 this.foregroundColor
= foregroundColor
;
145 * Returns the foreground color used when drawing the label, or {@code null} if the color is read from the current
147 * @return Foreground color used when drawing the label, or {@code null} if the color is read from the current
150 public TextColor
getForegroundColor() {
151 return foregroundColor
;
155 * Overrides the current theme's background color and use the one specified. If called with {@code null}, the
156 * override is cleared and the theme is used again.
157 * @param backgroundColor Background color to use when drawing the label, if {@code null} then use the theme's
161 public synchronized Label
setBackgroundColor(TextColor backgroundColor
) {
162 this.backgroundColor
= backgroundColor
;
167 * Returns the background color used when drawing the label, or {@code null} if the color is read from the current
169 * @return Background color used when drawing the label, or {@code null} if the color is read from the current
172 public TextColor
getBackgroundColor() {
173 return backgroundColor
;
177 * Adds an additional SGR style to use when drawing the label, in case it wasn't enabled by the theme
178 * @param sgr SGR style to enable for this label
181 public synchronized Label
addStyle(SGR sgr
) {
182 additionalStyles
.add(sgr
);
187 * Removes an additional SGR style used when drawing the label, previously added by {@code addStyle(..)}. If the
188 * style you are trying to remove is specified by the theme, calling this method will have no effect.
189 * @param sgr SGR style to remove
192 public synchronized Label
removeStyle(SGR sgr
) {
193 additionalStyles
.remove(sgr
);
198 * Use this method to limit how wide the label can grow. If set to {@code null} there is no limit but if set to a
199 * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
200 * this label width. This may make the label increase in height as new rows may be requested. Please note that some
201 * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
202 * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
203 * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
204 * what was requested. By default this is set to 0.
206 * @param labelWidth Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
207 * wrapping should be used if the assigned area is smaller than the requested size, or a positive
208 * integer setting the requested maximum width at what point word wrapping will begin
211 public synchronized Label
setLabelWidth(Integer labelWidth
) {
212 this.labelWidth
= labelWidth
;
217 * Returns the limit how wide the label can grow. If set to {@code null} or 0 there is no limit but if set to a
218 * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
219 * the label width. This may make the label increase in height as new rows may be requested. Please note that some
220 * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
221 * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
222 * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
223 * what was requested.
224 * @return Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
225 * wrapping should be used if the assigned area is smaller than the requested size, or a positive
226 * integer setting the requested maximum width at what point word wrapping will begin
228 public Integer
getLabelWidth() {
233 protected ComponentRenderer
<Label
> createDefaultRenderer() {
234 return new ComponentRenderer
<Label
>() {
236 public TerminalSize
getPreferredSize(Label Label
) {
241 public void drawComponent(TextGUIGraphics graphics
, Label component
) {
242 ThemeDefinition themeDefinition
= graphics
.getThemeDefinition(Label
.class);
243 graphics
.applyThemeStyle(themeDefinition
.getNormal());
244 if(foregroundColor
!= null) {
245 graphics
.setForegroundColor(foregroundColor
);
247 if(backgroundColor
!= null) {
248 graphics
.setBackgroundColor(backgroundColor
);
250 for(SGR sgr
: additionalStyles
) {
251 graphics
.enableModifiers(sgr
);
254 String
[] linesToDraw
;
255 if(component
.getLabelWidth() == null) {
256 linesToDraw
= component
.lines
;
259 linesToDraw
= TerminalTextUtils
.getWordWrappedText(graphics
.getSize().getColumns(), component
.lines
).toArray(new String
[0]);
262 for(int row
= 0; row
< Math
.min(graphics
.getSize().getRows(), linesToDraw
.length
); row
++) {
263 String line
= linesToDraw
[row
];
264 if(graphics
.getSize().getColumns() >= labelSize
.getColumns()) {
265 graphics
.putString(0, row
, line
);
268 int availableColumns
= graphics
.getSize().getColumns();
269 String fitString
= TerminalTextUtils
.fitString(line
, availableColumns
);
270 graphics
.putString(0, row
, fitString
);