Commit | Line | Data |
---|---|---|
a3b510ab NR |
1 | /* |
2 | * This file is part of lanterna (http://code.google.com/p/lanterna/). | |
3 | * | |
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. | |
8 | * | |
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. | |
13 | * | |
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/>. | |
16 | * | |
17 | * Copyright (C) 2010-2015 Martin | |
18 | */ | |
19 | package com.googlecode.lanterna.gui2; | |
20 | ||
21 | import com.googlecode.lanterna.TerminalTextUtils; | |
22 | import com.googlecode.lanterna.TerminalPosition; | |
23 | import com.googlecode.lanterna.TerminalSize; | |
24 | import com.googlecode.lanterna.graphics.ThemeDefinition; | |
25 | import com.googlecode.lanterna.input.KeyStroke; | |
26 | import com.googlecode.lanterna.input.KeyType; | |
27 | ||
28 | /** | |
29 | * Simple labeled button with an optional action attached to it, you trigger the action by pressing the Enter key on the | |
30 | * keyboard when the component is in focus. | |
31 | * @author Martin | |
32 | */ | |
33 | public class Button extends AbstractInteractableComponent<Button> { | |
34 | private final Runnable action; | |
35 | private String label; | |
36 | ||
37 | /** | |
38 | * Creates a new button with a specific label and no attached action. Why would you need this? I have no idea. | |
39 | * @param label Label to put on the button | |
40 | */ | |
41 | public Button(String label) { | |
42 | this(label, new Runnable() { | |
43 | @Override | |
44 | public void run() { | |
45 | } | |
46 | }); | |
47 | } | |
48 | ||
49 | /** | |
50 | * Creates a new button with a label and an associated action to fire when triggered by the user | |
51 | * @param label Label to put on the button | |
52 | * @param action What action to fire when the user triggers the button by pressing the enter key | |
53 | */ | |
54 | public Button(String label, Runnable action) { | |
55 | this.action = action; | |
56 | setLabel(label); | |
57 | } | |
58 | ||
59 | @Override | |
60 | protected ButtonRenderer createDefaultRenderer() { | |
61 | return new DefaultButtonRenderer(); | |
62 | } | |
63 | ||
64 | @Override | |
65 | public synchronized TerminalPosition getCursorLocation() { | |
66 | return getRenderer().getCursorLocation(this); | |
67 | } | |
68 | ||
69 | @Override | |
70 | public synchronized Result handleKeyStroke(KeyStroke keyStroke) { | |
71 | if(keyStroke.getKeyType() == KeyType.Enter) { | |
72 | action.run(); | |
73 | return Result.HANDLED; | |
74 | } | |
75 | return super.handleKeyStroke(keyStroke); | |
76 | } | |
77 | ||
78 | /** | |
79 | * Updates the label on the button to the specified string | |
80 | * @param label New label to use on the button | |
81 | */ | |
82 | public final synchronized void setLabel(String label) { | |
83 | if(label == null) { | |
84 | throw new IllegalArgumentException("null label to a button is not allowed"); | |
85 | } | |
86 | if(label.isEmpty()) { | |
87 | label = " "; | |
88 | } | |
89 | this.label = label; | |
90 | invalidate(); | |
91 | } | |
92 | ||
93 | /** | |
94 | * Returns the label current assigned to the button | |
95 | * @return Label currently used by the button | |
96 | */ | |
97 | public String getLabel() { | |
98 | return label; | |
99 | } | |
100 | ||
101 | @Override | |
102 | public String toString() { | |
103 | return "Button{" + label + "}"; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Helper interface that doesn't add any new methods but makes coding new button renderers a little bit more clear | |
108 | */ | |
109 | public interface ButtonRenderer extends InteractableRenderer<Button> { | |
110 | } | |
111 | ||
112 | /** | |
113 | * This is the default button renderer that is used if you don't override anything. With this renderer, buttons are | |
114 | * drawn on a single line, with the label inside of "<" and ">". | |
115 | */ | |
116 | public static class DefaultButtonRenderer implements ButtonRenderer { | |
117 | @Override | |
118 | public TerminalPosition getCursorLocation(Button button) { | |
119 | return new TerminalPosition(1 + getLabelShift(button, button.getSize()), 0); | |
120 | } | |
121 | ||
122 | @Override | |
123 | public TerminalSize getPreferredSize(Button button) { | |
124 | return new TerminalSize(Math.max(8, TerminalTextUtils.getColumnWidth(button.getLabel()) + 2), 1); | |
125 | } | |
126 | ||
127 | @Override | |
128 | public void drawComponent(TextGUIGraphics graphics, Button button) { | |
129 | if(button.isFocused()) { | |
130 | graphics.applyThemeStyle(getThemeDefinition(graphics).getActive()); | |
131 | } | |
132 | else { | |
133 | graphics.applyThemeStyle(getThemeDefinition(graphics).getInsensitive()); | |
134 | } | |
135 | graphics.fill(' '); | |
136 | graphics.setCharacter(0, 0, getThemeDefinition(graphics).getCharacter("LEFT_BORDER", '<')); | |
137 | graphics.setCharacter(graphics.getSize().getColumns() - 1, 0, getThemeDefinition(graphics).getCharacter("RIGHT_BORDER", '>')); | |
138 | ||
139 | if(button.isFocused()) { | |
140 | graphics.applyThemeStyle(getThemeDefinition(graphics).getActive()); | |
141 | } | |
142 | else { | |
143 | graphics.applyThemeStyle(getThemeDefinition(graphics).getPreLight()); | |
144 | } | |
145 | int labelShift = getLabelShift(button, graphics.getSize()); | |
146 | graphics.setCharacter(1 + labelShift, 0, button.getLabel().charAt(0)); | |
147 | ||
148 | if(TerminalTextUtils.getColumnWidth(button.getLabel()) == 1) { | |
149 | return; | |
150 | } | |
151 | if(button.isFocused()) { | |
152 | graphics.applyThemeStyle(getThemeDefinition(graphics).getSelected()); | |
153 | } | |
154 | else { | |
155 | graphics.applyThemeStyle(getThemeDefinition(graphics).getNormal()); | |
156 | } | |
157 | graphics.putString(1 + labelShift + 1, 0, button.getLabel().substring(1)); | |
158 | } | |
159 | ||
160 | private int getLabelShift(Button button, TerminalSize size) { | |
161 | int availableSpace = size.getColumns() - 2; | |
162 | if(availableSpace <= 0) { | |
163 | return 0; | |
164 | } | |
165 | int labelShift = 0; | |
166 | int widthInColumns = TerminalTextUtils.getColumnWidth(button.getLabel()); | |
167 | if(availableSpace > widthInColumns) { | |
168 | labelShift = (size.getColumns() - 2 - widthInColumns) / 2; | |
169 | } | |
170 | return labelShift; | |
171 | } | |
172 | } | |
173 | ||
174 | /** | |
175 | * Alternative button renderer that displays buttons with just the label and minimal decoration | |
176 | */ | |
177 | public static class FlatButtonRenderer implements ButtonRenderer { | |
178 | @Override | |
179 | public TerminalPosition getCursorLocation(Button component) { | |
180 | return null; | |
181 | } | |
182 | ||
183 | @Override | |
184 | public TerminalSize getPreferredSize(Button component) { | |
185 | return new TerminalSize(TerminalTextUtils.getColumnWidth(component.getLabel()), 1); | |
186 | } | |
187 | ||
188 | @Override | |
189 | public void drawComponent(TextGUIGraphics graphics, Button button) { | |
190 | if(button.isFocused()) { | |
191 | graphics.applyThemeStyle(getThemeDefinition(graphics).getActive()); | |
192 | } | |
193 | else { | |
194 | graphics.applyThemeStyle(getThemeDefinition(graphics).getInsensitive()); | |
195 | } | |
196 | graphics.fill(' '); | |
197 | if(button.isFocused()) { | |
198 | graphics.applyThemeStyle(getThemeDefinition(graphics).getSelected()); | |
199 | } | |
200 | else { | |
201 | graphics.applyThemeStyle(getThemeDefinition(graphics).getNormal()); | |
202 | } | |
203 | graphics.putString(0, 0, button.getLabel()); | |
204 | } | |
205 | } | |
206 | ||
207 | private static ThemeDefinition getThemeDefinition(TextGUIGraphics graphics) { | |
208 | return graphics.getThemeDefinition(Button.class); | |
209 | } | |
210 | } |