double-click support
[fanfix.git] / src / jexer / TWidget.java
index 0f920ba976242b5d81240682dac7f8d18a1ff89e..8833f8ff7d0668fb232222ef6fa032a232976be8 100644 (file)
@@ -1,38 +1,38 @@
-/**
+/*
  * Jexer - Java Text User Interface
  *
- * License: LGPLv3 or later
- *
- * This module is licensed under the GNU Lesser General Public License
- * Version 3.  Please see the file "COPYING" in this directory for more
- * information about the GNU Lesser General Public License Version 3.
+ * The MIT License (MIT)
  *
- *     Copyright (C) 2015  Kevin Lamonte
+ * Copyright (C) 2017 Kevin Lamonte
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see
- * http://www.gnu.org/licenses/, or write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * @author Kevin Lamonte [kevin.lamonte@gmail.com]
  * @version 1
  */
 package jexer;
 
+import java.io.IOException;
 import java.util.List;
-import java.util.LinkedList;
+import java.util.ArrayList;
 
+import jexer.backend.Screen;
 import jexer.bits.ColorTheme;
 import jexer.event.TCommandEvent;
 import jexer.event.TInputEvent;
@@ -40,7 +40,6 @@ import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
-import jexer.io.Screen;
 import jexer.menu.TMenu;
 import static jexer.TKeypress.*;
 
@@ -50,6 +49,10 @@ import static jexer.TKeypress.*;
  */
 public abstract class TWidget implements Comparable<TWidget> {
 
+    // ------------------------------------------------------------------------
+    // Common widget attributes -----------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Every widget has a parent widget that it may be "contained" in.  For
      * example, a TWindow might contain several TTextFields, or a TComboBox
@@ -66,51 +69,6 @@ public abstract class TWidget implements Comparable<TWidget> {
         return parent;
     }
 
-    /**
-     * Backdoor access for TWindow's constructor.  ONLY TWindow USES THIS.
-     *
-     * @param window the top-level window
-     * @param x column relative to parent
-     * @param y row relative to parent
-     * @param width width of window
-     * @param height height of window
-     */
-    protected final void setupForTWindow(final TWindow window,
-        final int x, final int y, final int width, final int height) {
-
-        this.parent = window;
-        this.window = window;
-        this.x      = x;
-        this.y      = y;
-        this.width  = width;
-        this.height = height;
-    }
-
-    /**
-     * Request full repaint on next screen refresh.
-     */
-    protected final void setRepaint() {
-        window.getApplication().setRepaint();
-    }
-
-    /**
-     * Get this TWidget's parent TApplication.
-     *
-     * @return the parent TApplication
-     */
-    public TApplication getApplication() {
-        return window.getApplication();
-    }
-
-    /**
-     * Get the Screen.
-     *
-     * @return the Screen
-     */
-    public Screen getScreen() {
-        return window.getScreen();
-    }
-
     /**
      * Child widgets that this widget contains.
      */
@@ -140,7 +98,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return if true, this widget will receive events
      */
-    public final boolean getActive() {
+    public final boolean isActive() {
         return active;
     }
 
@@ -259,6 +217,23 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.height = height;
     }
 
+    /**
+     * Change the dimensions.
+     *
+     * @param x absolute X position of the top-left corner
+     * @param y absolute Y position of the top-left corner
+     * @param width new widget width
+     * @param height new widget height
+     */
+    public final void setDimensions(final int x, final int y, final int width,
+        final int height) {
+
+        setX(x);
+        setY(y);
+        setWidth(width);
+        setHeight(height);
+    }
+
     /**
      * My tab order inside a window or containing widget.
      */
@@ -274,7 +249,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return if true, this widget can be tabbed to or receive events
      */
-    public final boolean getEnabled() {
+    public final boolean isEnabled() {
         return enabled;
     }
 
@@ -310,15 +285,15 @@ public abstract class TWidget implements Comparable<TWidget> {
     /**
      * If true, this widget has a cursor.
      */
-    private boolean hasCursor = false;
+    private boolean cursorVisible = false;
 
     /**
      * Set visible cursor flag.
      *
-     * @param hasCursor if true, this widget has a cursor
+     * @param cursorVisible if true, this widget has a cursor
      */
-    public final void setHasCursor(final boolean hasCursor) {
-        this.hasCursor = hasCursor;
+    public final void setCursorVisible(final boolean cursorVisible) {
+        this.cursorVisible = cursorVisible;
     }
 
     /**
@@ -326,8 +301,27 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return if true, this widget has a visible cursor
      */
-    public final boolean visibleCursor() {
-        return hasCursor;
+    public final boolean isCursorVisible() {
+        // If cursor is out of my bounds, it is not visible.
+        if ((cursorX >= width)
+            || (cursorX < 0)
+            || (cursorY >= height)
+            || (cursorY < 0)
+        ) {
+            return false;
+        }
+
+        // If cursor is out of my window's bounds, it is not visible.
+        if ((getCursorAbsoluteX() >= window.getAbsoluteX()
+                + window.getWidth() - 1)
+            || (getCursorAbsoluteX() < 0)
+            || (getCursorAbsoluteY() >= window.getAbsoluteY()
+                + window.getHeight() - 1)
+            || (getCursorAbsoluteY() < 0)
+        ) {
+            return false;
+        }
+        return cursorVisible;
     }
 
     /**
@@ -376,20 +370,52 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.cursorY = cursorY;
     }
 
+    // ------------------------------------------------------------------------
+    // TApplication integration -----------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get this TWidget's parent TApplication.
+     *
+     * @return the parent TApplication
+     */
+    public TApplication getApplication() {
+        return window.getApplication();
+    }
+
     /**
-     * Comparison operator sorts on tabOrder for TWidgets and z for TWindows.
+     * Get the Screen.
+     *
+     * @return the Screen
+     */
+    public Screen getScreen() {
+        return window.getScreen();
+    }
+
+    /**
+     * Comparison operator.  For various subclasses it sorts on:
+     * <ul>
+     * <li>tabOrder for TWidgets</li>
+     * <li>z for TWindows</li>
+     * <li>text for TTreeItems</li>
+     * </ul>
      *
-     * @param that another TWidget or TWindow instance
+     * @param that another TWidget, TWindow, or TTreeItem instance
      * @return difference between this.tabOrder and that.tabOrder, or
-     * difference between this.z and that.z
+     * difference between this.z and that.z, or String.compareTo(text)
      */
-    @Override
     public final int compareTo(final TWidget that) {
         if ((this instanceof TWindow)
             && (that instanceof TWindow)
         ) {
             return (((TWindow) this).getZ() - ((TWindow) that).getZ());
         }
+        if ((this instanceof TTreeItem)
+            && (that instanceof TTreeItem)
+        ) {
+            return (((TTreeItem) this).getText().compareTo(
+                ((TTreeItem) that).getText()));
+        }
         return (this.tabOrder - that.tabOrder);
     }
 
@@ -399,11 +425,11 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @return true if this widget is active and all of its parents are
      * active.
      */
-    public final boolean getAbsoluteActive() {
+    public final boolean isAbsoluteActive() {
         if (parent == this) {
             return active;
         }
-        return (active && parent.getAbsoluteActive());
+        return (active && parent.isAbsoluteActive());
     }
 
     /**
@@ -412,7 +438,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @return absolute screen column number for the cursor's X position
      */
     public final int getCursorAbsoluteX() {
-        assert (hasCursor);
+        assert (cursorVisible);
         return getAbsoluteX() + cursorX;
     }
 
@@ -422,7 +448,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @return absolute screen row number for the cursor's Y position
      */
     public final int getCursorAbsoluteY() {
-        assert (hasCursor);
+        assert (cursorVisible);
         return getAbsoluteY() + cursorY;
     }
 
@@ -437,7 +463,10 @@ public abstract class TWidget implements Comparable<TWidget> {
         if (parent == this) {
             return x;
         }
-        if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
+        if ((parent instanceof TWindow)
+            && !(parent instanceof TMenu)
+            && !(parent instanceof TDesktop)
+        ) {
             // Widgets on a TWindow have (0,0) as their top-left, but this is
             // actually the TWindow's (1,1).
             return parent.getAbsoluteX() + x + 1;
@@ -456,7 +485,10 @@ public abstract class TWidget implements Comparable<TWidget> {
         if (parent == this) {
             return y;
         }
-        if ((parent instanceof TWindow) && !(parent instanceof TMenu)) {
+        if ((parent instanceof TWindow)
+            && !(parent instanceof TMenu)
+            && !(parent instanceof TDesktop)
+        ) {
             // Widgets on a TWindow have (0,0) as their top-left, but this is
             // actually the TWindow's (1,1).
             return parent.getAbsoluteY() + y + 1;
@@ -487,14 +519,20 @@ public abstract class TWidget implements Comparable<TWidget> {
     public final void drawChildren() {
         // Set my clipping rectangle
         assert (window != null);
-        assert (window.getScreen() != null);
-        Screen screen = window.getScreen();
+        assert (getScreen() != null);
+        Screen screen = getScreen();
+
+        // Special case: TStatusBar is drawn by TApplication, not anything
+        // else.
+        if (this instanceof TStatusBar) {
+            return;
+        }
 
         screen.setClipRight(width);
         screen.setClipBottom(height);
 
-        int absoluteRightEdge = window.getAbsoluteX() + screen.getWidth();
-        int absoluteBottomEdge = window.getAbsoluteY() + screen.getHeight();
+        int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
+        int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
         if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
             absoluteRightEdge -= 1;
         }
@@ -508,14 +546,14 @@ public abstract class TWidget implements Comparable<TWidget> {
             screen.setClipRight(0);
         } else if (myRightEdge > absoluteRightEdge) {
             screen.setClipRight(screen.getClipRight()
-                - myRightEdge - absoluteRightEdge);
+                - (myRightEdge - absoluteRightEdge));
         }
         if (getAbsoluteY() > absoluteBottomEdge) {
             // I am offscreen
             screen.setClipBottom(0);
         } else if (myBottomEdge > absoluteBottomEdge) {
             screen.setClipBottom(screen.getClipBottom()
-                - myBottomEdge - absoluteBottomEdge);
+                - (myBottomEdge - absoluteBottomEdge));
         }
 
         // Set my offset
@@ -531,11 +569,22 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
+    /**
+     * Repaint the screen on the next update.
+     */
+    public void doRepaint() {
+        window.getApplication().doRepaint();
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Default constructor for subclasses.
      */
     protected TWidget() {
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
     }
 
     /**
@@ -547,6 +596,21 @@ public abstract class TWidget implements Comparable<TWidget> {
         this(parent, true);
     }
 
+    /**
+     * Protected constructor.
+     *
+     * @param parent parent widget
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of widget
+     * @param height height of widget
+     */
+    protected TWidget(final TWidget parent, final int x, final int y,
+        final int width, final int height) {
+
+        this(parent, true, x, y, width, height);
+    }
+
     /**
      * Protected constructor used by subclasses that are disabled by default.
      *
@@ -557,10 +621,69 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
-        children = new LinkedList<TWidget>();
-        parent.addChild(this);
+        children = new ArrayList<TWidget>();
+
+        // Do not add TStatusBars, they are drawn by TApplication
+        if (this instanceof TStatusBar) {
+        } else {
+            parent.addChild(this);
+        }
+    }
+
+    /**
+     * Protected constructor used by subclasses that are disabled by default.
+     *
+     * @param parent parent widget
+     * @param enabled if true assume enabled
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of widget
+     * @param height height of widget
+     */
+    protected TWidget(final TWidget parent, final boolean enabled,
+        final int x, final int y, final int width, final int height) {
+
+        this.enabled = enabled;
+        this.parent = parent;
+        this.window = parent.window;
+        children = new ArrayList<TWidget>();
+
+        // Do not add TStatusBars, they are drawn by TApplication
+        if (this instanceof TStatusBar) {
+        } else {
+            parent.addChild(this);
+        }
+
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+    }
+
+    /**
+     * Backdoor access for TWindow's constructor.  ONLY TWindow USES THIS.
+     *
+     * @param window the top-level window
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of window
+     * @param height height of window
+     */
+    protected final void setupForTWindow(final TWindow window,
+        final int x, final int y, final int width, final int height) {
+
+        this.parent = window;
+        this.window = window;
+        this.x      = x;
+        this.y      = y;
+        this.width  = width;
+        this.height = height;
     }
 
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Add a child widget to my list of children.  We set its tabOrder to 0
      * and increment the tabOrder of all other children.
@@ -687,9 +810,6 @@ public abstract class TWidget implements Comparable<TWidget> {
         activeChild.active = false;
         children.get(tabOrder).active = true;
         activeChild = children.get(tabOrder);
-
-        // Refresh
-        window.getApplication().setRepaint();
     }
 
     /**
@@ -713,6 +833,33 @@ public abstract class TWidget implements Comparable<TWidget> {
         return this;
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Check if a mouse press/release event coordinate is contained in this
+     * widget.
+     *
+     * @param mouse a mouse-based event
+     * @return whether or not a mouse click would be sent to this widget
+     */
+    public final boolean mouseWouldHit(final TMouseEvent mouse) {
+
+        if (!enabled) {
+            return false;
+        }
+
+        if ((mouse.getAbsoluteX() >= getAbsoluteX())
+            && (mouse.getAbsoluteX() <  getAbsoluteX() + width)
+            && (mouse.getAbsoluteY() >= getAbsoluteY())
+            && (mouse.getAbsoluteY() <  getAbsoluteY() + height)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Method that subclasses can override to handle keystrokes.
      *
@@ -721,9 +868,8 @@ public abstract class TWidget implements Comparable<TWidget> {
     public void onKeypress(final TKeypressEvent keypress) {
 
         if ((children.size() == 0)
-            // TODO
-            // || (cast(TTreeView)this)
-            // || (cast(TText)this)
+            || (this instanceof TTreeView)
+            || (this instanceof TText)
         ) {
 
             // Defaults:
@@ -749,27 +895,25 @@ public abstract class TWidget implements Comparable<TWidget> {
         // If I have any buttons on me AND this is an Alt-key that matches
         // its mnemonic, send it an Enter keystroke
         for (TWidget widget: children) {
-            /*
-            TODO
-
-            if (TButton button = cast(TButton)w) {
-                if (button.enabled &&
-                    !keypress.key.isKey &&
-                    keypress.key.alt &&
-                    !keypress.key.ctrl &&
-                    (toLowercase(button.mnemonic.shortcut) == toLowercase(keypress.key.ch))) {
-
-                    w.handleEvent(new TKeypressEvent(kbEnter));
+            if (widget instanceof TButton) {
+                TButton button = (TButton) widget;
+                if (button.isEnabled()
+                    && !keypress.getKey().isFnKey()
+                    && keypress.getKey().isAlt()
+                    && !keypress.getKey().isCtrl()
+                    && (Character.toLowerCase(button.getMnemonic().getShortcut())
+                        == Character.toLowerCase(keypress.getKey().getChar()))
+                ) {
+
+                    widget.handleEvent(new TKeypressEvent(kbEnter));
                     return;
                 }
             }
-             */
         }
 
         // Dispatch the keypress to an active widget
         for (TWidget widget: children) {
             if (widget.active) {
-                window.getApplication().setRepaint();
                 widget.handleEvent(keypress);
                 return;
             }
@@ -783,7 +927,8 @@ public abstract class TWidget implements Comparable<TWidget> {
      */
     public void onMouseDown(final TMouseEvent mouse) {
         // Default: do nothing, pass to children instead
-        for (TWidget widget: children) {
+        for (int i = children.size() - 1 ; i >= 0 ; i--) {
+            TWidget widget = children.get(i);
             if (widget.mouseWouldHit(mouse)) {
                 // Dispatch to this child, also activate it
                 activate(widget);
@@ -804,7 +949,8 @@ public abstract class TWidget implements Comparable<TWidget> {
      */
     public void onMouseUp(final TMouseEvent mouse) {
         // Default: do nothing, pass to children instead
-        for (TWidget widget: children) {
+        for (int i = children.size() - 1 ; i >= 0 ; i--) {
+            TWidget widget = children.get(i);
             if (widget.mouseWouldHit(mouse)) {
                 // Dispatch to this child, also activate it
                 activate(widget);
@@ -834,6 +980,29 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
+    /**
+     * Method that subclasses can override to handle mouse button
+     * double-clicks.
+     *
+     * @param mouse mouse button event
+     */
+    public void onMouseDoubleClick(final TMouseEvent mouse) {
+        // Default: do nothing, pass to children instead
+        for (int i = children.size() - 1 ; i >= 0 ; i--) {
+            TWidget widget = children.get(i);
+            if (widget.mouseWouldHit(mouse)) {
+                // Dispatch to this child, also activate it
+                activate(widget);
+
+                // Set x and y relative to the child's coordinates
+                mouse.setX(mouse.getAbsoluteX() - widget.getAbsoluteX());
+                mouse.setY(mouse.getAbsoluteY() - widget.getAbsoluteY());
+                widget.handleEvent(mouse);
+                return;
+            }
+        }
+    }
+
     /**
      * Method that subclasses can override to handle window/screen resize
      * events.
@@ -841,9 +1010,15 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param resize resize event
      */
     public void onResize(final TResizeEvent resize) {
-        // Default: do nothing, pass to children instead
-        for (TWidget widget: children) {
-            widget.onResize(resize);
+        // Default: change my width/height.
+        if (resize.getType() == TResizeEvent.Type.WIDGET) {
+            width = resize.getWidth();
+            height = resize.getHeight();
+        } else {
+            // Let children see the screen resize
+            for (TWidget widget: children) {
+                widget.onResize(resize);
+            }
         }
     }
 
@@ -874,7 +1049,8 @@ public abstract class TWidget implements Comparable<TWidget> {
 
     /**
      * Method that subclasses can override to do processing when the UI is
-     * idle.
+     * idle.  Note that repainting is NOT assumed.  To get a refresh after
+     * onIdle, call doRepaint().
      */
     public void onIdle() {
         // Default: do nothing, pass to children instead
@@ -919,6 +1095,10 @@ public abstract class TWidget implements Comparable<TWidget> {
                 onMouseMotion(mouse);
                 break;
 
+            case MOUSE_DOUBLE_CLICK:
+                onMouseDoubleClick(mouse);
+                break;
+
             default:
                 throw new IllegalArgumentException("Invalid mouse event type: "
                     + mouse.getType());
@@ -935,28 +1115,9 @@ public abstract class TWidget implements Comparable<TWidget> {
         return;
     }
 
-    /**
-     * Check if a mouse press/release event coordinate is contained in this
-     * widget.
-     *
-     * @param mouse a mouse-based event
-     * @return whether or not a mouse click would be sent to this widget
-     */
-    public final boolean mouseWouldHit(final TMouseEvent mouse) {
-
-        if (!enabled) {
-            return false;
-        }
-
-        if ((mouse.getAbsoluteX() >= getAbsoluteX())
-            && (mouse.getAbsoluteX() <  getAbsoluteX() + width)
-            && (mouse.getAbsoluteY() >= getAbsoluteY())
-            && (mouse.getAbsoluteY() <  getAbsoluteY() + height)
-        ) {
-            return true;
-        }
-        return false;
-    }
+    // ------------------------------------------------------------------------
+    // Other TWidget constructors ---------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Convenience function to add a label to this container/window.
@@ -1046,5 +1207,362 @@ public abstract class TWidget implements Comparable<TWidget> {
         return new TRadioGroup(this, x, y, label);
     }
 
+    /**
+     * Convenience function to add a text field to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @return the new text field
+     */
+    public final TField addField(final int x, final int y,
+        final int width, final boolean fixed) {
+
+        return new TField(this, x, y, width, fixed);
+    }
+
+    /**
+     * Convenience function to add a text field to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @param text initial text, default is empty string
+     * @return the new text field
+     */
+    public final TField addField(final int x, final int y,
+        final int width, final boolean fixed, final String text) {
+
+        return new TField(this, x, y, width, fixed, text);
+    }
+
+    /**
+     * Convenience function to add a text field to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @param text initial text, default is empty string
+     * @param enterAction function to call when enter key is pressed
+     * @param updateAction function to call when the text is updated
+     * @return the new text field
+     */
+    public final TField addField(final int x, final int y,
+        final int width, final boolean fixed, final String text,
+        final TAction enterAction, final TAction updateAction) {
+
+        return new TField(this, x, y, width, fixed, text, enterAction,
+            updateAction);
+    }
+
+    /**
+     * Convenience function to add a scrollable text box to this
+     * container/window.
+     *
+     * @param text text on the screen
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @param colorKey ColorTheme key color to use for foreground text
+     * @return the new text box
+     */
+    public final TText addText(final String text, final int x,
+        final int y, final int width, final int height, final String colorKey) {
+
+        return new TText(this, text, x, y, width, height, colorKey);
+    }
+
+    /**
+     * Convenience function to add a scrollable text box to this
+     * container/window.
+     *
+     * @param text text on the screen
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new text box
+     */
+    public final TText addText(final String text, final int x, final int y,
+        final int width, final int height) {
+
+        return new TText(this, text, x, y, width, height, "ttext");
+    }
+
+    /**
+     * Convenience function to add an editable text area box to this
+     * container/window.
+     *
+     * @param text text on the screen
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new text box
+     */
+    public final TEditorWidget addEditor(final String text, final int x,
+        final int y, final int width, final int height) {
+
+        return new TEditorWidget(this, text, x, y, width, height);
+    }
+
+    /**
+     * Convenience function to spawn a message box.
+     *
+     * @param title window title, will be centered along the top border
+     * @param caption message to display.  Use embedded newlines to get a
+     * multi-line box.
+     * @return the new message box
+     */
+    public final TMessageBox messageBox(final String title,
+        final String caption) {
+
+        return getApplication().messageBox(title, caption, TMessageBox.Type.OK);
+    }
+
+    /**
+     * Convenience function to spawn a message box.
+     *
+     * @param title window title, will be centered along the top border
+     * @param caption message to display.  Use embedded newlines to get a
+     * multi-line box.
+     * @param type one of the TMessageBox.Type constants.  Default is
+     * Type.OK.
+     * @return the new message box
+     */
+    public final TMessageBox messageBox(final String title,
+        final String caption, final TMessageBox.Type type) {
+
+        return getApplication().messageBox(title, caption, type);
+    }
+
+    /**
+     * Convenience function to spawn an input box.
+     *
+     * @param title window title, will be centered along the top border
+     * @param caption message to display.  Use embedded newlines to get a
+     * multi-line box.
+     * @return the new input box
+     */
+    public final TInputBox inputBox(final String title, final String caption) {
+
+        return getApplication().inputBox(title, caption);
+    }
+
+    /**
+     * Convenience function to spawn an input box.
+     *
+     * @param title window title, will be centered along the top border
+     * @param caption message to display.  Use embedded newlines to get a
+     * multi-line box.
+     * @param text initial text to seed the field with
+     * @return the new input box
+     */
+    public final TInputBox inputBox(final String title, final String caption,
+        final String text) {
+
+        return getApplication().inputBox(title, caption, text);
+    }
+
+    /**
+     * Convenience function to add a password text field to this
+     * container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @return the new text field
+     */
+    public final TPasswordField addPasswordField(final int x, final int y,
+        final int width, final boolean fixed) {
+
+        return new TPasswordField(this, x, y, width, fixed);
+    }
+
+    /**
+     * Convenience function to add a password text field to this
+     * container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @param text initial text, default is empty string
+     * @return the new text field
+     */
+    public final TPasswordField addPasswordField(final int x, final int y,
+        final int width, final boolean fixed, final String text) {
+
+        return new TPasswordField(this, x, y, width, fixed, text);
+    }
+
+    /**
+     * Convenience function to add a password text field to this
+     * container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width visible text width
+     * @param fixed if true, the text cannot exceed the display width
+     * @param text initial text, default is empty string
+     * @param enterAction function to call when enter key is pressed
+     * @param updateAction function to call when the text is updated
+     * @return the new text field
+     */
+    public final TPasswordField addPasswordField(final int x, final int y,
+        final int width, final boolean fixed, final String text,
+        final TAction enterAction, final TAction updateAction) {
+
+        return new TPasswordField(this, x, y, width, fixed, text, enterAction,
+            updateAction);
+    }
+
+    /**
+     * Convenience function to add a tree view to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     * @return the new tree view
+     */
+    public final TTreeView addTreeView(final int x, final int y,
+        final int width, final int height) {
+
+        return new TTreeView(this, x, y, width, height);
+    }
+
+    /**
+     * Convenience function to add a tree view to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of tree view
+     * @param height height of tree view
+     * @param action action to perform when an item is selected
+     * @return the new tree view
+     */
+    public final TTreeView addTreeView(final int x, final int y,
+        final int width, final int height, final TAction action) {
+
+        return new TTreeView(this, x, y, width, height, action);
+    }
+
+    /**
+     * Convenience function to spawn a file open box.
+     *
+     * @param path path of selected file
+     * @return the result of the new file open box
+     * @throws IOException if a java.io operation throws
+     */
+    public final String fileOpenBox(final String path) throws IOException {
+        return getApplication().fileOpenBox(path);
+    }
+
+    /**
+     * Convenience function to spawn a file open box.
+     *
+     * @param path path of selected file
+     * @param type one of the Type constants
+     * @return the result of the new file open box
+     * @throws IOException if a java.io operation throws
+     */
+    public final String fileOpenBox(final String path,
+        final TFileOpenBox.Type type) throws IOException {
+
+        return getApplication().fileOpenBox(path, type);
+    }
+    /**
+     * Convenience function to add a directory list to this container/window.
+     *
+     * @param path directory path, must be a directory
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new directory list
+     */
+    public final TDirectoryList addDirectoryList(final String path, final int x,
+        final int y, final int width, final int height) {
+
+        return new TDirectoryList(this, path, x, y, width, height, null);
+    }
+
+    /**
+     * Convenience function to add a directory list to this container/window.
+     *
+     * @param path directory path, must be a directory
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @param action action to perform when an item is selected
+     * @return the new directory list
+     */
+    public final TDirectoryList addDirectoryList(final String path, final int x,
+        final int y, final int width, final int height, final TAction action) {
+
+        return new TDirectoryList(this, path, x, y, width, height, action);
+    }
+
+    /**
+     * Convenience function to add a directory list to this container/window.
+     *
+     * @param strings list of strings to show
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new directory list
+     */
+    public final TList addList(final List<String> strings, final int x,
+        final int y, final int width, final int height) {
+
+        return new TList(this, strings, x, y, width, height, null);
+    }
+
+    /**
+     * Convenience function to add a directory list to this container/window.
+     *
+     * @param strings list of strings to show
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @param enterAction action to perform when an item is selected
+     * @return the new directory list
+     */
+    public final TList addList(final List<String> strings, final int x,
+        final int y, final int width, final int height,
+        final TAction enterAction) {
+
+        return new TList(this, strings, x, y, width, height, enterAction);
+    }
+
+    /**
+     * Convenience function to add a directory list to this container/window.
+     *
+     * @param strings list of strings to show
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @param enterAction action to perform when an item is selected
+     * @param moveAction action to perform when the user navigates to a new
+     * item with arrow/page keys
+     * @return the new directory list
+     */
+    public final TList addList(final List<String> strings, final int x,
+        final int y, final int width, final int height,
+        final TAction enterAction, final TAction moveAction) {
+
+        return new TList(this, strings, x, y, width, height, enterAction,
+            moveAction);
+    }
 
 }