tbutton: allow empty shadow colour
[fanfix.git] / src / jexer / TButton.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer;
30
31 import jexer.bits.CellAttributes;
32 import jexer.bits.Color;
33 import jexer.bits.GraphicsChars;
34 import jexer.bits.MnemonicString;
35 import jexer.event.TKeypressEvent;
36 import jexer.event.TMouseEvent;
37 import static jexer.TKeypress.*;
38
39 /**
40 * TButton implements a simple button. To make the button do something, pass
41 * a TAction class to its constructor.
42 *
43 * @see TAction#DO()
44 */
45 public class TButton extends TWidget {
46
47 // ------------------------------------------------------------------------
48 // Variables --------------------------------------------------------------
49 // ------------------------------------------------------------------------
50
51 /**
52 * The shortcut and button text.
53 */
54 private MnemonicString mnemonic;
55
56 /**
57 * Remember mouse state.
58 */
59 private TMouseEvent mouse;
60
61 /**
62 * True when the button is being pressed and held down.
63 */
64 private boolean inButtonPress = false;
65
66 /**
67 * The action to perform when the button is clicked.
68 */
69 private TAction action;
70
71 /**
72 * The background color used for the button "shadow", or NULL for "no shadow".
73 */
74 private CellAttributes shadowColor;
75
76 // ------------------------------------------------------------------------
77 // Constructors -----------------------------------------------------------
78 // ------------------------------------------------------------------------
79
80 /**
81 * Private constructor.
82 *
83 * @param parent parent widget
84 * @param text label on the button
85 * @param x column relative to parent
86 * @param y row relative to parent
87 */
88 private TButton(final TWidget parent, final String text,
89 final int x, final int y) {
90
91 // Set parent and window
92 super(parent);
93
94 mnemonic = new MnemonicString(text);
95
96 setX(x);
97 setY(y);
98 setHeight(2);
99 setWidth(mnemonic.getRawLabel().length() + 3);
100
101 shadowColor = new CellAttributes();
102 shadowColor.setTo(getWindow().getBackground());
103 shadowColor.setForeColor(Color.BLACK);
104 shadowColor.setBold(false);
105 }
106
107 /**
108 * Public constructor.
109 *
110 * @param parent parent widget
111 * @param text label on the button
112 * @param x column relative to parent
113 * @param y row relative to parent
114 * @param action to call when button is pressed
115 */
116 public TButton(final TWidget parent, final String text,
117 final int x, final int y, final TAction action) {
118
119 this(parent, text, x, y);
120 this.action = action;
121 }
122
123 // ------------------------------------------------------------------------
124 // Event handlers ---------------------------------------------------------
125 // ------------------------------------------------------------------------
126
127 /**
128 * Returns true if the mouse is currently on the button.
129 *
130 * @return if true the mouse is currently on the button
131 */
132 private boolean mouseOnButton() {
133 int rightEdge = getWidth() - 1;
134 if (inButtonPress) {
135 rightEdge++;
136 }
137 if ((mouse != null)
138 && (mouse.getY() == 0)
139 && (mouse.getX() >= 0)
140 && (mouse.getX() < rightEdge)
141 ) {
142 return true;
143 }
144 return false;
145 }
146
147 /**
148 * Handle mouse button presses.
149 *
150 * @param mouse mouse button event
151 */
152 @Override
153 public void onMouseDown(final TMouseEvent mouse) {
154 this.mouse = mouse;
155
156 if ((mouseOnButton()) && (mouse.isMouse1())) {
157 // Begin button press
158 inButtonPress = true;
159 }
160 }
161
162 /**
163 * Handle mouse button releases.
164 *
165 * @param mouse mouse button release event
166 */
167 @Override
168 public void onMouseUp(final TMouseEvent mouse) {
169 this.mouse = mouse;
170
171 if (inButtonPress && mouse.isMouse1()) {
172 // Dispatch the event
173 dispatch();
174 }
175
176 }
177
178 /**
179 * Handle mouse movements.
180 *
181 * @param mouse mouse motion event
182 */
183 @Override
184 public void onMouseMotion(final TMouseEvent mouse) {
185 this.mouse = mouse;
186
187 if (!mouseOnButton()) {
188 inButtonPress = false;
189 }
190 }
191
192 /**
193 * Handle keystrokes.
194 *
195 * @param keypress keystroke event
196 */
197 @Override
198 public void onKeypress(final TKeypressEvent keypress) {
199 if (keypress.equals(kbEnter)
200 || keypress.equals(kbSpace)
201 ) {
202 // Dispatch
203 dispatch();
204 return;
205 }
206
207 // Pass to parent for the things we don't care about.
208 super.onKeypress(keypress);
209 }
210
211 // ------------------------------------------------------------------------
212 // TWidget ----------------------------------------------------------------
213 // ------------------------------------------------------------------------
214
215 /**
216 * Draw a button with a shadow.
217 */
218 @Override
219 public void draw() {
220 CellAttributes buttonColor;
221 CellAttributes menuMnemonicColor;
222
223 if (!isEnabled()) {
224 buttonColor = getTheme().getColor("tbutton.disabled");
225 menuMnemonicColor = getTheme().getColor("tbutton.disabled");
226 } else if (isAbsoluteActive()) {
227 buttonColor = getTheme().getColor("tbutton.active");
228 menuMnemonicColor = getTheme().getColor("tbutton.mnemonic.highlighted");
229 } else {
230 buttonColor = getTheme().getColor("tbutton.inactive");
231 menuMnemonicColor = getTheme().getColor("tbutton.mnemonic");
232 }
233
234 if (inButtonPress) {
235 putCharXY(1, 0, ' ', buttonColor);
236 putStringXY(2, 0, mnemonic.getRawLabel(), buttonColor);
237 putCharXY(getWidth() - 1, 0, ' ', buttonColor);
238 } else {
239 putCharXY(0, 0, ' ', buttonColor);
240 putStringXY(1, 0, mnemonic.getRawLabel(), buttonColor);
241 putCharXY(getWidth() - 2, 0, ' ', buttonColor);
242
243 if (shadowColor != null) {
244 putCharXY(getWidth() - 1, 0,
245 GraphicsChars.CP437[0xDC], shadowColor);
246 hLineXY(1, 1, getWidth() - 1,
247 GraphicsChars.CP437[0xDF], shadowColor);
248 }
249 }
250 if (mnemonic.getShortcutIdx() >= 0) {
251 if (inButtonPress) {
252 putCharXY(2 + mnemonic.getShortcutIdx(), 0,
253 mnemonic.getShortcut(), menuMnemonicColor);
254 } else {
255 putCharXY(1 + mnemonic.getShortcutIdx(), 0,
256 mnemonic.getShortcut(), menuMnemonicColor);
257 }
258 }
259 }
260
261 // ------------------------------------------------------------------------
262 // TButton ----------------------------------------------------------------
263 // ------------------------------------------------------------------------
264
265 /**
266 * Get the mnemonic string for this button.
267 *
268 * @return mnemonic string
269 */
270 public MnemonicString getMnemonic() {
271 return mnemonic;
272 }
273
274 /**
275 * Act as though the button was pressed. This is useful for other UI
276 * elements to get the same action as if the user clicked the button.
277 */
278 public void dispatch() {
279 if (action != null) {
280 action.DO();
281 inButtonPress = false;
282 }
283 }
284
285 /**
286 * Set the background color used for the button "shadow".
287 * <p>
288 * Can be NULL for "no shadow".
289 *
290 * @param color the new background color, or NULL if none
291 */
292 public void setShadowColor(final CellAttributes color) {
293 if (color != null) {
294 shadowColor = new CellAttributes();
295 shadowColor.setTo(color);
296 shadowColor.setForeColor(Color.BLACK);
297 shadowColor.setBold(false);
298 } else {
299 shadowColor = null;
300 }
301 }
302
303 }