reflowable text box
[fanfix.git] / src / jexer / TWidget.java
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 */
31 package jexer;
32
33 import java.util.List;
34 import java.util.LinkedList;
35
36 import jexer.bits.ColorTheme;
37 import jexer.event.TCommandEvent;
38 import jexer.event.TInputEvent;
39 import jexer.event.TKeypressEvent;
40 import jexer.event.TMenuEvent;
41 import jexer.event.TMouseEvent;
42 import jexer.event.TResizeEvent;
43 import jexer.io.Screen;
44 import jexer.menu.TMenu;
45 import 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 */
51 public abstract class TWidget implements Comparable<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 */
58 private TWidget parent = null;
59
60 /**
61 * Get parent widget.
62 *
63 * @return parent widget
64 */
65 public final TWidget getParent() {
66 return parent;
67 }
68
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 }
88
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
114 /**
115 * Child widgets that this widget contains.
116 */
117 private List<TWidget> children;
118
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
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 */
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 */
152 public final void setActive(final boolean active) {
153 this.active = active;
154 }
155
156 /**
157 * The window that this widget draws to.
158 */
159 private TWindow window = null;
160
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
170 /**
171 * Absolute X position of the top-left corner.
172 */
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 }
192
193 /**
194 * Absolute Y position of the top-left corner.
195 */
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 }
215
216 /**
217 * Width.
218 */
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 }
238
239 /**
240 * Height.
241 */
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 }
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;
288 if (!enabled) {
289 active = false;
290 // See if there are any active siblings to switch to
291 boolean foundSibling = false;
292 if (parent != null) {
293 for (TWidget w: parent.children) {
294 if ((w.enabled)
295 && !(this instanceof THScroller)
296 && !(this instanceof TVScroller)
297 ) {
298 parent.activate(w);
299 foundSibling = true;
300 break;
301 }
302 }
303 if (!foundSibling) {
304 parent.activeChild = null;
305 }
306 }
307 }
308 }
309
310 /**
311 * If true, this widget has a cursor.
312 */
313 private boolean hasCursor = false;
314
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
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
333 /**
334 * Cursor column position in relative coordinates.
335 */
336 private int cursorX = 0;
337
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
356 /**
357 * Cursor row position in relative coordinates.
358 */
359 private int cursorY = 0;
360
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
379 /**
380 * Comparison operator sorts on tabOrder for TWidgets and z for TWindows.
381 *
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)
389 && (that instanceof TWindow)
390 ) {
391 return (((TWindow) this).getZ() - ((TWindow) that).getZ());
392 }
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
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
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 /**
535 * Default constructor for subclasses.
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) {
547 this(parent, true);
548 }
549
550 /**
551 * Protected constructor used by subclasses that are disabled by default.
552 *
553 * @param parent parent widget
554 * @param enabled if true assume enabled
555 */
556 protected TWidget(final TWidget parent, final boolean enabled) {
557 this.enabled = enabled;
558 this.parent = parent;
559 this.window = parent.window;
560 children = new LinkedList<TWidget>();
561 parent.addChild(this);
562 }
563
564 /**
565 * Add a child widget to my list of children. We set its tabOrder to 0
566 * and increment the tabOrder of all other children.
567 *
568 * @param child TWidget to add
569 */
570 private void addChild(final TWidget child) {
571 children.add(child);
572
573 if ((child.enabled)
574 && !(child instanceof THScroller)
575 && !(child instanceof TVScroller)
576 ) {
577 for (TWidget widget: children) {
578 widget.active = false;
579 }
580 child.active = true;
581 activeChild = child;
582 }
583 for (int i = 0; i < children.size(); i++) {
584 children.get(i).tabOrder = i;
585 }
586 }
587
588 /**
589 * Switch the active child.
590 *
591 * @param child TWidget to activate
592 */
593 public final void activate(final TWidget child) {
594 assert (child.enabled);
595 if ((child instanceof THScroller)
596 || (child instanceof TVScroller)
597 ) {
598 return;
599 }
600
601 if (child != activeChild) {
602 if (activeChild != null) {
603 activeChild.active = false;
604 }
605 child.active = true;
606 activeChild = child;
607 }
608 }
609
610 /**
611 * Switch the active child.
612 *
613 * @param tabOrder tabOrder of the child to activate. If that child
614 * isn't enabled, then the next enabled child will be activated.
615 */
616 public final void activate(final int tabOrder) {
617 if (activeChild == null) {
618 return;
619 }
620 TWidget child = null;
621 for (TWidget widget: children) {
622 if ((widget.enabled)
623 && !(widget instanceof THScroller)
624 && !(widget instanceof TVScroller)
625 && (widget.tabOrder >= tabOrder)
626 ) {
627 child = widget;
628 break;
629 }
630 }
631 if ((child != null) && (child != activeChild)) {
632 activeChild.active = false;
633 assert (child.enabled);
634 child.active = true;
635 activeChild = child;
636 }
637 }
638
639 /**
640 * Switch the active widget with the next in the tab order.
641 *
642 * @param forward if true, then switch to the next enabled widget in the
643 * list, otherwise switch to the previous enabled widget in the list
644 */
645 public final void switchWidget(final boolean forward) {
646
647 // Only switch if there are multiple enabled widgets
648 if ((children.size() < 2) || (activeChild == null)) {
649 return;
650 }
651
652 int tabOrder = activeChild.tabOrder;
653 do {
654 if (forward) {
655 tabOrder++;
656 } else {
657 tabOrder--;
658 }
659 if (tabOrder < 0) {
660
661 // If at the end, pass the switch to my parent.
662 if ((!forward) && (parent != this)) {
663 parent.switchWidget(forward);
664 return;
665 }
666
667 tabOrder = children.size() - 1;
668 } else if (tabOrder == children.size()) {
669 // If at the end, pass the switch to my parent.
670 if ((forward) && (parent != this)) {
671 parent.switchWidget(forward);
672 return;
673 }
674
675 tabOrder = 0;
676 }
677 if (activeChild.tabOrder == tabOrder) {
678 // We wrapped around
679 break;
680 }
681 } while ((!children.get(tabOrder).enabled)
682 && !(children.get(tabOrder) instanceof THScroller)
683 && !(children.get(tabOrder) instanceof TVScroller));
684
685 assert (children.get(tabOrder).enabled);
686
687 activeChild.active = false;
688 children.get(tabOrder).active = true;
689 activeChild = children.get(tabOrder);
690
691 // Refresh
692 window.getApplication().setRepaint();
693 }
694
695 /**
696 * Returns my active widget.
697 *
698 * @return widget that is active, or this if no children
699 */
700 public TWidget getActiveChild() {
701 if ((this instanceof THScroller)
702 || (this instanceof TVScroller)
703 ) {
704 return parent;
705 }
706
707 for (TWidget widget: children) {
708 if (widget.active) {
709 return widget.getActiveChild();
710 }
711 }
712 // No active children, return me
713 return this;
714 }
715
716 /**
717 * Method that subclasses can override to handle keystrokes.
718 *
719 * @param keypress keystroke event
720 */
721 public void onKeypress(final TKeypressEvent keypress) {
722
723 if ((children.size() == 0)
724 // TODO
725 // || (cast(TTreeView)this)
726 // || (cast(TText)this)
727 ) {
728
729 // Defaults:
730 // tab / shift-tab - switch to next/previous widget
731 // right-arrow or down-arrow: same as tab
732 // left-arrow or up-arrow: same as shift-tab
733 if ((keypress.equals(kbTab))
734 || (keypress.equals(kbRight))
735 || (keypress.equals(kbDown))
736 ) {
737 parent.switchWidget(true);
738 return;
739 } else if ((keypress.equals(kbShiftTab))
740 || (keypress.equals(kbBackTab))
741 || (keypress.equals(kbLeft))
742 || (keypress.equals(kbUp))
743 ) {
744 parent.switchWidget(false);
745 return;
746 }
747 }
748
749 // If I have any buttons on me AND this is an Alt-key that matches
750 // its mnemonic, send it an Enter keystroke
751 for (TWidget widget: children) {
752 /*
753 TODO
754
755 if (TButton button = cast(TButton)w) {
756 if (button.enabled &&
757 !keypress.key.isKey &&
758 keypress.key.alt &&
759 !keypress.key.ctrl &&
760 (toLowercase(button.mnemonic.shortcut) == toLowercase(keypress.key.ch))) {
761
762 w.handleEvent(new TKeypressEvent(kbEnter));
763 return;
764 }
765 }
766 */
767 }
768
769 // Dispatch the keypress to an active widget
770 for (TWidget widget: children) {
771 if (widget.active) {
772 window.getApplication().setRepaint();
773 widget.handleEvent(keypress);
774 return;
775 }
776 }
777 }
778
779 /**
780 * Method that subclasses can override to handle mouse button presses.
781 *
782 * @param mouse mouse button event
783 */
784 public void onMouseDown(final TMouseEvent mouse) {
785 // Default: do nothing, pass to children instead
786 for (TWidget widget: children) {
787 if (widget.mouseWouldHit(mouse)) {
788 // Dispatch to this child, also activate it
789 activate(widget);
790
791 // Set x and y relative to the child's coordinates
792 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
793 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
794 widget.handleEvent(mouse);
795 return;
796 }
797 }
798 }
799
800 /**
801 * Method that subclasses can override to handle mouse button releases.
802 *
803 * @param mouse mouse button event
804 */
805 public void onMouseUp(final TMouseEvent mouse) {
806 // Default: do nothing, pass to children instead
807 for (TWidget widget: children) {
808 if (widget.mouseWouldHit(mouse)) {
809 // Dispatch to this child, also activate it
810 activate(widget);
811
812 // Set x and y relative to the child's coordinates
813 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
814 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
815 widget.handleEvent(mouse);
816 return;
817 }
818 }
819 }
820
821 /**
822 * Method that subclasses can override to handle mouse movements.
823 *
824 * @param mouse mouse motion event
825 */
826 public void onMouseMotion(final TMouseEvent mouse) {
827 // Default: do nothing, pass it on to ALL of my children. This way
828 // the children can see the mouse "leaving" their area.
829 for (TWidget widget: children) {
830 // Set x and y relative to the child's coordinates
831 mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
832 mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
833 widget.handleEvent(mouse);
834 }
835 }
836
837 /**
838 * Method that subclasses can override to handle window/screen resize
839 * events.
840 *
841 * @param resize resize event
842 */
843 public void onResize(final TResizeEvent resize) {
844 // Default: do nothing, pass to children instead
845 for (TWidget widget: children) {
846 widget.onResize(resize);
847 }
848 }
849
850 /**
851 * Method that subclasses can override to handle posted command events.
852 *
853 * @param command command event
854 */
855 public void onCommand(final TCommandEvent command) {
856 // Default: do nothing, pass to children instead
857 for (TWidget widget: children) {
858 widget.onCommand(command);
859 }
860 }
861
862 /**
863 * Method that subclasses can override to handle menu or posted menu
864 * events.
865 *
866 * @param menu menu event
867 */
868 public void onMenu(final TMenuEvent menu) {
869 // Default: do nothing, pass to children instead
870 for (TWidget widget: children) {
871 widget.onMenu(menu);
872 }
873 }
874
875 /**
876 * Method that subclasses can override to do processing when the UI is
877 * idle.
878 */
879 public void onIdle() {
880 // Default: do nothing, pass to children instead
881 for (TWidget widget: children) {
882 widget.onIdle();
883 }
884 }
885
886 /**
887 * Consume event. Subclasses that want to intercept all events in one go
888 * can override this method.
889 *
890 * @param event keyboard, mouse, resize, command, or menu event
891 */
892 public void handleEvent(final TInputEvent event) {
893 // System.err.printf("TWidget (%s) event: %s\n", this.getClass().getName(),
894 // event);
895
896 if (!enabled) {
897 // Discard event
898 // System.err.println(" -- discard --");
899 return;
900 }
901
902 if (event instanceof TKeypressEvent) {
903 onKeypress((TKeypressEvent) event);
904 } else if (event instanceof TMouseEvent) {
905
906 TMouseEvent mouse = (TMouseEvent) event;
907
908 switch (mouse.getType()) {
909
910 case MOUSE_DOWN:
911 onMouseDown(mouse);
912 break;
913
914 case MOUSE_UP:
915 onMouseUp(mouse);
916 break;
917
918 case MOUSE_MOTION:
919 onMouseMotion(mouse);
920 break;
921
922 default:
923 throw new IllegalArgumentException("Invalid mouse event type: "
924 + mouse.getType());
925 }
926 } else if (event instanceof TResizeEvent) {
927 onResize((TResizeEvent) event);
928 } else if (event instanceof TCommandEvent) {
929 onCommand((TCommandEvent) event);
930 } else if (event instanceof TMenuEvent) {
931 onMenu((TMenuEvent) event);
932 }
933
934 // Do nothing else
935 return;
936 }
937
938 /**
939 * Check if a mouse press/release event coordinate is contained in this
940 * widget.
941 *
942 * @param mouse a mouse-based event
943 * @return whether or not a mouse click would be sent to this widget
944 */
945 public final boolean mouseWouldHit(final TMouseEvent mouse) {
946
947 if (!enabled) {
948 return false;
949 }
950
951 if ((mouse.getAbsoluteX() >= getAbsoluteX())
952 && (mouse.getAbsoluteX() < getAbsoluteX() + width)
953 && (mouse.getAbsoluteY() >= getAbsoluteY())
954 && (mouse.getAbsoluteY() < getAbsoluteY() + height)
955 ) {
956 return true;
957 }
958 return false;
959 }
960
961 /**
962 * Convenience function to add a label to this container/window.
963 *
964 * @param text label
965 * @param x column relative to parent
966 * @param y row relative to parent
967 * @return the new label
968 */
969 public final TLabel addLabel(final String text, final int x, final int y) {
970 return addLabel(text, x, y, "tlabel");
971 }
972
973 /**
974 * Convenience function to add a label to this container/window.
975 *
976 * @param text label
977 * @param x column relative to parent
978 * @param y row relative to parent
979 * @param colorKey ColorTheme key color to use for foreground text.
980 * Default is "tlabel"
981 * @return the new label
982 */
983 public final TLabel addLabel(final String text, final int x, final int y,
984 final String colorKey) {
985
986 return new TLabel(this, text, x, y, colorKey);
987 }
988
989 /**
990 * Convenience function to add a button to this container/window.
991 *
992 * @param text label on the button
993 * @param x column relative to parent
994 * @param y row relative to parent
995 * @param action to call when button is pressed
996 * @return the new button
997 */
998 public final TButton addButton(final String text, final int x, final int y,
999 final TAction action) {
1000
1001 return new TButton(this, text, x, y, action);
1002 }
1003
1004 /**
1005 * Convenience function to add a checkbox to this container/window.
1006 *
1007 * @param x column relative to parent
1008 * @param y row relative to parent
1009 * @param label label to display next to (right of) the checkbox
1010 * @param checked initial check state
1011 * @return the new checkbox
1012 */
1013 public final TCheckbox addCheckbox(final int x, final int y,
1014 final String label, final boolean checked) {
1015
1016 return new TCheckbox(this, x, y, label, checked);
1017 }
1018
1019 /**
1020 * Convenience function to add a progress bar to this container/window.
1021 *
1022 * @param x column relative to parent
1023 * @param y row relative to parent
1024 * @param width width of progress bar
1025 * @param value initial value of percent complete
1026 * @return the new progress bar
1027 */
1028 public final TProgressBar addProgressBar(final int x, final int y,
1029 final int width, final int value) {
1030
1031 return new TProgressBar(this, x, y, width, value);
1032 }
1033
1034 /**
1035 * Convenience function to add a radio button group to this
1036 * container/window.
1037 *
1038 * @param x column relative to parent
1039 * @param y row relative to parent
1040 * @param label label to display on the group box
1041 * @return the new radio button group
1042 */
1043 public final TRadioGroup addRadioGroup(final int x, final int y,
1044 final String label) {
1045
1046 return new TRadioGroup(this, x, y, label);
1047 }
1048
1049 /**
1050 * Convenience function to add a text field to this container/window.
1051 *
1052 * @param x column relative to parent
1053 * @param y row relative to parent
1054 * @param width visible text width
1055 * @param fixed if true, the text cannot exceed the display width
1056 * @return the new text field
1057 */
1058 public final TField addField(final int x, final int y,
1059 final int width, final boolean fixed) {
1060
1061 return new TField(this, x, y, width, fixed);
1062 }
1063
1064 /**
1065 * Convenience function to add a text field to this container/window.
1066 *
1067 * @param x column relative to parent
1068 * @param y row relative to parent
1069 * @param width visible text width
1070 * @param fixed if true, the text cannot exceed the display width
1071 * @param text initial text, default is empty string
1072 * @return the new text field
1073 */
1074 public final TField addField(final int x, final int y,
1075 final int width, final boolean fixed, final String text) {
1076
1077 return new TField(this, x, y, width, fixed, text);
1078 }
1079
1080 /**
1081 * Convenience function to add a text field to this container/window.
1082 *
1083 * @param x column relative to parent
1084 * @param y row relative to parent
1085 * @param width visible text width
1086 * @param fixed if true, the text cannot exceed the display width
1087 * @param text initial text, default is empty string
1088 * @param enterAction function to call when enter key is pressed
1089 * @param updateAction function to call when the text is updated
1090 * @return the new text field
1091 */
1092 public final TField addField(final int x, final int y,
1093 final int width, final boolean fixed, final String text,
1094 final TAction enterAction, final TAction updateAction) {
1095
1096 return new TField(this, x, y, width, fixed, text, enterAction,
1097 updateAction);
1098 }
1099
1100 /**
1101 * Convenience function to add a scrollable text box to this
1102 * container/window.
1103 *
1104 * @param text text on the screen
1105 * @param x column relative to parent
1106 * @param y row relative to parent
1107 * @param width width of text area
1108 * @param height height of text area
1109 * @param colorKey ColorTheme key color to use for foreground text
1110 * @return the new text box
1111 */
1112 public TText addText(final String text, final int x,
1113 final int y, final int width, final int height, final String colorKey) {
1114
1115 return new TText(this, text, x, y, width, height, colorKey);
1116 }
1117
1118 /**
1119 * Convenience function to add a scrollable text box to this
1120 * container/window.
1121 *
1122 * @param text text on the screen
1123 * @param x column relative to parent
1124 * @param y row relative to parent
1125 * @param width width of text area
1126 * @param height height of text area
1127 * @return the new text box
1128 */
1129 public TText addText(final String text, final int x, final int y,
1130 final int width, final int height) {
1131
1132 return new TText(this, text, x, y, width, height, "ttext");
1133 }
1134
1135
1136 }