TButton and TLabel
[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 */
51public abstract class TWidget {
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
a06459bd
KL
315 /**
316 * See if this widget has a visible cursor.
317 *
318 * @return if true, this widget has a visible cursor
319 */
320 public final boolean visibleCursor() {
321 return hasCursor;
322 }
323
48e27807
KL
324 /**
325 * Cursor column position in relative coordinates.
326 */
327 private int cursorX = 0;
328
329 /**
330 * Cursor row position in relative coordinates.
331 */
332 private int cursorY = 0;
333
334 /**
335 * Comparison operator sorts on tabOrder.
336 *
337 * @param that another TWidget instance
338 * @return difference between this.tabOrder and that.tabOrder
339 */
340 public final int compare(final TWidget that) {
341 return (this.tabOrder - that.tabOrder);
342 }
343
344 /**
345 * See if this widget should render with the active color.
346 *
347 * @return true if this widget is active and all of its parents are
348 * active.
349 */
350 public final boolean getAbsoluteActive() {
351 if (parent == this) {
352 return active;
353 }
354 return (active && parent.getAbsoluteActive());
355 }
356
357 /**
358 * Returns the cursor X position.
359 *
360 * @return absolute screen column number for the cursor's X position
361 */
362 public final int getCursorAbsoluteX() {
363 assert (hasCursor);
364 return getAbsoluteX() + cursorX;
365 }
366
367 /**
368 * Returns the cursor Y position.
369 *
370 * @return absolute screen row number for the cursor's Y position
371 */
372 public final int getCursorAbsoluteY() {
373 assert (hasCursor);
374 return getAbsoluteY() + cursorY;
375 }
376
377 /**
378 * Compute my absolute X position as the sum of my X plus all my parent's
379 * X's.
380 *
381 * @return absolute screen column number for my X position
382 */
383 public final int getAbsoluteX() {
384 assert (parent != null);
385 if (parent == this) {
386 return x;
387 }
388 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
389 // Widgets on a TWindow have (0,0) as their top-left, but this is
390 // actually the TWindow's (1,1).
391 return parent.getAbsoluteX() + x + 1;
392 }
393 return parent.getAbsoluteX() + x;
394 }
395
396 /**
397 * Compute my absolute Y position as the sum of my Y plus all my parent's
398 * Y's.
399 *
400 * @return absolute screen row number for my Y position
401 */
402 public final int getAbsoluteY() {
403 assert (parent != null);
404 if (parent == this) {
405 return y;
406 }
407 if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
408 // Widgets on a TWindow have (0,0) as their top-left, but this is
409 // actually the TWindow's (1,1).
410 return parent.getAbsoluteY() + y + 1;
411 }
412 return parent.getAbsoluteY() + y;
413 }
414
928811d8
KL
415 /**
416 * Get the global color theme.
417 *
418 * @return the ColorTheme
419 */
420 public final ColorTheme getTheme() {
421 return window.getApplication().getTheme();
422 }
423
48e27807
KL
424 /**
425 * Draw my specific widget. When called, the screen rectangle I draw
426 * into is already setup (offset and clipping).
427 */
428 public void draw() {
429 // Default widget draws nothing.
430 }
431
432 /**
433 * Called by parent to render to TWindow.
434 */
435 public final void drawChildren() {
436 // Set my clipping rectangle
437 assert (window != null);
438 assert (window.getScreen() != null);
439 Screen screen = window.getScreen();
440
441 screen.setClipRight(width);
442 screen.setClipBottom(height);
443
444 int absoluteRightEdge = window.getAbsoluteX() + screen.getWidth();
445 int absoluteBottomEdge = window.getAbsoluteY() + screen.getHeight();
446 if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
447 absoluteRightEdge -= 1;
448 }
449 if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
450 absoluteBottomEdge -= 1;
451 }
452 int myRightEdge = getAbsoluteX() + width;
453 int myBottomEdge = getAbsoluteY() + height;
454 if (getAbsoluteX() > absoluteRightEdge) {
455 // I am offscreen
456 screen.setClipRight(0);
457 } else if (myRightEdge > absoluteRightEdge) {
458 screen.setClipRight(screen.getClipRight()
459 - myRightEdge - absoluteRightEdge);
460 }
461 if (getAbsoluteY() > absoluteBottomEdge) {
462 // I am offscreen
463 screen.setClipBottom(0);
464 } else if (myBottomEdge > absoluteBottomEdge) {
465 screen.setClipBottom(screen.getClipBottom()
466 - myBottomEdge - absoluteBottomEdge);
467 }
468
469 // Set my offset
470 screen.setOffsetX(getAbsoluteX());
471 screen.setOffsetY(getAbsoluteY());
472
473 // Draw me
474 draw();
475
476 // Continue down the chain
477 for (TWidget widget: children) {
478 widget.drawChildren();
479 }
480 }
481
482 /**
fca67db0 483 * Default constructor for subclasses.
48e27807
KL
484 */
485 protected TWidget() {
486 children = new LinkedList<TWidget>();
487 }
488
489 /**
490 * Protected constructor.
491 *
492 * @param parent parent widget
493 */
494 protected TWidget(final TWidget parent) {
30d336cc
KL
495 this(parent, true);
496 }
497
498 /**
499 * Protected constructor used by subclasses that are disabled by default.
500 *
501 * @param parent parent widget
502 * @param enabled if true assume enabled
503 */
504 protected TWidget(final TWidget parent, final boolean enabled) {
505 this.enabled = enabled;
48e27807
KL
506 this.parent = parent;
507 this.window = parent.window;
fca67db0 508 children = new LinkedList<TWidget>();
48e27807
KL
509 parent.addChild(this);
510 }
511
512 /**
513 * Add a child widget to my list of children. We set its tabOrder to 0
514 * and increment the tabOrder of all other children.
515 *
516 * @param child TWidget to add
517 */
518 private void addChild(final TWidget child) {
519 children.add(child);
520
521 if ((child.enabled)
522 && !(child instanceof THScroller)
523 && !(child instanceof TVScroller)
524 ) {
525 for (TWidget widget: children) {
526 widget.active = false;
527 }
528 child.active = true;
529 activeChild = child;
530 }
531 for (int i = 0; i < children.size(); i++) {
532 children.get(i).tabOrder = i;
533 }
534 }
535
536 /**
537 * Switch the active child.
538 *
539 * @param child TWidget to activate
540 */
541 public final void activate(final TWidget child) {
542 assert (child.enabled);
543 if ((child instanceof THScroller)
544 || (child instanceof TVScroller)
545 ) {
546 return;
547 }
548
549 if (child != activeChild) {
550 if (activeChild != null) {
551 activeChild.active = false;
552 }
553 child.active = true;
554 activeChild = child;
555 }
556 }
557
558 /**
559 * Switch the active child.
560 *
561 * @param tabOrder tabOrder of the child to activate. If that child
562 * isn't enabled, then the next enabled child will be activated.
563 */
564 public final void activate(final int tabOrder) {
565 if (activeChild == null) {
566 return;
567 }
568 TWidget child = null;
569 for (TWidget widget: children) {
570 if ((widget.enabled)
571 && !(widget instanceof THScroller)
572 && !(widget instanceof TVScroller)
573 && (widget.tabOrder >= tabOrder)
574 ) {
575 child = widget;
576 break;
577 }
578 }
579 if ((child != null) && (child != activeChild)) {
580 activeChild.active = false;
581 assert (child.enabled);
582 child.active = true;
583 activeChild = child;
584 }
585 }
586
587 /**
588 * Switch the active widget with the next in the tab order.
589 *
590 * @param forward if true, then switch to the next enabled widget in the
591 * list, otherwise switch to the previous enabled widget in the list
592 */
593 public final void switchWidget(final boolean forward) {
594
595 // Only switch if there are multiple enabled widgets
596 if ((children.size() < 2) || (activeChild == null)) {
597 return;
598 }
599
600 int tabOrder = activeChild.tabOrder;
601 do {
602 if (forward) {
603 tabOrder++;
604 } else {
605 tabOrder--;
606 }
607 if (tabOrder < 0) {
608
609 // If at the end, pass the switch to my parent.
610 if ((!forward) && (parent != this)) {
611 parent.switchWidget(forward);
612 return;
613 }
614
615 tabOrder = children.size() - 1;
616 } else if (tabOrder == children.size()) {
617 // If at the end, pass the switch to my parent.
618 if ((forward) && (parent != this)) {
619 parent.switchWidget(forward);
620 return;
621 }
622
623 tabOrder = 0;
624 }
625 if (activeChild.tabOrder == tabOrder) {
626 // We wrapped around
627 break;
628 }
629 } while ((!children.get(tabOrder).enabled)
630 && !(children.get(tabOrder) instanceof THScroller)
631 && !(children.get(tabOrder) instanceof TVScroller));
632
633 assert (children.get(tabOrder).enabled);
634
635 activeChild.active = false;
636 children.get(tabOrder).active = true;
637 activeChild = children.get(tabOrder);
638
639 // Refresh
640 window.getApplication().setRepaint();
641 }
642
643 /**
644 * Returns my active widget.
645 *
646 * @return widget that is active, or this if no children
647 */
928811d8 648 public TWidget getActiveChild() {
48e27807
KL
649 if ((this instanceof THScroller)
650 || (this instanceof TVScroller)
651 ) {
652 return parent;
653 }
654
655 for (TWidget widget: children) {
656 if (widget.active) {
657 return widget.getActiveChild();
658 }
659 }
660 // No active children, return me
661 return this;
662 }
663
664 /**
665 * Method that subclasses can override to handle keystrokes.
666 *
667 * @param keypress keystroke event
668 */
669 public void onKeypress(final TKeypressEvent keypress) {
670
671 if ((children.size() == 0)
fca67db0 672 // TODO
48e27807
KL
673 // || (cast(TTreeView)this)
674 // || (cast(TText)this)
675 ) {
676
677 // Defaults:
678 // tab / shift-tab - switch to next/previous widget
679 // right-arrow or down-arrow: same as tab
680 // left-arrow or up-arrow: same as shift-tab
681 if ((keypress.equals(kbTab))
682 || (keypress.equals(kbRight))
683 || (keypress.equals(kbDown))
684 ) {
685 parent.switchWidget(true);
686 return;
687 } else if ((keypress.equals(kbShiftTab))
688 || (keypress.equals(kbBackTab))
689 || (keypress.equals(kbLeft))
690 || (keypress.equals(kbUp))
691 ) {
692 parent.switchWidget(false);
693 return;
694 }
695 }
696
697 // If I have any buttons on me AND this is an Alt-key that matches
698 // its mnemonic, send it an Enter keystroke
699 for (TWidget widget: children) {
700 /*
701 TODO
702
703 if (TButton button = cast(TButton)w) {
704 if (button.enabled &&
705 !keypress.key.isKey &&
706 keypress.key.alt &&
707 !keypress.key.ctrl &&
708 (toLowercase(button.mnemonic.shortcut) == toLowercase(keypress.key.ch))) {
709
710 w.handleEvent(new TKeypressEvent(kbEnter));
711 return;
712 }
713 }
714 */
715 }
716
717 // Dispatch the keypress to an active widget
718 for (TWidget widget: children) {
719 if (widget.active) {
720 window.getApplication().setRepaint();
721 widget.handleEvent(keypress);
722 return;
723 }
724 }
725 }
726
727 /**
728 * Method that subclasses can override to handle mouse button presses.
729 *
730 * @param mouse mouse button event
731 */
732 public void onMouseDown(final TMouseEvent mouse) {
733 // Default: do nothing, pass to children instead
734 for (TWidget widget: children) {
735 if (widget.mouseWouldHit(mouse)) {
736 // Dispatch to this child, also activate it
737 activate(widget);
738
739 // Set x and y relative to the child's coordinates
740 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
741 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
742 widget.handleEvent(mouse);
743 return;
744 }
745 }
746 }
747
748 /**
749 * Method that subclasses can override to handle mouse button releases.
750 *
751 * @param mouse mouse button event
752 */
753 public void onMouseUp(final TMouseEvent mouse) {
754 // Default: do nothing, pass to children instead
755 for (TWidget widget: children) {
756 if (widget.mouseWouldHit(mouse)) {
757 // Dispatch to this child, also activate it
758 activate(widget);
759
760 // Set x and y relative to the child's coordinates
761 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
762 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
763 widget.handleEvent(mouse);
764 return;
765 }
766 }
767 }
768
769 /**
770 * Method that subclasses can override to handle mouse movements.
771 *
772 * @param mouse mouse motion event
773 */
774 public void onMouseMotion(final TMouseEvent mouse) {
775 // Default: do nothing, pass it on to ALL of my children. This way
776 // the children can see the mouse "leaving" their area.
777 for (TWidget widget: children) {
778 // Set x and y relative to the child's coordinates
779 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
780 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
781 widget.handleEvent(mouse);
782 }
783 }
784
785 /**
786 * Method that subclasses can override to handle window/screen resize
787 * events.
788 *
789 * @param resize resize event
790 */
791 public void onResize(final TResizeEvent resize) {
792 // Default: do nothing, pass to children instead
793 for (TWidget widget: children) {
794 widget.onResize(resize);
795 }
796 }
797
798 /**
799 * Method that subclasses can override to handle posted command events.
800 *
801 * @param command command event
802 */
803 public void onCommand(final TCommandEvent command) {
804 // Default: do nothing, pass to children instead
805 for (TWidget widget: children) {
806 widget.onCommand(command);
807 }
808 }
809
810 /**
811 * Method that subclasses can override to handle menu or posted menu
812 * events.
813 *
814 * @param menu menu event
815 */
816 public void onMenu(final TMenuEvent menu) {
817 // Default: do nothing, pass to children instead
818 for (TWidget widget: children) {
819 widget.onMenu(menu);
820 }
821 }
822
823 /**
824 * Method that subclasses can override to do processing when the UI is
825 * idle.
826 */
827 public void onIdle() {
828 // Default: do nothing, pass to children instead
829 for (TWidget widget: children) {
830 widget.onIdle();
831 }
832 }
833
834 /**
835 * Consume event. Subclasses that want to intercept all events in one go
836 * can override this method.
837 *
838 * @param event keyboard, mouse, resize, command, or menu event
839 */
840 public void handleEvent(final TInputEvent event) {
841 // System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
842 // event);
843
844 if (!enabled) {
845 // Discard event
846 // System.err.println(" -- discard --");
847 return;
848 }
849
850 if (event instanceof TKeypressEvent) {
851 onKeypress((TKeypressEvent) event);
852 } else if (event instanceof TMouseEvent) {
853
854 TMouseEvent mouse = (TMouseEvent) event;
855
856 switch (mouse.getType()) {
857
858 case MOUSE_DOWN:
859 onMouseDown(mouse);
860 break;
861
862 case MOUSE_UP:
863 onMouseUp(mouse);
864 break;
865
866 case MOUSE_MOTION:
867 onMouseMotion(mouse);
868 break;
869
870 default:
871 throw new IllegalArgumentException("Invalid mouse event type: "
872 + mouse.getType());
873 }
874 } else if (event instanceof TResizeEvent) {
875 onResize((TResizeEvent) event);
876 } else if (event instanceof TCommandEvent) {
877 onCommand((TCommandEvent) event);
878 } else if (event instanceof TMenuEvent) {
879 onMenu((TMenuEvent) event);
880 }
881
882 // Do nothing else
883 return;
884 }
885
886 /**
887 * Check if a mouse press/release event coordinate is contained in this
888 * widget.
889 *
890 * @param mouse a mouse-based event
891 * @return whether or not a mouse click would be sent to this widget
892 */
893 public final boolean mouseWouldHit(final TMouseEvent mouse) {
894
895 if (!enabled) {
896 return false;
897 }
898
899 if ((mouse.getAbsoluteX() >= getAbsoluteX())
900 && (mouse.getAbsoluteX() < getAbsoluteX() + width)
901 && (mouse.getAbsoluteY() >= getAbsoluteY())
902 && (mouse.getAbsoluteY() < getAbsoluteY() + height)
903 ) {
904 return true;
905 }
906 return false;
907 }
908
30d336cc
KL
909 /**
910 * Convenience function to add a label to this container/window.
911 *
912 * @param text label
913 * @param x column relative to parent
914 * @param y row relative to parent
915 * @return the new label
916 */
917 public final TLabel addLabel(final String text, final int x, final int y) {
918 return addLabel(text, x, y, "tlabel");
919 }
920
921 /**
922 * Convenience function to add a label to this container/window.
923 *
924 * @param text label
925 * @param x column relative to parent
926 * @param y row relative to parent
927 * @param colorKey ColorTheme key color to use for foreground text.
928 * Default is "tlabel"
929 * @return the new label
930 */
931 public final TLabel addLabel(final String text, final int x, final int y,
932 final String colorKey) {
933
934 return new TLabel(this, text, x, y, colorKey);
935 }
936
937 /**
938 * Convenience function to add a button to this container/window.
939 *
940 * @param text label on the button
941 * @param x column relative to parent
942 * @param y row relative to parent
943 * @param action to call when button is pressed
944 * @return the new button
945 */
946 public final TButton addButton(final String text, final int x, final int y,
947 final TAction action) {
948
949 return new TButton(this, text, x, y, action);
950 }
951
952
48e27807 953}