#40 twidget API updates
[fanfix.git] / src / jexer / TStatusBar.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 java.util.ArrayList;
32 import java.util.List;
33
34 import jexer.bits.CellAttributes;
35 import jexer.bits.GraphicsChars;
36 import jexer.event.TCommandEvent;
37 import jexer.event.TKeypressEvent;
38 import jexer.event.TMouseEvent;
39
40 /**
41 * TStatusBar implements a status line with clickable buttons.
42 */
43 public class TStatusBar extends TWidget {
44
45 // ------------------------------------------------------------------------
46 // Variables --------------------------------------------------------------
47 // ------------------------------------------------------------------------
48
49 /**
50 * Remember mouse state.
51 */
52 private TMouseEvent mouse;
53
54 /**
55 * The text to display on the right side of the shortcut keys.
56 */
57 private String text = null;
58
59 /**
60 * The shortcut keys.
61 */
62 private List<TStatusBarKey> keys = new ArrayList<TStatusBarKey>();
63
64 /**
65 * A single shortcut key.
66 */
67 private class TStatusBarKey {
68
69 /**
70 * The keypress for this action.
71 */
72 public TKeypress key;
73
74 /**
75 * The command to issue.
76 */
77 public TCommand cmd;
78
79 /**
80 * The label text.
81 */
82 public String label;
83
84 /**
85 * If true, the mouse is on this key.
86 */
87 public boolean selected;
88
89 /**
90 * The left edge coordinate to draw this key with.
91 */
92 public int x = 0;
93
94 /**
95 * The width of this key on the screen.
96 *
97 * @return the number of columns this takes when drawn
98 */
99 public int width() {
100 return this.label.length() + this.key.toString().length() + 3;
101 }
102
103 /**
104 * Add a key to this status bar.
105 *
106 * @param key the key to trigger on
107 * @param cmd the command event to issue when key is pressed or this
108 * item is clicked
109 * @param label the label for this action
110 */
111 public TStatusBarKey(final TKeypress key, final TCommand cmd,
112 final String label) {
113
114 this.key = key;
115 this.cmd = cmd;
116 this.label = label;
117 }
118
119 }
120
121 // ------------------------------------------------------------------------
122 // Constructors -----------------------------------------------------------
123 // ------------------------------------------------------------------------
124
125 /**
126 * Public constructor.
127 *
128 * @param window the window associated with this status bar
129 * @param text text for the bar on the bottom row
130 */
131 public TStatusBar(final TWindow window, final String text) {
132
133 // TStatusBar is a parentless widget, because TApplication handles
134 // its drawing and event routing directly.
135 super(null, false, 0, 0, text.length(), 1);
136
137 this.text = text;
138 setWindow(window);
139 }
140
141 /**
142 * Public constructor.
143 *
144 * @param window the window associated with this status bar
145 */
146 public TStatusBar(final TWindow window) {
147 this(window, "");
148 }
149
150 // ------------------------------------------------------------------------
151 // Event handlers ---------------------------------------------------------
152 // ------------------------------------------------------------------------
153
154 /**
155 * Handle keypresses.
156 *
157 * @param keypress keystroke event
158 * @return true if this keypress was consumed
159 */
160 public boolean statusBarKeypress(final TKeypressEvent keypress) {
161 for (TStatusBarKey key: keys) {
162 if (keypress.equals(key.key)) {
163 getApplication().postMenuEvent(new TCommandEvent(key.cmd));
164 return true;
165 }
166 }
167 return false;
168 }
169
170 /**
171 * Returns true if the mouse is currently on the button.
172 *
173 * @param statusBarKey the status bar item
174 * @return if true the mouse is currently on the button
175 */
176 private boolean mouseOnShortcut(final TStatusBarKey statusBarKey) {
177 if ((mouse != null)
178 && (mouse.getAbsoluteY() == getApplication().getDesktopBottom())
179 && (mouse.getAbsoluteX() >= statusBarKey.x)
180 && (mouse.getAbsoluteX() < statusBarKey.x + statusBarKey.width())
181 ) {
182 return true;
183 }
184 return false;
185 }
186
187 /**
188 * Handle mouse button presses.
189 *
190 * @param mouse mouse button event
191 * @return true if this mouse event was consumed
192 */
193 public boolean statusBarMouseDown(final TMouseEvent mouse) {
194 this.mouse = mouse;
195
196 for (TStatusBarKey key: keys) {
197 if ((mouseOnShortcut(key)) && (mouse.isMouse1())) {
198 key.selected = true;
199 return true;
200 }
201 }
202 return false;
203 }
204
205 /**
206 * Handle mouse button releases.
207 *
208 * @param mouse mouse button release event
209 * @return true if this mouse event was consumed
210 */
211 public boolean statusBarMouseUp(final TMouseEvent mouse) {
212 this.mouse = mouse;
213
214 for (TStatusBarKey key: keys) {
215 if (key.selected && mouse.isMouse1()) {
216 key.selected = false;
217
218 // Dispatch the event
219 getApplication().postMenuEvent(new TCommandEvent(key.cmd));
220 return true;
221 }
222 }
223 return false;
224 }
225
226 /**
227 * Handle mouse movements.
228 *
229 * @param mouse mouse motion event
230 */
231 public void statusBarMouseMotion(final TMouseEvent mouse) {
232 this.mouse = mouse;
233
234 for (TStatusBarKey key: keys) {
235 if (!mouseOnShortcut(key)) {
236 key.selected = false;
237 }
238 }
239 }
240
241 // ------------------------------------------------------------------------
242 // TWidget ----------------------------------------------------------------
243 // ------------------------------------------------------------------------
244
245 /**
246 * Draw the bar.
247 */
248 @Override
249 public void draw() {
250 CellAttributes barColor = new CellAttributes();
251 barColor.setTo(getTheme().getColor("tstatusbar.text"));
252 CellAttributes keyColor = new CellAttributes();
253 keyColor.setTo(getTheme().getColor("tstatusbar.button"));
254 CellAttributes selectedColor = new CellAttributes();
255 selectedColor.setTo(getTheme().getColor("tstatusbar.selected"));
256
257 // Status bar is weird. Its draw() method is called directly by
258 // TApplication after everything is drawn, and after
259 // Screen.resetClipping(). So at this point we are drawing in
260 // absolute coordinates, not relative to our TWindow.
261 int row = getScreen().getHeight() - 1;
262 int width = getScreen().getWidth();
263
264 hLineXY(0, row, width, ' ', barColor);
265
266 int col = 0;
267 for (TStatusBarKey key: keys) {
268 String keyStr = key.key.toString();
269 if (key.selected) {
270 putCharXY(col++, row, ' ', selectedColor);
271 putStringXY(col, row, keyStr, selectedColor);
272 col += keyStr.length();
273 putCharXY(col++, row, ' ', selectedColor);
274 putStringXY(col, row, key.label, selectedColor);
275 col += key.label.length();
276 putCharXY(col++, row, ' ', selectedColor);
277 } else {
278 putCharXY(col++, row, ' ', barColor);
279 putStringXY(col, row, keyStr, keyColor);
280 col += keyStr.length() + 1;
281 putStringXY(col, row, key.label, barColor);
282 col += key.label.length();
283 putCharXY(col++, row, ' ', barColor);
284 }
285 }
286 if (text.length() > 0) {
287 if (keys.size() > 0) {
288 putCharXY(col++, row, GraphicsChars.VERTICAL_BAR, barColor);
289 }
290 putCharXY(col++, row, ' ', barColor);
291 putStringXY(col, row, text, barColor);
292 }
293 }
294
295 // ------------------------------------------------------------------------
296 // TStatusBar -------------------------------------------------------------
297 // ------------------------------------------------------------------------
298
299 /**
300 * Add a key to this status bar.
301 *
302 * @param key the key to trigger on
303 * @param cmd the command event to issue when key is pressed or this item
304 * is clicked
305 * @param label the label for this action
306 */
307 public void addShortcutKeypress(final TKeypress key, final TCommand cmd,
308 final String label) {
309
310 TStatusBarKey newKey = new TStatusBarKey(key, cmd, label);
311 if (keys.size() > 0) {
312 TStatusBarKey oldKey = keys.get(keys.size() - 1);
313 newKey.x = oldKey.x + oldKey.width();
314 }
315 keys.add(newKey);
316 }
317
318 /**
319 * Set the text to display on the right side of the shortcut keys.
320 *
321 * @param text the new text
322 */
323 public void setText(final String text) {
324 this.text = text;
325 }
326
327 }