Merge branch 'subtree'
[fanfix.git] / src / jexer / TButton.java
CommitLineData
daa4106c 1/*
30d336cc
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
30d336cc 5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
30d336cc 7 *
e16dda65
KL
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
30d336cc 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
30d336cc 17 *
e16dda65
KL
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
30d336cc
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer;
30
31import jexer.bits.CellAttributes;
32import jexer.bits.Color;
33import jexer.bits.GraphicsChars;
34import jexer.bits.MnemonicString;
3af53a35 35import jexer.bits.StringUtils;
30d336cc
KL
36import jexer.event.TKeypressEvent;
37import jexer.event.TMouseEvent;
a39c5665
KL
38import static jexer.TKeypress.kbEnter;
39import static jexer.TKeypress.kbSpace;
30d336cc
KL
40
41/**
42 * TButton implements a simple button. To make the button do something, pass
43 * a TAction class to its constructor.
44 *
45 * @see TAction#DO()
46 */
051e2913 47public class TButton extends TWidget {
30d336cc 48
d36057df
KL
49 // ------------------------------------------------------------------------
50 // Variables --------------------------------------------------------------
51 // ------------------------------------------------------------------------
52
30d336cc
KL
53 /**
54 * The shortcut and button text.
55 */
56 private MnemonicString mnemonic;
57
58 /**
59 * Remember mouse state.
60 */
61 private TMouseEvent mouse;
62
63 /**
64 * True when the button is being pressed and held down.
65 */
66 private boolean inButtonPress = false;
67
68 /**
69 * The action to perform when the button is clicked.
70 */
71 private TAction action;
72
e23ea538 73 /**
a39c5665
KL
74 * The background color used for the button "shadow", or null for "no
75 * shadow".
e23ea538
KL
76 */
77 private CellAttributes shadowColor;
a39c5665 78
d36057df
KL
79 // ------------------------------------------------------------------------
80 // Constructors -----------------------------------------------------------
81 // ------------------------------------------------------------------------
91c9a837 82
30d336cc
KL
83 /**
84 * Private constructor.
85 *
86 * @param parent parent widget
87 * @param text label on the button
88 * @param x column relative to parent
89 * @param y row relative to parent
90 */
91 private TButton(final TWidget parent, final String text,
92 final int x, final int y) {
93
94 // Set parent and window
95 super(parent);
96
97 mnemonic = new MnemonicString(text);
98
99 setX(x);
100 setY(y);
d8dc8aea
KL
101 super.setHeight(2);
102 super.setWidth(StringUtils.width(mnemonic.getRawLabel()) + 3);
e23ea538
KL
103
104 shadowColor = new CellAttributes();
105 shadowColor.setTo(getWindow().getBackground());
106 shadowColor.setForeColor(Color.BLACK);
107 shadowColor.setBold(false);
d8dc8aea
KL
108
109 // Since we set dimensions after TWidget's constructor, we need to
110 // update the layout manager.
111 if (getParent().getLayoutManager() != null) {
112 getParent().getLayoutManager().remove(this);
113 getParent().getLayoutManager().add(this);
114 }
30d336cc
KL
115 }
116
117 /**
118 * Public constructor.
119 *
120 * @param parent parent widget
121 * @param text label on the button
122 * @param x column relative to parent
123 * @param y row relative to parent
124 * @param action to call when button is pressed
125 */
126 public TButton(final TWidget parent, final String text,
127 final int x, final int y, final TAction action) {
128
129 this(parent, text, x, y);
130 this.action = action;
131 }
c62861df
NR
132
133 /**
134 * The action to call when the button is pressed.
135 **/
136 public TAction getAction() {
137 return action;
138 }
139
140 /**
141 * The action to call when the button is pressed.
142 **/
143 public void setAction(TAction action) {
144 this.action = action;
145 }
30d336cc 146
d36057df
KL
147 // ------------------------------------------------------------------------
148 // Event handlers ---------------------------------------------------------
149 // ------------------------------------------------------------------------
150
30d336cc
KL
151 /**
152 * Returns true if the mouse is currently on the button.
153 *
154 * @return if true the mouse is currently on the button
155 */
156 private boolean mouseOnButton() {
157 int rightEdge = getWidth() - 1;
158 if (inButtonPress) {
159 rightEdge++;
160 }
161 if ((mouse != null)
162 && (mouse.getY() == 0)
163 && (mouse.getX() >= 0)
164 && (mouse.getX() < rightEdge)
165 ) {
166 return true;
167 }
168 return false;
169 }
170
30d336cc
KL
171 /**
172 * Handle mouse button presses.
173 *
174 * @param mouse mouse button event
175 */
176 @Override
177 public void onMouseDown(final TMouseEvent mouse) {
178 this.mouse = mouse;
179
7c870d89 180 if ((mouseOnButton()) && (mouse.isMouse1())) {
30d336cc
KL
181 // Begin button press
182 inButtonPress = true;
183 }
184 }
185
186 /**
187 * Handle mouse button releases.
188 *
189 * @param mouse mouse button release event
190 */
191 @Override
192 public void onMouseUp(final TMouseEvent mouse) {
193 this.mouse = mouse;
194
7c870d89 195 if (inButtonPress && mouse.isMouse1()) {
30d336cc 196 // Dispatch the event
a325f111 197 dispatch();
30d336cc
KL
198 }
199
200 }
201
202 /**
203 * Handle mouse movements.
204 *
205 * @param mouse mouse motion event
206 */
207 @Override
208 public void onMouseMotion(final TMouseEvent mouse) {
209 this.mouse = mouse;
210
211 if (!mouseOnButton()) {
212 inButtonPress = false;
213 }
214 }
215
216 /**
217 * Handle keystrokes.
218 *
219 * @param keypress keystroke event
220 */
221 @Override
222 public void onKeypress(final TKeypressEvent keypress) {
223 if (keypress.equals(kbEnter)
224 || keypress.equals(kbSpace)
225 ) {
226 // Dispatch
a325f111 227 dispatch();
30d336cc
KL
228 return;
229 }
230
231 // Pass to parent for the things we don't care about.
232 super.onKeypress(keypress);
233 }
234
d36057df
KL
235 // ------------------------------------------------------------------------
236 // TWidget ----------------------------------------------------------------
237 // ------------------------------------------------------------------------
238
d8dc8aea
KL
239 /**
240 * Override TWidget's width: we can only set width at construction time.
241 *
242 * @param width new widget width (ignored)
243 */
244 @Override
245 public void setWidth(final int width) {
246 // Do nothing
247 }
248
249 /**
250 * Override TWidget's height: we can only set height at construction
251 * time.
252 *
253 * @param height new widget height (ignored)
254 */
255 @Override
256 public void setHeight(final int height) {
257 // Do nothing
258 }
259
d36057df
KL
260 /**
261 * Draw a button with a shadow.
262 */
263 @Override
264 public void draw() {
265 CellAttributes buttonColor;
266 CellAttributes menuMnemonicColor;
d36057df
KL
267
268 if (!isEnabled()) {
269 buttonColor = getTheme().getColor("tbutton.disabled");
270 menuMnemonicColor = getTheme().getColor("tbutton.disabled");
271 } else if (isAbsoluteActive()) {
272 buttonColor = getTheme().getColor("tbutton.active");
273 menuMnemonicColor = getTheme().getColor("tbutton.mnemonic.highlighted");
274 } else {
275 buttonColor = getTheme().getColor("tbutton.inactive");
276 menuMnemonicColor = getTheme().getColor("tbutton.mnemonic");
277 }
278
279 if (inButtonPress) {
a39c5665
KL
280 putCharXY(1, 0, ' ', buttonColor);
281 putStringXY(2, 0, mnemonic.getRawLabel(), buttonColor);
a69ed767 282 putCharXY(getWidth() - 1, 0, ' ', buttonColor);
d36057df 283 } else {
a69ed767
KL
284 putCharXY(0, 0, ' ', buttonColor);
285 putStringXY(1, 0, mnemonic.getRawLabel(), buttonColor);
286 putCharXY(getWidth() - 2, 0, ' ', buttonColor);
a39c5665 287
255967a8 288 if (shadowColor != null) {
a39c5665
KL
289 putCharXY(getWidth() - 1, 0,
290 GraphicsChars.CP437[0xDC], shadowColor);
291 hLineXY(1, 1, getWidth() - 1,
292 GraphicsChars.CP437[0xDF], shadowColor);
255967a8 293 }
d36057df 294 }
3fe82fa7 295 if (mnemonic.getScreenShortcutIdx() >= 0) {
d36057df 296 if (inButtonPress) {
3fe82fa7 297 putCharXY(2 + mnemonic.getScreenShortcutIdx(), 0,
d36057df
KL
298 mnemonic.getShortcut(), menuMnemonicColor);
299 } else {
3fe82fa7 300 putCharXY(1 + mnemonic.getScreenShortcutIdx(), 0,
d36057df
KL
301 mnemonic.getShortcut(), menuMnemonicColor);
302 }
d36057df
KL
303 }
304 }
305
306 // ------------------------------------------------------------------------
307 // TButton ----------------------------------------------------------------
308 // ------------------------------------------------------------------------
309
310 /**
311 * Get the mnemonic string for this button.
312 *
313 * @return mnemonic string
314 */
315 public MnemonicString getMnemonic() {
316 return mnemonic;
317 }
318
319 /**
320 * Act as though the button was pressed. This is useful for other UI
321 * elements to get the same action as if the user clicked the button.
322 */
323 public void dispatch() {
324 if (action != null) {
a524aa2e 325 action.DO(this);
d36057df
KL
326 inButtonPress = false;
327 }
328 }
329
e23ea538 330 /**
a39c5665
KL
331 * Set the background color used for the button "shadow". If null, no
332 * shadow will be drawn.
e23ea538 333 *
a39c5665 334 * @param color the new background color, or null for no shadow
e23ea538
KL
335 */
336 public void setShadowColor(final CellAttributes color) {
a39c5665
KL
337 if (color != null) {
338 shadowColor = new CellAttributes();
339 shadowColor.setTo(color);
340 shadowColor.setForeColor(Color.BLACK);
341 shadowColor.setBold(false);
342 } else {
343 shadowColor = null;
344 }
e23ea538
KL
345 }
346
30d336cc 347}