misc cleanup
[fanfix.git] / src / jexer / TWidget.java
CommitLineData
48e27807
KL
1/**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31package jexer;
32
33import java.util.List;
34import java.util.LinkedList;
35
928811d8 36import jexer.bits.ColorTheme;
48e27807
KL
37import jexer.event.TCommandEvent;
38import jexer.event.TInputEvent;
39import jexer.event.TKeypressEvent;
40import jexer.event.TMenuEvent;
41import jexer.event.TMouseEvent;
42import jexer.event.TResizeEvent;
43import jexer.io.Screen;
928811d8 44import jexer.menu.TMenu;
48e27807
KL
45import static jexer.TKeypress.*;
46
47/**
48 * TWidget is the base class of all objects that can be drawn on screen or
49 * handle user input events.
50 */
2b9c27db 51public abstract class TWidget implements Comparable<TWidget> {
48e27807
KL
52
53 /**
54 * Every widget has a parent widget that it may be "contained" in. For
55 * example, a TWindow might contain several TTextFields, or a TComboBox
56 * may contain a TScrollBar.
57 */
fca67db0
KL
58 private TWidget parent = null;
59
928811d8
KL
60 /**
61 * Get parent widget.
62 *
63 * @return parent widget
64 */
65 public final TWidget getParent() {
66 return parent;
67 }
68
fca67db0
KL
69 /**
70 * Backdoor access for TWindow's constructor. ONLY TWindow USES THIS.
71 *
72 * @param window the top-level window
73 * @param x column relative to parent
74 * @param y row relative to parent
75 * @param width width of window
76 * @param height height of window
77 */
78 protected final void setupForTWindow(final TWindow window,
79 final int x, final int y, final int width, final int height) {
80
81 this.parent = window;
82 this.window = window;
83 this.x = x;
84 this.y = y;
85 this.width = width;
86 this.height = height;
87 }
48e27807 88
928811d8
KL
89 /**
90 * Request full repaint on next screen refresh.
91 */
92 protected final void setRepaint() {
93 window.getApplication().setRepaint();
94 }
95
96 /**
97 * Get this TWidget's parent TApplication.
98 *
99 * @return the parent TApplication
100 */
101 public TApplication getApplication() {
102 return window.getApplication();
103 }
104
105 /**
106 * Get the Screen.
107 *
108 * @return the Screen
109 */
110 public Screen getScreen() {
111 return window.getScreen();
112 }
113
48e27807
KL
114 /**
115 * Child widgets that this widget contains.
116 */
117 private List<TWidget> children;
118
928811d8
KL
119 /**
120 * Get the list of child widgets that this widget contains.
121 *
122 * @return the list of child widgets
123 */
124 public List<TWidget> getChildren() {
125 return children;
126 }
127
48e27807
KL
128 /**
129 * The currently active child widget that will receive keypress events.
130 */
131 private TWidget activeChild = null;
132
133 /**
134 * If true, this widget will receive events.
135 */
fca67db0
KL
136 private boolean active = false;
137
138 /**
139 * Get active flag.
140 *
141 * @return if true, this widget will receive events
142 */
143 public final boolean getActive() {
144 return active;
145 }
146
147 /**
148 * Set active flag.
149 *
150 * @param active if true, this widget will receive events
151 */
928811d8 152 public final void setActive(final boolean active) {
fca67db0
KL
153 this.active = active;
154 }
48e27807
KL
155
156 /**
157 * The window that this widget draws to.
158 */
fca67db0 159 private TWindow window = null;
48e27807 160
30d336cc
KL
161 /**
162 * Get the window this widget is on.
163 *
164 * @return the window
165 */
166 public final TWindow getWindow() {
167 return window;
168 }
169
48e27807
KL
170 /**
171 * Absolute X position of the top-left corner.
172 */
fca67db0
KL
173 private int x = 0;
174
175 /**
176 * Get X position.
177 *
178 * @return absolute X position of the top-left corner
179 */
180 public final int getX() {
181 return x;
182 }
183
184 /**
185 * Set X position.
186 *
187 * @param x absolute X position of the top-left corner
188 */
189 public final void setX(final int x) {
190 this.x = x;
191 }
48e27807
KL
192
193 /**
194 * Absolute Y position of the top-left corner.
195 */
fca67db0
KL
196 private int y = 0;
197
198 /**
199 * Get Y position.
200 *
201 * @return absolute Y position of the top-left corner
202 */
203 public final int getY() {
204 return y;
205 }
206
207 /**
208 * Set Y position.
209 *
210 * @param y absolute Y position of the top-left corner
211 */
212 public final void setY(final int y) {
213 this.y = y;
214 }
48e27807
KL
215
216 /**
217 * Width.
218 */
fca67db0
KL
219 private int width = 0;
220
221 /**
222 * Get the width.
223 *
224 * @return widget width
225 */
226 public final int getWidth() {
227 return this.width;
228 }
229
230 /**
231 * Change the width.
232 *
233 * @param width new widget width
234 */
235 public final void setWidth(final int width) {
236 this.width = width;
237 }
48e27807
KL
238
239 /**
240 * Height.
241 */
fca67db0
KL
242 private int height = 0;
243
244 /**
245 * Get the height.
246 *
247 * @return widget height
248 */
249 public final int getHeight() {
250 return this.height;
251 }
252
253 /**
254 * Change the height.
255 *
256 * @param height new widget height
257 */
258 public final void setHeight(final int height) {
259 this.height = height;
260 }
48e27807
KL
261
262 /**
263 * My tab order inside a window or containing widget.
264 */
265 private int tabOrder = 0;
266
267 /**
268 * If true, this widget can be tabbed to or receive events.
269 */
270 private boolean enabled = true;
271
272 /**
273 * Get enabled flag.
274 *
275 * @return if true, this widget can be tabbed to or receive events
276 */
277 public final boolean getEnabled() {
278 return enabled;
279 }
280
281 /**
282 * Set enabled flag.
283 *
284 * @param enabled if true, this widget can be tabbed to or receive events
285 */
286 public final void setEnabled(final boolean enabled) {
287 this.enabled = enabled;
fca67db0 288 if (!enabled) {
48e27807
KL
289 active = false;
290 // See if there are any active siblings to switch to
291 boolean foundSibling = false;
fca67db0
KL
292 if (parent != null) {
293 for (TWidget w: parent.children) {
294 if ((w.enabled)
295 && !(this instanceof THScroller)
296 && !(this instanceof TVScroller)
48e27807
KL
297 ) {
298 parent.activate(w);
299 foundSibling = true;
300 break;
301 }
302 }
303 if (!foundSibling) {
304 parent.activeChild = null;
305 }
306 }
307 }
48e27807
KL
308 }
309
310 /**
311 * If true, this widget has a cursor.
312 */
313 private boolean hasCursor = false;
314
7272e49f
KL
315 /**
316 * Set visible cursor flag.
317 *
318 * @param hasCursor if true, this widget has a cursor
319 */
320 public final void setHasCursor(final boolean hasCursor) {
321 this.hasCursor = hasCursor;
322 }
323
a06459bd
KL
324 /**
325 * See if this widget has a visible cursor.
326 *
327 * @return if true, this widget has a visible cursor
328 */
329 public final boolean visibleCursor() {
330 return hasCursor;
331 }
332
48e27807
KL
333 /**
334 * Cursor column position in relative coordinates.
335 */
336 private int cursorX = 0;
337
7272e49f
KL
338 /**
339 * Get cursor X value.
340 *
341 * @return cursor column position in relative coordinates
342 */
343 public final int getCursorX() {
344 return cursorX;
345 }
346
347 /**
348 * Set cursor X value.
349 *
350 * @param cursorX column position in relative coordinates
351 */
352 public final void setCursorX(final int cursorX) {
353 this.cursorX = cursorX;
354 }
355
48e27807
KL
356 /**
357 * Cursor row position in relative coordinates.
358 */
359 private int cursorY = 0;
360
7272e49f
KL
361 /**
362 * Get cursor Y value.
363 *
364 * @return cursor row position in relative coordinates
365 */
366 public final int getCursorY() {
367 return cursorY;
368 }
369
370 /**
371 * Set cursor Y value.
372 *
373 * @param cursorY row position in relative coordinates
374 */
375 public final void setCursorY(final int cursorY) {
376 this.cursorY = cursorY;
377 }
378
48e27807 379 /**
2b9c27db 380 * Comparison operator sorts on tabOrder for TWidgets and z for TWindows.
48e27807 381 *
2b9c27db
KL
382 * @param that another TWidget or TWindow instance
383 * @return difference between this.tabOrder and that.tabOrder, or
384 * difference between this.z and that.z
385 */
386 @Override
387 public final int compareTo(final TWidget that) {
388 if ((this instanceof TWindow)
7272e49f 389 && (that instanceof TWindow)
2b9c27db
KL
390 ) {
391 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
392 }
48e27807
KL
393 return (this.tabOrder - that.tabOrder);
394 }
395
396 /**
397 * See if this widget should render with the active color.
398 *
399 * @return true if this widget is active and all of its parents are
400 * active.
401 */
402 public final boolean getAbsoluteActive() {
403 if (parent == this) {
404 return active;
405 }
406 return (active && parent.getAbsoluteActive());
407 }
408
409 /**
410 * Returns the cursor X position.
411 *
412 * @return absolute screen column number for the cursor's X position
413 */
414 public final int getCursorAbsoluteX() {
415 assert (hasCursor);
416 return getAbsoluteX() + cursorX;
417 }
418
419 /**
420 * Returns the cursor Y position.
421 *
422 * @return absolute screen row number for the cursor's Y position
423 */
424 public final int getCursorAbsoluteY() {
425 assert (hasCursor);
426 return getAbsoluteY() + cursorY;
427 }
428
429 /**
430 * Compute my absolute X position as the sum of my X plus all my parent's
431 * X's.
432 *
433 * @return absolute screen column number for my X position
434 */
435 public final int getAbsoluteX() {
436 assert (parent != null);
437 if (parent == this) {
438 return x;
439 }
440 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
441 // Widgets on a TWindow have (0,0) as their top-left, but this is
442 // actually the TWindow's (1,1).
443 return parent.getAbsoluteX() + x + 1;
444 }
445 return parent.getAbsoluteX() + x;
446 }
447
448 /**
449 * Compute my absolute Y position as the sum of my Y plus all my parent's
450 * Y's.
451 *
452 * @return absolute screen row number for my Y position
453 */
454 public final int getAbsoluteY() {
455 assert (parent != null);
456 if (parent == this) {
457 return y;
458 }
459 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
460 // Widgets on a TWindow have (0,0) as their top-left, but this is
461 // actually the TWindow's (1,1).
462 return parent.getAbsoluteY() + y + 1;
463 }
464 return parent.getAbsoluteY() + y;
465 }
466
928811d8
KL
467 /**
468 * Get the global color theme.
469 *
470 * @return the ColorTheme
471 */
472 public final ColorTheme getTheme() {
473 return window.getApplication().getTheme();
474 }
475
48e27807
KL
476 /**
477 * Draw my specific widget. When called, the screen rectangle I draw
478 * into is already setup (offset and clipping).
479 */
480 public void draw() {
481 // Default widget draws nothing.
482 }
483
484 /**
485 * Called by parent to render to TWindow.
486 */
487 public final void drawChildren() {
488 // Set my clipping rectangle
489 assert (window != null);
490 assert (window.getScreen() != null);
491 Screen screen = window.getScreen();
492
493 screen.setClipRight(width);
494 screen.setClipBottom(height);
495
496 int absoluteRightEdge = window.getAbsoluteX() + screen.getWidth();
497 int absoluteBottomEdge = window.getAbsoluteY() + screen.getHeight();
498 if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
499 absoluteRightEdge -= 1;
500 }
501 if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
502 absoluteBottomEdge -= 1;
503 }
504 int myRightEdge = getAbsoluteX() + width;
505 int myBottomEdge = getAbsoluteY() + height;
506 if (getAbsoluteX() > absoluteRightEdge) {
507 // I am offscreen
508 screen.setClipRight(0);
509 } else if (myRightEdge > absoluteRightEdge) {
510 screen.setClipRight(screen.getClipRight()
511 - myRightEdge - absoluteRightEdge);
512 }
513 if (getAbsoluteY() > absoluteBottomEdge) {
514 // I am offscreen
515 screen.setClipBottom(0);
516 } else if (myBottomEdge > absoluteBottomEdge) {
517 screen.setClipBottom(screen.getClipBottom()
518 - myBottomEdge - absoluteBottomEdge);
519 }
520
521 // Set my offset
522 screen.setOffsetX(getAbsoluteX());
523 screen.setOffsetY(getAbsoluteY());
524
525 // Draw me
526 draw();
527
528 // Continue down the chain
529 for (TWidget widget: children) {
530 widget.drawChildren();
531 }
532 }
533
534 /**
fca67db0 535 * Default constructor for subclasses.
48e27807
KL
536 */
537 protected TWidget() {
538 children = new LinkedList<TWidget>();
539 }
540
541 /**
542 * Protected constructor.
543 *
544 * @param parent parent widget
545 */
546 protected TWidget(final TWidget parent) {
30d336cc
KL
547 this(parent, true);
548 }
549
a83fea2b
KL
550 /**
551 * Protected constructor.
552 *
553 * @param parent parent widget
554 * @param x column relative to parent
555 * @param y row relative to parent
556 * @param width width of widget
557 * @param height height of widget
558 */
559 protected TWidget(final TWidget parent, final int x, final int y,
560 final int width, final int height) {
561
562 this(parent, true, x, y, width, height);
563 }
564
30d336cc
KL
565 /**
566 * Protected constructor used by subclasses that are disabled by default.
567 *
568 * @param parent parent widget
569 * @param enabled if true assume enabled
570 */
571 protected TWidget(final TWidget parent, final boolean enabled) {
572 this.enabled = enabled;
48e27807
KL
573 this.parent = parent;
574 this.window = parent.window;
fca67db0 575 children = new LinkedList<TWidget>();
48e27807
KL
576 parent.addChild(this);
577 }
578
a83fea2b
KL
579 /**
580 * Protected constructor used by subclasses that are disabled by default.
581 *
582 * @param parent parent widget
583 * @param enabled if true assume enabled
584 * @param x column relative to parent
585 * @param y row relative to parent
586 * @param width width of widget
587 * @param height height of widget
588 */
589 protected TWidget(final TWidget parent, final boolean enabled,
590 final int x, final int y, final int width, final int height) {
591
592 this.enabled = enabled;
593 this.parent = parent;
594 this.window = parent.window;
595 children = new LinkedList<TWidget>();
596 parent.addChild(this);
597
598 this.x = x;
599 this.y = y;
600 this.width = width;
601 this.height = height;
602 }
603
48e27807
KL
604 /**
605 * Add a child widget to my list of children. We set its tabOrder to 0
606 * and increment the tabOrder of all other children.
607 *
608 * @param child TWidget to add
609 */
610 private void addChild(final TWidget child) {
611 children.add(child);
612
613 if ((child.enabled)
614 && !(child instanceof THScroller)
615 && !(child instanceof TVScroller)
616 ) {
617 for (TWidget widget: children) {
618 widget.active = false;
619 }
620 child.active = true;
621 activeChild = child;
622 }
623 for (int i = 0; i < children.size(); i++) {
624 children.get(i).tabOrder = i;
625 }
626 }
627
628 /**
629 * Switch the active child.
630 *
631 * @param child TWidget to activate
632 */
633 public final void activate(final TWidget child) {
634 assert (child.enabled);
635 if ((child instanceof THScroller)
636 || (child instanceof TVScroller)
637 ) {
638 return;
639 }
640
641 if (child != activeChild) {
642 if (activeChild != null) {
643 activeChild.active = false;
644 }
645 child.active = true;
646 activeChild = child;
647 }
648 }
649
650 /**
651 * Switch the active child.
652 *
653 * @param tabOrder tabOrder of the child to activate. If that child
654 * isn't enabled, then the next enabled child will be activated.
655 */
656 public final void activate(final int tabOrder) {
657 if (activeChild == null) {
658 return;
659 }
660 TWidget child = null;
661 for (TWidget widget: children) {
662 if ((widget.enabled)
663 && !(widget instanceof THScroller)
664 && !(widget instanceof TVScroller)
665 && (widget.tabOrder >= tabOrder)
666 ) {
667 child = widget;
668 break;
669 }
670 }
671 if ((child != null) && (child != activeChild)) {
672 activeChild.active = false;
673 assert (child.enabled);
674 child.active = true;
675 activeChild = child;
676 }
677 }
678
679 /**
680 * Switch the active widget with the next in the tab order.
681 *
682 * @param forward if true, then switch to the next enabled widget in the
683 * list, otherwise switch to the previous enabled widget in the list
684 */
685 public final void switchWidget(final boolean forward) {
686
687 // Only switch if there are multiple enabled widgets
688 if ((children.size() < 2) || (activeChild == null)) {
689 return;
690 }
691
692 int tabOrder = activeChild.tabOrder;
693 do {
694 if (forward) {
695 tabOrder++;
696 } else {
697 tabOrder--;
698 }
699 if (tabOrder < 0) {
700
701 // If at the end, pass the switch to my parent.
702 if ((!forward) && (parent != this)) {
703 parent.switchWidget(forward);
704 return;
705 }
706
707 tabOrder = children.size() - 1;
708 } else if (tabOrder == children.size()) {
709 // If at the end, pass the switch to my parent.
710 if ((forward) && (parent != this)) {
711 parent.switchWidget(forward);
712 return;
713 }
714
715 tabOrder = 0;
716 }
717 if (activeChild.tabOrder == tabOrder) {
718 // We wrapped around
719 break;
720 }
721 } while ((!children.get(tabOrder).enabled)
722 && !(children.get(tabOrder) instanceof THScroller)
723 && !(children.get(tabOrder) instanceof TVScroller));
724
725 assert (children.get(tabOrder).enabled);
726
727 activeChild.active = false;
728 children.get(tabOrder).active = true;
729 activeChild = children.get(tabOrder);
730
731 // Refresh
732 window.getApplication().setRepaint();
733 }
734
735 /**
736 * Returns my active widget.
737 *
738 * @return widget that is active, or this if no children
739 */
928811d8 740 public TWidget getActiveChild() {
48e27807
KL
741 if ((this instanceof THScroller)
742 || (this instanceof TVScroller)
743 ) {
744 return parent;
745 }
746
747 for (TWidget widget: children) {
748 if (widget.active) {
749 return widget.getActiveChild();
750 }
751 }
752 // No active children, return me
753 return this;
754 }
755
756 /**
757 * Method that subclasses can override to handle keystrokes.
758 *
759 * @param keypress keystroke event
760 */
761 public void onKeypress(final TKeypressEvent keypress) {
762
763 if ((children.size() == 0)
fca67db0 764 // TODO
48e27807 765 // || (cast(TTreeView)this)
a83fea2b 766 || (this instanceof TText)
48e27807
KL
767 ) {
768
769 // Defaults:
770 // tab / shift-tab - switch to next/previous widget
771 // right-arrow or down-arrow: same as tab
772 // left-arrow or up-arrow: same as shift-tab
773 if ((keypress.equals(kbTab))
774 || (keypress.equals(kbRight))
775 || (keypress.equals(kbDown))
776 ) {
777 parent.switchWidget(true);
778 return;
779 } else if ((keypress.equals(kbShiftTab))
780 || (keypress.equals(kbBackTab))
781 || (keypress.equals(kbLeft))
782 || (keypress.equals(kbUp))
783 ) {
784 parent.switchWidget(false);
785 return;
786 }
787 }
788
789 // If I have any buttons on me AND this is an Alt-key that matches
790 // its mnemonic, send it an Enter keystroke
791 for (TWidget widget: children) {
792 /*
793 TODO
794
795 if (TButton button = cast(TButton)w) {
796 if (button.enabled &&
797 !keypress.key.isKey &&
798 keypress.key.alt &&
799 !keypress.key.ctrl &&
800 (toLowercase(button.mnemonic.shortcut) == toLowercase(keypress.key.ch))) {
801
802 w.handleEvent(new TKeypressEvent(kbEnter));
803 return;
804 }
805 }
806 */
807 }
808
809 // Dispatch the keypress to an active widget
810 for (TWidget widget: children) {
811 if (widget.active) {
812 window.getApplication().setRepaint();
813 widget.handleEvent(keypress);
814 return;
815 }
816 }
817 }
818
819 /**
820 * Method that subclasses can override to handle mouse button presses.
821 *
822 * @param mouse mouse button event
823 */
824 public void onMouseDown(final TMouseEvent mouse) {
825 // Default: do nothing, pass to children instead
826 for (TWidget widget: children) {
827 if (widget.mouseWouldHit(mouse)) {
828 // Dispatch to this child, also activate it
829 activate(widget);
830
831 // Set x and y relative to the child's coordinates
832 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
833 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
834 widget.handleEvent(mouse);
835 return;
836 }
837 }
838 }
839
840 /**
841 * Method that subclasses can override to handle mouse button releases.
842 *
843 * @param mouse mouse button event
844 */
845 public void onMouseUp(final TMouseEvent mouse) {
846 // Default: do nothing, pass to children instead
847 for (TWidget widget: children) {
848 if (widget.mouseWouldHit(mouse)) {
849 // Dispatch to this child, also activate it
850 activate(widget);
851
852 // Set x and y relative to the child's coordinates
853 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
854 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
855 widget.handleEvent(mouse);
856 return;
857 }
858 }
859 }
860
861 /**
862 * Method that subclasses can override to handle mouse movements.
863 *
864 * @param mouse mouse motion event
865 */
866 public void onMouseMotion(final TMouseEvent mouse) {
867 // Default: do nothing, pass it on to ALL of my children. This way
868 // the children can see the mouse "leaving" their area.
869 for (TWidget widget: children) {
870 // Set x and y relative to the child's coordinates
871 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
872 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
873 widget.handleEvent(mouse);
874 }
875 }
876
877 /**
878 * Method that subclasses can override to handle window/screen resize
879 * events.
880 *
881 * @param resize resize event
882 */
883 public void onResize(final TResizeEvent resize) {
884 // Default: do nothing, pass to children instead
885 for (TWidget widget: children) {
886 widget.onResize(resize);
887 }
888 }
889
890 /**
891 * Method that subclasses can override to handle posted command events.
892 *
893 * @param command command event
894 */
895 public void onCommand(final TCommandEvent command) {
896 // Default: do nothing, pass to children instead
897 for (TWidget widget: children) {
898 widget.onCommand(command);
899 }
900 }
901
902 /**
903 * Method that subclasses can override to handle menu or posted menu
904 * events.
905 *
906 * @param menu menu event
907 */
908 public void onMenu(final TMenuEvent menu) {
909 // Default: do nothing, pass to children instead
910 for (TWidget widget: children) {
911 widget.onMenu(menu);
912 }
913 }
914
915 /**
916 * Method that subclasses can override to do processing when the UI is
917 * idle.
918 */
919 public void onIdle() {
920 // Default: do nothing, pass to children instead
921 for (TWidget widget: children) {
922 widget.onIdle();
923 }
924 }
925
926 /**
927 * Consume event. Subclasses that want to intercept all events in one go
928 * can override this method.
929 *
930 * @param event keyboard, mouse, resize, command, or menu event
931 */
932 public void handleEvent(final TInputEvent event) {
933 // System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
934 // event);
935
936 if (!enabled) {
937 // Discard event
938 // System.err.println(" -- discard --");
939 return;
940 }
941
942 if (event instanceof TKeypressEvent) {
943 onKeypress((TKeypressEvent) event);
944 } else if (event instanceof TMouseEvent) {
945
946 TMouseEvent mouse = (TMouseEvent) event;
947
948 switch (mouse.getType()) {
949
950 case MOUSE_DOWN:
951 onMouseDown(mouse);
952 break;
953
954 case MOUSE_UP:
955 onMouseUp(mouse);
956 break;
957
958 case MOUSE_MOTION:
959 onMouseMotion(mouse);
960 break;
961
962 default:
963 throw new IllegalArgumentException("Invalid mouse event type: "
964 + mouse.getType());
965 }
966 } else if (event instanceof TResizeEvent) {
967 onResize((TResizeEvent) event);
968 } else if (event instanceof TCommandEvent) {
969 onCommand((TCommandEvent) event);
970 } else if (event instanceof TMenuEvent) {
971 onMenu((TMenuEvent) event);
972 }
973
974 // Do nothing else
975 return;
976 }
977
978 /**
979 * Check if a mouse press/release event coordinate is contained in this
980 * widget.
981 *
982 * @param mouse a mouse-based event
983 * @return whether or not a mouse click would be sent to this widget
984 */
985 public final boolean mouseWouldHit(final TMouseEvent mouse) {
986
987 if (!enabled) {
988 return false;
989 }
990
991 if ((mouse.getAbsoluteX() >= getAbsoluteX())
992 && (mouse.getAbsoluteX() < getAbsoluteX() + width)
993 && (mouse.getAbsoluteY() >= getAbsoluteY())
994 && (mouse.getAbsoluteY() < getAbsoluteY() + height)
995 ) {
996 return true;
997 }
998 return false;
999 }
1000
30d336cc
KL
1001 /**
1002 * Convenience function to add a label to this container/window.
1003 *
1004 * @param text label
1005 * @param x column relative to parent
1006 * @param y row relative to parent
1007 * @return the new label
1008 */
1009 public final TLabel addLabel(final String text, final int x, final int y) {
1010 return addLabel(text, x, y, "tlabel");
1011 }
1012
1013 /**
1014 * Convenience function to add a label to this container/window.
1015 *
1016 * @param text label
1017 * @param x column relative to parent
1018 * @param y row relative to parent
1019 * @param colorKey ColorTheme key color to use for foreground text.
1020 * Default is "tlabel"
1021 * @return the new label
1022 */
1023 public final TLabel addLabel(final String text, final int x, final int y,
1024 final String colorKey) {
1025
1026 return new TLabel(this, text, x, y, colorKey);
1027 }
1028
1029 /**
1030 * Convenience function to add a button to this container/window.
1031 *
1032 * @param text label on the button
1033 * @param x column relative to parent
1034 * @param y row relative to parent
1035 * @param action to call when button is pressed
1036 * @return the new button
1037 */
1038 public final TButton addButton(final String text, final int x, final int y,
1039 final TAction action) {
1040
1041 return new TButton(this, text, x, y, action);
1042 }
1043
7272e49f
KL
1044 /**
1045 * Convenience function to add a checkbox to this container/window.
1046 *
1047 * @param x column relative to parent
1048 * @param y row relative to parent
1049 * @param label label to display next to (right of) the checkbox
1050 * @param checked initial check state
1051 * @return the new checkbox
1052 */
1053 public final TCheckbox addCheckbox(final int x, final int y,
1054 final String label, final boolean checked) {
1055
1056 return new TCheckbox(this, x, y, label, checked);
1057 }
30d336cc 1058
d502a0e9
KL
1059 /**
1060 * Convenience function to add a progress bar to this container/window.
1061 *
1062 * @param x column relative to parent
1063 * @param y row relative to parent
1064 * @param width width of progress bar
1065 * @param value initial value of percent complete
00d2622b 1066 * @return the new progress bar
d502a0e9
KL
1067 */
1068 public final TProgressBar addProgressBar(final int x, final int y,
1069 final int width, final int value) {
1070
1071 return new TProgressBar(this, x, y, width, value);
1072 }
1073
00d2622b
KL
1074 /**
1075 * Convenience function to add a radio button group to this
1076 * container/window.
1077 *
1078 * @param x column relative to parent
1079 * @param y row relative to parent
1080 * @param label label to display on the group box
1081 * @return the new radio button group
1082 */
1083 public final TRadioGroup addRadioGroup(final int x, final int y,
1084 final String label) {
1085
1086 return new TRadioGroup(this, x, y, label);
1087 }
1088
128e5be1
KL
1089 /**
1090 * Convenience function to add a text field to this container/window.
1091 *
1092 * @param x column relative to parent
1093 * @param y row relative to parent
1094 * @param width visible text width
1095 * @param fixed if true, the text cannot exceed the display width
1096 * @return the new text field
1097 */
1098 public final TField addField(final int x, final int y,
1099 final int width, final boolean fixed) {
1100
1101 return new TField(this, x, y, width, fixed);
1102 }
1103
1104 /**
1105 * Convenience function to add a text field to this container/window.
1106 *
1107 * @param x column relative to parent
1108 * @param y row relative to parent
1109 * @param width visible text width
1110 * @param fixed if true, the text cannot exceed the display width
1111 * @param text initial text, default is empty string
1112 * @return the new text field
1113 */
1114 public final TField addField(final int x, final int y,
1115 final int width, final boolean fixed, final String text) {
1116
1117 return new TField(this, x, y, width, fixed, text);
1118 }
1119
1120 /**
1121 * Convenience function to add a text field to this container/window.
1122 *
1123 * @param x column relative to parent
1124 * @param y row relative to parent
1125 * @param width visible text width
1126 * @param fixed if true, the text cannot exceed the display width
1127 * @param text initial text, default is empty string
1128 * @param enterAction function to call when enter key is pressed
1129 * @param updateAction function to call when the text is updated
1130 * @return the new text field
1131 */
1132 public final TField addField(final int x, final int y,
1133 final int width, final boolean fixed, final String text,
1134 final TAction enterAction, final TAction updateAction) {
1135
1136 return new TField(this, x, y, width, fixed, text, enterAction,
1137 updateAction);
1138 }
00d2622b 1139
cc99cba8
KL
1140 /**
1141 * Convenience function to add a scrollable text box to this
1142 * container/window.
1143 *
1144 * @param text text on the screen
1145 * @param x column relative to parent
1146 * @param y row relative to parent
1147 * @param width width of text area
1148 * @param height height of text area
1149 * @param colorKey ColorTheme key color to use for foreground text
1150 * @return the new text box
1151 */
c6940ed9 1152 public final TText addText(final String text, final int x,
cc99cba8
KL
1153 final int y, final int width, final int height, final String colorKey) {
1154
1155 return new TText(this, text, x, y, width, height, colorKey);
1156 }
1157
1158 /**
1159 * Convenience function to add a scrollable text box to this
1160 * container/window.
1161 *
1162 * @param text text on the screen
1163 * @param x column relative to parent
1164 * @param y row relative to parent
1165 * @param width width of text area
1166 * @param height height of text area
1167 * @return the new text box
1168 */
c6940ed9 1169 public final TText addText(final String text, final int x, final int y,
cc99cba8
KL
1170 final int width, final int height) {
1171
1172 return new TText(this, text, x, y, width, height, "ttext");
1173 }
1174
c6940ed9
KL
1175 /**
1176 * Convenience function to spawn a message box.
1177 *
1178 * @param title window title, will be centered along the top border
1179 * @param caption message to display. Use embedded newlines to get a
1180 * multi-line box.
1181 * @return the new message box
1182 */
1183 public final TMessageBox messageBox(final String title,
1184 final String caption) {
1185
1186 return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
1187 }
1188
1189 /**
1190 * Convenience function to spawn a message box.
1191 *
1192 * @param title window title, will be centered along the top border
1193 * @param caption message to display. Use embedded newlines to get a
1194 * multi-line box.
1195 * @param type one of the TMessageBox.Type constants. Default is
1196 * Type.OK.
1197 * @return the new message box
1198 */
1199 public final TMessageBox messageBox(final String title,
1200 final String caption, final TMessageBox.Type type) {
1201
1202 return getApplication().messageBox(title, caption, type);
1203 }
1204
1205 /**
1206 * Convenience function to spawn an input box.
1207 *
1208 * @param title window title, will be centered along the top border
1209 * @param caption message to display. Use embedded newlines to get a
1210 * multi-line box.
1211 * @return the new input box
1212 */
1213 public final TInputBox inputBox(final String title, final String caption) {
1214
1215 return getApplication().inputBox(title, caption);
1216 }
1217
1218 /**
1219 * Convenience function to spawn an input box.
1220 *
1221 * @param title window title, will be centered along the top border
1222 * @param caption message to display. Use embedded newlines to get a
1223 * multi-line box.
1224 * @param text initial text to seed the field with
1225 * @return the new input box
1226 */
1227 public final TInputBox inputBox(final String title, final String caption,
1228 final String text) {
1229
1230 return getApplication().inputBox(title, caption, text);
1231 }
cc99cba8 1232
87a17f3c
KL
1233 /**
1234 * Convenience function to add a password text field to this
1235 * container/window.
1236 *
1237 * @param x column relative to parent
1238 * @param y row relative to parent
1239 * @param width visible text width
1240 * @param fixed if true, the text cannot exceed the display width
1241 * @return the new text field
1242 */
1243 public final TPasswordField addPasswordField(final int x, final int y,
1244 final int width, final boolean fixed) {
1245
1246 return new TPasswordField(this, x, y, width, fixed);
1247 }
1248
1249 /**
1250 * Convenience function to add a password text field to this
1251 * container/window.
1252 *
1253 * @param x column relative to parent
1254 * @param y row relative to parent
1255 * @param width visible text width
1256 * @param fixed if true, the text cannot exceed the display width
1257 * @param text initial text, default is empty string
1258 * @return the new text field
1259 */
1260 public final TPasswordField addPasswordField(final int x, final int y,
1261 final int width, final boolean fixed, final String text) {
1262
1263 return new TPasswordField(this, x, y, width, fixed, text);
1264 }
1265
1266 /**
1267 * Convenience function to add a password text field to this
1268 * container/window.
1269 *
1270 * @param x column relative to parent
1271 * @param y row relative to parent
1272 * @param width visible text width
1273 * @param fixed if true, the text cannot exceed the display width
1274 * @param text initial text, default is empty string
1275 * @param enterAction function to call when enter key is pressed
1276 * @param updateAction function to call when the text is updated
1277 * @return the new text field
1278 */
1279 public final TPasswordField addPasswordField(final int x, final int y,
1280 final int width, final boolean fixed, final String text,
1281 final TAction enterAction, final TAction updateAction) {
1282
1283 return new TPasswordField(this, x, y, width, fixed, text, enterAction,
1284 updateAction);
1285 }
1286
48e27807 1287}