TStatusBar
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 18 Mar 2017 20:46:35 +0000 (16:46 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 18 Mar 2017 20:46:35 +0000 (16:46 -0400)
20 files changed:
README.md
docs/TODO.md
src/jexer/TApplication.java
src/jexer/TCommand.java
src/jexer/TEditColorThemeWindow.java
src/jexer/TLabel.java
src/jexer/TStatusBar.java [new file with mode: 0644]
src/jexer/TTerminalWindow.java
src/jexer/TWidget.java
src/jexer/TWindow.java
src/jexer/bits/ColorTheme.java
src/jexer/bits/GraphicsChars.java
src/jexer/demos/DemoCheckboxWindow.java
src/jexer/demos/DemoMainWindow.java
src/jexer/demos/DemoMsgBoxWindow.java
src/jexer/demos/DemoTextFieldWindow.java [new file with mode: 0644]
src/jexer/demos/DemoTextWindow.java
src/jexer/demos/DemoTreeViewWindow.java
src/jexer/io/package-info.java
src/jexer/tterminal/ECMA48.java

index 2f146cc4f3b585d4678eeb3bd2c350109099ed32..22436558fb3f653491423ec7a26bb600fb3855aa 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Jexer currently supports three backends:
   sequences generated by the library itself: ncurses is not required
   or linked to.  xterm mouse tracking using UTF8 and SGR coordinates
   are supported.  For the demo application, this is the default
-  backend on non-Windows platforms.
+  backend on non-Windows/non-Mac platforms.
 
 * The same command-line ECMA-48 / ANSI X3.64 type terminal as above,
   but to any general InputStream/OutputStream or Reader/Writer.  See
@@ -23,9 +23,9 @@ Jexer currently supports three backends:
   character encoding than the default UTF-8.
 
 * Java Swing UI.  This backend can be selected by setting
-  jexer.Swing=true.  The default window size for Swing is 132x40,
-  which is set in jexer.session.SwingSession.  For the demo
-  application, this is the default backend on Windows platforms.
+  jexer.Swing=true.  The default window size for Swing is 80x25, which
+  is set in jexer.session.SwingSession.  For the demo application,
+  this is the default backend on Windows and Mac platforms.
 
 Additional backends can be created by subclassing
 jexer.backend.Backend and passing it into the TApplication
@@ -180,10 +180,11 @@ ambiguous.  This section describes such issues.
     input (see the ENABLE_LINE_INPUT flag for GetConsoleMode() and
     SetConsoleMode()).
 
-  - TTerminalWindow launches 'script -fqe /dev/null' on non-Windows
-    platforms.  This is a workaround for the C library behavior of
-    checking for a tty: script launches $SHELL in a pseudo-tty.  This
-    works on Linux but might not on other Posix-y platforms.
+  - TTerminalWindow launches 'script -fqe /dev/null' or 'script -q -F
+    /dev/null' on non-Windows platforms.  This is a workaround for the
+    C library behavior of checking for a tty: script launches $SHELL
+    in a pseudo-tty.  This works on Linux and Mac but might not on
+    other Posix-y platforms.
 
   - Closing a TTerminalWindow without exiting the process inside it
     may result in a zombie 'script' process.
index 47f07d0575cb97f0538ff90f9005689663ec2bcd..f3e1233621569b4a9f783f8f03331ef80d65a8cc 100644 (file)
@@ -7,11 +7,6 @@ Roadmap
 
 0.0.4
 
-- TStatusBar
-  - TMenu version
-  - TWindow version
-  - Click mouse
-
 - TWindow
   - "Smart placement" for new windows
 
index 91d2e98d7c039da4aeb9d01c84bb33385f762772..ff9c19a0b00c9b7d18fa554fc80787a4edb9dabc 100644 (file)
@@ -58,12 +58,17 @@ import jexer.io.Screen;
 import jexer.menu.TMenu;
 import jexer.menu.TMenuItem;
 import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * TApplication sets up a full Text User Interface application.
  */
 public class TApplication implements Runnable {
 
+    // ------------------------------------------------------------------------
+    // Public constants -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * If true, emit thread stuff to System.err.
      */
@@ -94,6 +99,10 @@ public class TApplication implements Runnable {
         XTERM
     }
 
+    // ------------------------------------------------------------------------
+    // Primary/secondary event handlers ---------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * WidgetEventHandler is the main event consumer loop.  There are at most
      * two such threads in existence: the primary for normal case and a
@@ -365,6 +374,10 @@ public class TApplication implements Runnable {
         lockoutHandleEvent = false;
     }
 
+    // ------------------------------------------------------------------------
+    // TApplication attributes ------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Access to the physical screen, keyboard, and mouse.
      */
@@ -508,6 +521,23 @@ public class TApplication implements Runnable {
         return desktopBottom;
     }
 
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Display the about dialog.
+     */
+    protected void showAboutDialog() {
+        messageBox("About", "Jexer Version " +
+            this.getClass().getPackage().getImplementationVersion(),
+            TMessageBox.Type.OK);
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Public constructor.
      *
@@ -620,6 +650,10 @@ public class TApplication implements Runnable {
         (new Thread(primaryEventHandler)).start();
     }
 
+    // ------------------------------------------------------------------------
+    // Screen refresh loop ----------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Invert the cell color at a position.  This is used to track the mouse.
      *
@@ -681,6 +715,7 @@ public class TApplication implements Runnable {
         // Draw each window in reverse Z order
         List<TWindow> sorted = new LinkedList<TWindow>(windows);
         Collections.sort(sorted);
+        TWindow topLevel = sorted.get(0);
         Collections.reverse(sorted);
         for (TWindow window: sorted) {
             window.drawChildren();
@@ -699,6 +734,7 @@ public class TApplication implements Runnable {
             if (menu.isActive()) {
                 menuColor = theme.getColor("tmenu.highlighted");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted");
+                topLevel = menu;
             } else {
                 menuColor = theme.getColor("tmenu");
                 menuMnemonicColor = theme.getColor("tmenu.mnemonic");
@@ -725,6 +761,20 @@ public class TApplication implements Runnable {
             menu.drawChildren();
         }
 
+        // Draw the status bar of the top-level window
+        TStatusBar statusBar = topLevel.getStatusBar();
+        if (statusBar != null) {
+            getScreen().resetClipping();
+            statusBar.setWidth(getScreen().getWidth());
+            statusBar.setY(getScreen().getHeight() - topLevel.getY());
+            statusBar.draw();
+        } else {
+            CellAttributes barColor = new CellAttributes();
+            barColor.setTo(getTheme().getColor("tstatusbar.text"));
+            getScreen().hLineXY(0, desktopBottom, getScreen().getWidth(), ' ',
+                barColor);
+        }
+
         // Draw the mouse pointer
         invertCell(mouseX, mouseY);
         oldMouseX = mouseX;
@@ -754,6 +804,10 @@ public class TApplication implements Runnable {
         repaint = false;
     }
 
+    // ------------------------------------------------------------------------
+    // Main loop --------------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Run this application until it exits.
      */
@@ -988,7 +1042,7 @@ public class TApplication implements Runnable {
                 }
             }
 
-            if (!windowWillShortcut) {
+            if (!windowWillShortcut && !modalWindowActive()) {
                 TKeypress keypressLowercase = keypress.getKey().toLowerCase();
                 TMenuItem item = null;
                 synchronized (accelerators) {
@@ -1001,11 +1055,11 @@ public class TApplication implements Runnable {
                         return;
                     }
                 }
-            }
 
-            // Handle the keypress
-            if (onKeypress(keypress)) {
-                return;
+                // Handle the keypress
+                if (onKeypress(keypress)) {
+                    return;
+                }
             }
         }
 
@@ -1120,31 +1174,9 @@ public class TApplication implements Runnable {
         }
     }
 
-    /**
-     * Get the amount of time I can sleep before missing a Timer tick.
-     *
-     * @param timeout = initial (maximum) timeout in millis
-     * @return number of milliseconds between now and the next timer event
-     */
-    private long getSleepTime(final long timeout) {
-        Date now = new Date();
-        long nowTime = now.getTime();
-        long sleepTime = timeout;
-        for (TTimer timer: timers) {
-            long nextTickTime = timer.getNextTick().getTime();
-            if (nextTickTime < nowTime) {
-                return 0;
-            }
-
-            long timeDifference = nextTickTime - nowTime;
-            if (timeDifference < sleepTime) {
-                sleepTime = timeDifference;
-            }
-        }
-        assert (sleepTime >= 0);
-        assert (sleepTime <= timeout);
-        return sleepTime;
-    }
+    // ------------------------------------------------------------------------
+    // TWindow management -----------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Close window.  Note that the window's destructor is NOT called by this
@@ -1254,9 +1286,11 @@ public class TApplication implements Runnable {
      */
     public final void addWindow(final TWindow window) {
         synchronized (windows) {
-            // Do not allow a modal window to spawn a non-modal window
-            if ((windows.size() > 0) && (windows.get(0).isModal())) {
-                assert (window.isModal());
+            // Do not allow a modal window to spawn a non-modal window.  If a
+            // modal window is active, then this window will become modal
+            // too.
+            if (modalWindowActive()) {
+                window.flags |= TWindow.MODAL;
             }
             for (TWindow w: windows) {
                 if (w.isActive()) {
@@ -1281,9 +1315,119 @@ public class TApplication implements Runnable {
         if (windows.size() == 0) {
             return false;
         }
-        return windows.get(windows.size() - 1).isModal();
+
+        for (TWindow w: windows) {
+            if (w.isModal()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Close all open windows.
+     */
+    private void closeAllWindows() {
+        // Don't do anything if we are in the menu
+        if (activeMenu != null) {
+            return;
+        }
+        while (windows.size() > 0) {
+            closeWindow(windows.get(0));
+        }
     }
 
+    /**
+     * Re-layout the open windows as non-overlapping tiles.  This produces
+     * almost the same results as Turbo Pascal 7.0's IDE.
+     */
+    private void tileWindows() {
+        synchronized (windows) {
+            // Don't do anything if we are in the menu
+            if (activeMenu != null) {
+                return;
+            }
+            int z = windows.size();
+            if (z == 0) {
+                return;
+            }
+            int a = 0;
+            int b = 0;
+            a = (int)(Math.sqrt(z));
+            int c = 0;
+            while (c < a) {
+                b = (z - c) / a;
+                if (((a * b) + c) == z) {
+                    break;
+                }
+                c++;
+            }
+            assert (a > 0);
+            assert (b > 0);
+            assert (c < a);
+            int newWidth = (getScreen().getWidth() / a);
+            int newHeight1 = ((getScreen().getHeight() - 1) / b);
+            int newHeight2 = ((getScreen().getHeight() - 1) / (b + c));
+
+            List<TWindow> sorted = new LinkedList<TWindow>(windows);
+            Collections.sort(sorted);
+            Collections.reverse(sorted);
+            for (int i = 0; i < sorted.size(); i++) {
+                int logicalX = i / b;
+                int logicalY = i % b;
+                if (i >= ((a - 1) * b)) {
+                    logicalX = a - 1;
+                    logicalY = i - ((a - 1) * b);
+                }
+
+                TWindow w = sorted.get(i);
+                w.setX(logicalX * newWidth);
+                w.setWidth(newWidth);
+                if (i >= ((a - 1) * b)) {
+                    w.setY((logicalY * newHeight2) + 1);
+                    w.setHeight(newHeight2);
+                } else {
+                    w.setY((logicalY * newHeight1) + 1);
+                    w.setHeight(newHeight1);
+                }
+            }
+        }
+    }
+
+    /**
+     * Re-layout the open windows as overlapping cascaded windows.
+     */
+    private void cascadeWindows() {
+        synchronized (windows) {
+            // Don't do anything if we are in the menu
+            if (activeMenu != null) {
+                return;
+            }
+            int x = 0;
+            int y = 1;
+            List<TWindow> sorted = new LinkedList<TWindow>(windows);
+            Collections.sort(sorted);
+            Collections.reverse(sorted);
+            for (TWindow window: sorted) {
+                window.setX(x);
+                window.setY(y);
+                x++;
+                y++;
+                if (x > getScreen().getWidth()) {
+                    x = 0;
+                }
+                if (y >= getScreen().getHeight()) {
+                    y = 1;
+                }
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // TMenu management -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Check if a mouse event would hit either the active menu or any open
      * sub-menus.
@@ -1485,127 +1629,6 @@ public class TApplication implements Runnable {
         }
     }
 
-    /**
-     * Method that TApplication subclasses can override to handle menu or
-     * posted command events.
-     *
-     * @param command command event
-     * @return if true, this event was consumed
-     */
-    protected boolean onCommand(final TCommandEvent command) {
-        // Default: handle cmExit
-        if (command.equals(cmExit)) {
-            if (messageBox("Confirmation", "Exit application?",
-                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
-                quit = true;
-            }
-            return true;
-        }
-
-        if (command.equals(cmShell)) {
-            openTerminal(0, 0, TWindow.RESIZABLE);
-            return true;
-        }
-
-        if (command.equals(cmTile)) {
-            tileWindows();
-            return true;
-        }
-        if (command.equals(cmCascade)) {
-            cascadeWindows();
-            return true;
-        }
-        if (command.equals(cmCloseAll)) {
-            closeAllWindows();
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Display the about dialog.
-     */
-    protected void showAboutDialog() {
-        messageBox("About", "Jexer Version " +
-            this.getClass().getPackage().getImplementationVersion(),
-            TMessageBox.Type.OK);
-    }
-
-    /**
-     * Method that TApplication subclasses can override to handle menu
-     * events.
-     *
-     * @param menu menu event
-     * @return if true, this event was consumed
-     */
-    protected boolean onMenu(final TMenuEvent menu) {
-
-        // Default: handle MID_EXIT
-        if (menu.getId() == TMenu.MID_EXIT) {
-            if (messageBox("Confirmation", "Exit application?",
-                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
-                quit = true;
-            }
-            return true;
-        }
-
-        if (menu.getId() == TMenu.MID_SHELL) {
-            openTerminal(0, 0, TWindow.RESIZABLE);
-            return true;
-        }
-
-        if (menu.getId() == TMenu.MID_TILE) {
-            tileWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_CASCADE) {
-            cascadeWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_CLOSE_ALL) {
-            closeAllWindows();
-            return true;
-        }
-        if (menu.getId() == TMenu.MID_ABOUT) {
-            showAboutDialog();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Method that TApplication subclasses can override to handle keystrokes.
-     *
-     * @param keypress keystroke event
-     * @return if true, this event was consumed
-     */
-    protected boolean onKeypress(final TKeypressEvent keypress) {
-        // Default: only menu shortcuts
-
-        // Process Alt-F, Alt-E, etc. menu shortcut keys
-        if (!keypress.getKey().isFnKey()
-            && keypress.getKey().isAlt()
-            && !keypress.getKey().isCtrl()
-            && (activeMenu == null)
-        ) {
-
-            assert (subMenus.size() == 0);
-
-            for (TMenu menu: menus) {
-                if (Character.toLowerCase(menu.getMnemonic().getShortcut())
-                    == Character.toLowerCase(keypress.getKey().getChar())
-                ) {
-                    activeMenu = menu;
-                    menu.setActive(true);
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
     /**
      * Add a menu item to the global list.  If it has a keyboard accelerator,
      * that will be added the global hash.
@@ -1738,6 +1761,9 @@ public class TApplication implements Runnable {
         fileMenu.addSeparator();
         fileMenu.addDefaultItem(TMenu.MID_SHELL);
         fileMenu.addDefaultItem(TMenu.MID_EXIT);
+        TStatusBar statusBar = fileMenu.newStatusBar("File-management " +
+            "commands (Open, Save, Print, etc.)");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return fileMenu;
     }
 
@@ -1752,6 +1778,9 @@ public class TApplication implements Runnable {
         editMenu.addDefaultItem(TMenu.MID_COPY);
         editMenu.addDefaultItem(TMenu.MID_PASTE);
         editMenu.addDefaultItem(TMenu.MID_CLEAR);
+        TStatusBar statusBar = editMenu.newStatusBar("Editor operations, " +
+            "undo, and Clipboard access");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return editMenu;
     }
 
@@ -1771,6 +1800,9 @@ public class TApplication implements Runnable {
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_NEXT);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_PREVIOUS);
         windowMenu.addDefaultItem(TMenu.MID_WINDOW_CLOSE);
+        TStatusBar statusBar = windowMenu.newStatusBar("Open, arrange, and " +
+            "list windows");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return windowMenu;
     }
 
@@ -1789,106 +1821,156 @@ public class TApplication implements Runnable {
         helpMenu.addDefaultItem(TMenu.MID_HELP_ACTIVE_FILE);
         helpMenu.addSeparator();
         helpMenu.addDefaultItem(TMenu.MID_ABOUT);
+        TStatusBar statusBar = helpMenu.newStatusBar("Access online help");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
         return helpMenu;
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
-     * Close all open windows.
+     * Method that TApplication subclasses can override to handle menu or
+     * posted command events.
+     *
+     * @param command command event
+     * @return if true, this event was consumed
      */
-    private void closeAllWindows() {
-        // Don't do anything if we are in the menu
-        if (activeMenu != null) {
-            return;
+    protected boolean onCommand(final TCommandEvent command) {
+        // Default: handle cmExit
+        if (command.equals(cmExit)) {
+            if (messageBox("Confirmation", "Exit application?",
+                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
+                quit = true;
+            }
+            return true;
         }
-        while (windows.size() > 0) {
-            closeWindow(windows.get(0));
+
+        if (command.equals(cmShell)) {
+            openTerminal(0, 0, TWindow.RESIZABLE);
+            return true;
+        }
+
+        if (command.equals(cmTile)) {
+            tileWindows();
+            return true;
+        }
+        if (command.equals(cmCascade)) {
+            cascadeWindows();
+            return true;
+        }
+        if (command.equals(cmCloseAll)) {
+            closeAllWindows();
+            return true;
         }
+
+        return false;
     }
 
     /**
-     * Re-layout the open windows as non-overlapping tiles.  This produces
-     * almost the same results as Turbo Pascal 7.0's IDE.
+     * Method that TApplication subclasses can override to handle menu
+     * events.
+     *
+     * @param menu menu event
+     * @return if true, this event was consumed
      */
-    private void tileWindows() {
-        synchronized (windows) {
-            // Don't do anything if we are in the menu
-            if (activeMenu != null) {
-                return;
-            }
-            int z = windows.size();
-            if (z == 0) {
-                return;
-            }
-            int a = 0;
-            int b = 0;
-            a = (int)(Math.sqrt(z));
-            int c = 0;
-            while (c < a) {
-                b = (z - c) / a;
-                if (((a * b) + c) == z) {
-                    break;
-                }
-                c++;
+    protected boolean onMenu(final TMenuEvent menu) {
+
+        // Default: handle MID_EXIT
+        if (menu.getId() == TMenu.MID_EXIT) {
+            if (messageBox("Confirmation", "Exit application?",
+                    TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
+                quit = true;
             }
-            assert (a > 0);
-            assert (b > 0);
-            assert (c < a);
-            int newWidth = (getScreen().getWidth() / a);
-            int newHeight1 = ((getScreen().getHeight() - 1) / b);
-            int newHeight2 = ((getScreen().getHeight() - 1) / (b + c));
+            return true;
+        }
 
-            List<TWindow> sorted = new LinkedList<TWindow>(windows);
-            Collections.sort(sorted);
-            Collections.reverse(sorted);
-            for (int i = 0; i < sorted.size(); i++) {
-                int logicalX = i / b;
-                int logicalY = i % b;
-                if (i >= ((a - 1) * b)) {
-                    logicalX = a - 1;
-                    logicalY = i - ((a - 1) * b);
-                }
+        if (menu.getId() == TMenu.MID_SHELL) {
+            openTerminal(0, 0, TWindow.RESIZABLE);
+            return true;
+        }
 
-                TWindow w = sorted.get(i);
-                w.setX(logicalX * newWidth);
-                w.setWidth(newWidth);
-                if (i >= ((a - 1) * b)) {
-                    w.setY((logicalY * newHeight2) + 1);
-                    w.setHeight(newHeight2);
-                } else {
-                    w.setY((logicalY * newHeight1) + 1);
-                    w.setHeight(newHeight1);
+        if (menu.getId() == TMenu.MID_TILE) {
+            tileWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_CASCADE) {
+            cascadeWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_CLOSE_ALL) {
+            closeAllWindows();
+            return true;
+        }
+        if (menu.getId() == TMenu.MID_ABOUT) {
+            showAboutDialog();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Method that TApplication subclasses can override to handle keystrokes.
+     *
+     * @param keypress keystroke event
+     * @return if true, this event was consumed
+     */
+    protected boolean onKeypress(final TKeypressEvent keypress) {
+        // Default: only menu shortcuts
+
+        // Process Alt-F, Alt-E, etc. menu shortcut keys
+        if (!keypress.getKey().isFnKey()
+            && keypress.getKey().isAlt()
+            && !keypress.getKey().isCtrl()
+            && (activeMenu == null)
+            && !modalWindowActive()
+        ) {
+
+            assert (subMenus.size() == 0);
+
+            for (TMenu menu: menus) {
+                if (Character.toLowerCase(menu.getMnemonic().getShortcut())
+                    == Character.toLowerCase(keypress.getKey().getChar())
+                ) {
+                    activeMenu = menu;
+                    menu.setActive(true);
+                    return true;
                 }
             }
         }
+
+        return false;
     }
 
+    // ------------------------------------------------------------------------
+    // TTimer management ------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
-     * Re-layout the open windows as overlapping cascaded windows.
+     * Get the amount of time I can sleep before missing a Timer tick.
+     *
+     * @param timeout = initial (maximum) timeout in millis
+     * @return number of milliseconds between now and the next timer event
      */
-    private void cascadeWindows() {
-        synchronized (windows) {
-            // Don't do anything if we are in the menu
-            if (activeMenu != null) {
-                return;
+    private long getSleepTime(final long timeout) {
+        Date now = new Date();
+        long nowTime = now.getTime();
+        long sleepTime = timeout;
+        for (TTimer timer: timers) {
+            long nextTickTime = timer.getNextTick().getTime();
+            if (nextTickTime < nowTime) {
+                return 0;
             }
-            int x = 0;
-            int y = 1;
-            List<TWindow> sorted = new LinkedList<TWindow>(windows);
-            Collections.sort(sorted);
-            Collections.reverse(sorted);
-            for (TWindow window: sorted) {
-                window.setX(x);
-                window.setY(y);
-                x++;
-                y++;
-                if (x > getScreen().getWidth()) {
-                    x = 0;
-                }
-                if (y >= getScreen().getHeight()) {
-                    y = 1;
-                }
+
+            long timeDifference = nextTickTime - nowTime;
+            if (timeDifference < sleepTime) {
+                sleepTime = timeDifference;
             }
         }
+        assert (sleepTime >= 0);
+        assert (sleepTime <= timeout);
+        return sleepTime;
     }
 
     /**
@@ -1920,6 +2002,10 @@ public class TApplication implements Runnable {
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Other TWindow constructors ---------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Convenience function to spawn a message box.
      *
index 0bb3f95bfc45c132d5dc5f248735d441e4a08c34..584994f948cdfd5dd2abb3329eb791981bd50958 100644 (file)
@@ -39,82 +39,87 @@ public class TCommand {
      * Immediately abort the application (e.g. remote side closed
      * connection).
      */
-    public static final int ABORT       = 1;
+    public static final int ABORT               = 1;
 
     /**
      * File open dialog.
      */
-    public static final int OPEN        = 2;
+    public static final int OPEN                = 2;
 
     /**
      * Exit application.
      */
-    public static final int EXIT        = 3;
+    public static final int EXIT                = 3;
 
     /**
      * Spawn OS shell window.
      */
-    public static final int SHELL       = 4;
+    public static final int SHELL               = 4;
 
     /**
      * Cut selected text and copy to the clipboard.
      */
-    public static final int CUT         = 5;
+    public static final int CUT                 = 5;
 
     /**
      * Copy selected text to clipboard.
      */
-    public static final int COPY        = 6;
+    public static final int COPY                = 6;
 
     /**
      * Paste from clipboard.
      */
-    public static final int PASTE       = 7;
+    public static final int PASTE               = 7;
 
     /**
      * Clear selected text without copying it to the clipboard.
      */
-    public static final int CLEAR       = 8;
+    public static final int CLEAR               = 8;
 
     /**
      * Tile windows.
      */
-    public static final int TILE        = 9;
+    public static final int TILE                = 9;
 
     /**
      * Cascade windows.
      */
-    public static final int CASCADE     = 10;
+    public static final int CASCADE             = 10;
 
     /**
      * Close all windows.
      */
-    public static final int CLOSE_ALL   = 11;
+    public static final int CLOSE_ALL           = 11;
 
     /**
      * Move (move/resize) window.
      */
-    public static final int WINDOW_MOVE = 12;
+    public static final int WINDOW_MOVE         = 12;
 
     /**
      * Zoom (maximize/restore) window.
      */
-    public static final int WINDOW_ZOOM = 13;
+    public static final int WINDOW_ZOOM         = 13;
 
     /**
      * Next window (like Alt-TAB).
      */
-    public static final int WINDOW_NEXT = 14;
+    public static final int WINDOW_NEXT         = 14;
 
     /**
      * Previous window (like Shift-Alt-TAB).
      */
-    public static final int WINDOW_PREVIOUS = 15;
+    public static final int WINDOW_PREVIOUS     = 15;
 
     /**
      * Close window.
      */
-    public static final int WINDOW_CLOSE = 16;
+    public static final int WINDOW_CLOSE        = 16;
+
+    /**
+     * Enter help system.
+     */
+    public static final int HELP                = 20;
 
     /**
      * Type of command, one of EXIT, CASCADE, etc.
@@ -166,22 +171,23 @@ public class TCommand {
         return type;
     }
 
-    public static final TCommand cmAbort      = new TCommand(ABORT);
-    public static final TCommand cmExit       = new TCommand(EXIT);
-    public static final TCommand cmQuit       = new TCommand(EXIT);
-    public static final TCommand cmOpen       = new TCommand(OPEN);
-    public static final TCommand cmShell      = new TCommand(SHELL);
-    public static final TCommand cmCut        = new TCommand(CUT);
-    public static final TCommand cmCopy       = new TCommand(COPY);
-    public static final TCommand cmPaste      = new TCommand(PASTE);
-    public static final TCommand cmClear      = new TCommand(CLEAR);
-    public static final TCommand cmTile       = new TCommand(TILE);
-    public static final TCommand cmCascade    = new TCommand(CASCADE);
-    public static final TCommand cmCloseAll   = new TCommand(CLOSE_ALL);
-    public static final TCommand cmWindowMove = new TCommand(WINDOW_MOVE);
-    public static final TCommand cmWindowZoom = new TCommand(WINDOW_ZOOM);
-    public static final TCommand cmWindowNext = new TCommand(WINDOW_NEXT);
+    public static final TCommand cmAbort        = new TCommand(ABORT);
+    public static final TCommand cmExit         = new TCommand(EXIT);
+    public static final TCommand cmQuit         = new TCommand(EXIT);
+    public static final TCommand cmOpen         = new TCommand(OPEN);
+    public static final TCommand cmShell        = new TCommand(SHELL);
+    public static final TCommand cmCut          = new TCommand(CUT);
+    public static final TCommand cmCopy         = new TCommand(COPY);
+    public static final TCommand cmPaste        = new TCommand(PASTE);
+    public static final TCommand cmClear        = new TCommand(CLEAR);
+    public static final TCommand cmTile         = new TCommand(TILE);
+    public static final TCommand cmCascade      = new TCommand(CASCADE);
+    public static final TCommand cmCloseAll     = new TCommand(CLOSE_ALL);
+    public static final TCommand cmWindowMove   = new TCommand(WINDOW_MOVE);
+    public static final TCommand cmWindowZoom   = new TCommand(WINDOW_ZOOM);
+    public static final TCommand cmWindowNext   = new TCommand(WINDOW_NEXT);
     public static final TCommand cmWindowPrevious = new TCommand(WINDOW_PREVIOUS);
-    public static final TCommand cmWindowClose = new TCommand(WINDOW_CLOSE);
+    public static final TCommand cmWindowClose  = new TCommand(WINDOW_CLOSE);
+    public static final TCommand cmHelp         = new TCommand(HELP);
 
 }
index 976c8957b22f74db54f286903c997982c1a01916..47197d4216f82fd81a5b4801c3645bb42f72ef14 100644 (file)
@@ -707,6 +707,8 @@ public final class TEditColorThemeWindow extends TWindow {
         // Default to the color list
         activate(colorNames);
 
+        // Add shortcut text
+        newStatusBar("Select Colors");
     }
 
     /**
index f5e966581a6dbffb6026fe088c3d1a6246491e21..d6b817a845b651e1fcccef01f5e863d0bb6b86eb 100644 (file)
@@ -99,7 +99,8 @@ public final class TLabel extends TWidget {
     /**
      * Draw a static label.
      */
-    @Override public void draw() {
+    @Override
+    public void draw() {
         // Setup my color
         CellAttributes color = new CellAttributes();
         color.setTo(getTheme().getColor(colorKey));
diff --git a/src/jexer/TStatusBar.java b/src/jexer/TStatusBar.java
new file mode 100644 (file)
index 0000000..d03d526
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2016 Kevin Lamonte
+ *
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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.util.ArrayList;
+
+import jexer.bits.CellAttributes;
+import jexer.bits.GraphicsChars;
+import jexer.event.TCommandEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+
+/**
+ * TStatusBar implements a status line with clickable buttons.
+ */
+public final class TStatusBar extends TWidget {
+
+    /**
+     * A single shortcut key.
+     */
+    private class TStatusBarKey {
+
+        /**
+         * The keypress for this action.
+         */
+        public TKeypress key;
+
+        /**
+         * The command to issue.
+         */
+        public TCommand cmd;
+
+        /**
+         * The label text.
+         */
+        public String label;
+
+        /**
+         * If true, the mouse is on this key.
+         */
+        public boolean selected;
+
+        /**
+         * The left edge coordinate to draw this key with.
+         */
+        public int x = 0;
+
+        /**
+         * The width of this key on the screen.
+         *
+         * @return the number of columns this takes when drawn
+         */
+        public int width() {
+            return this.label.length() + this.key.toString().length() + 3;
+        }
+
+        /**
+         * Add a key to this status bar.
+         *
+         * @param key the key to trigger on
+         * @param cmd the command event to issue when key is pressed or this
+         * item is clicked
+         * @param label the label for this action
+         */
+        public TStatusBarKey(final TKeypress key, final TCommand cmd,
+            final String label) {
+
+            this.key    = key;
+            this.cmd    = cmd;
+            this.label  = label;
+        }
+
+    }
+
+    /**
+     * Remember mouse state.
+     */
+    private TMouseEvent mouse;
+
+    /**
+     * The text to display on the right side of the shortcut keys.
+     */
+    private String text = null;
+
+    /**
+     * The shortcut keys.
+     */
+    private ArrayList<TStatusBarKey> keys = new ArrayList<TStatusBarKey>();
+
+    /**
+     * Add a key to this status bar.
+     *
+     * @param key the key to trigger on
+     * @param cmd the command event to issue when key is pressed or this item
+     * is clicked
+     * @param label the label for this action
+     */
+    public void addShortcutKeypress(final TKeypress key, final TCommand cmd,
+        final String label) {
+
+        TStatusBarKey newKey = new TStatusBarKey(key, cmd, label);
+        if (keys.size() > 0) {
+            TStatusBarKey oldKey = keys.get(keys.size() - 1);
+            newKey.x = oldKey.x + oldKey.width();
+        }
+        keys.add(newKey);
+    }
+
+    /**
+     * Set the text to display on the right side of the shortcut keys.
+     *
+     * @param text the new text
+     */
+    public void setText(final String text) {
+        this.text = text;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     * @param text text for the bar on the bottom row
+     */
+    public TStatusBar(final TWidget parent, final String text) {
+
+        // Set parent and window
+        super(parent, false, 0, 0, text.length(), 1);
+
+        this.text = text;
+    }
+
+    /**
+     * Public constructor.
+     *
+     * @param parent parent widget
+     */
+    public TStatusBar(final TWidget parent) {
+        this(parent, "");
+    }
+
+    /**
+     * Draw the bar.
+     */
+    @Override
+    public void draw() {
+        CellAttributes barColor = new CellAttributes();
+        barColor.setTo(getTheme().getColor("tstatusbar.text"));
+        CellAttributes keyColor = new CellAttributes();
+        keyColor.setTo(getTheme().getColor("tstatusbar.button"));
+        CellAttributes selectedColor = new CellAttributes();
+        selectedColor.setTo(getTheme().getColor("tstatusbar.selected"));
+
+        // Status bar is weird.  Its draw() method is called directly by
+        // TApplication after everything is drawn, and after
+        // Screen.resetClipping().  So at this point we are drawing in
+        // absolute coordinates, not relative to our TWindow.
+        int row = getScreen().getHeight() - 1;
+        int width = getScreen().getWidth();
+
+        getScreen().hLineXY(0, row, width, ' ', barColor);
+
+        int col = 0;
+        for (TStatusBarKey key: keys) {
+            String keyStr = key.key.toString();
+            if (key.selected) {
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+                getScreen().putStringXY(col, row, keyStr, selectedColor);
+                col += keyStr.length();
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+                getScreen().putStringXY(col, row, key.label, selectedColor);
+                col += key.label.length();
+                getScreen().putCharXY(col++, row, ' ', selectedColor);
+            } else {
+                getScreen().putCharXY(col++, row, ' ', barColor);
+                getScreen().putStringXY(col, row, keyStr, keyColor);
+                col += keyStr.length() + 1;
+                getScreen().putStringXY(col, row, key.label, barColor);
+                col += key.label.length();
+                getScreen().putCharXY(col++, row, ' ', barColor);
+            }
+        }
+        if (text.length() > 0) {
+            if (keys.size() > 0) {
+                getScreen().putCharXY(col++, row, GraphicsChars.VERTICAL_BAR,
+                    barColor);
+            }
+            getScreen().putCharXY(col++, row, ' ', barColor);
+            getScreen().putStringXY(col, row, text, barColor);
+        }
+    }
+
+    /**
+     * Handle keypresses.
+     *
+     * @param keypress keystroke event
+     * @return true if this keypress was consumed
+     */
+    public boolean statusBarKeypress(final TKeypressEvent keypress) {
+        for (TStatusBarKey key: keys) {
+            if (keypress.equals(key.key)) {
+                getApplication().postMenuEvent(new TCommandEvent(key.cmd));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the button.
+     *
+     * @param statusBarKey the status bar item
+     * @return if true the mouse is currently on the button
+     */
+    private boolean mouseOnShortcut(final TStatusBarKey statusBarKey) {
+        if ((mouse != null)
+            && (mouse.getAbsoluteY() == getApplication().getDesktopBottom())
+            && (mouse.getAbsoluteX() >= statusBarKey.x)
+            && (mouse.getAbsoluteX() < statusBarKey.x + statusBarKey.width())
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse button presses.
+     *
+     * @param mouse mouse button event
+     * @return true if this mouse event was consumed
+     */
+    public boolean statusBarMouseDown(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if ((mouseOnShortcut(key)) && (mouse.isMouse1())) {
+                key.selected = true;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse button releases.
+     *
+     * @param mouse mouse button release event
+     * @return true if this mouse event was consumed
+     */
+    public boolean statusBarMouseUp(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if (key.selected && mouse.isMouse1()) {
+                key.selected = false;
+
+                // Dispatch the event
+                getApplication().postMenuEvent(new TCommandEvent(key.cmd));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Handle mouse movements.
+     *
+     * @param mouse mouse motion event
+     */
+    public void statusBarMouseMotion(final TMouseEvent mouse) {
+        this.mouse = mouse;
+
+        for (TStatusBarKey key: keys) {
+            if (!mouseOnShortcut(key)) {
+                key.selected = false;
+            }
+        }
+    }
+
+}
index c0af74bafa12ce9046277f0eab0746c7571bee43..50c87d6c6d4db7c1329dca206e1e2e97891565a5 100644 (file)
@@ -166,7 +166,7 @@ public class TTerminalWindow extends TWindow {
                 "script", "-fqe", "/dev/null"
             };
             String [] cmdShellBSD = {
-                "script", "-qe", "-F", "/dev/null"
+                "script", "-q", "-F", "/dev/null"
             };
             // Spawn a shell and pass its I/O to the other constructor.
 
@@ -200,6 +200,9 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
     }
 
     /**
@@ -263,6 +266,9 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
     }
 
     /**
@@ -358,7 +364,8 @@ public class TTerminalWindow extends TWindow {
     /**
      * Handle window close.
      */
-    @Override public void onClose() {
+    @Override
+    public void onClose() {
         emulator.close();
         if (shell != null) {
             terminateShellChildProcess();
@@ -406,6 +413,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The emulator thread has exited, but the shell Process
                     // hasn't figured that out yet.  Do nothing, we will see
@@ -421,6 +430,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The shell is still running, do nothing.
                 }
index 71032ab1944b0915dc9fc3125539be4c265a3f4c..4dfe5a14a890e3b4415232ab75d2d035ab20ded3 100644 (file)
@@ -30,7 +30,7 @@ package jexer;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.LinkedList;
+import java.util.ArrayList;
 
 import jexer.bits.ColorTheme;
 import jexer.event.TCommandEvent;
@@ -49,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
@@ -65,44 +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;
-    }
-
-    /**
-     * 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.
      */
@@ -368,6 +334,28 @@ 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();
+    }
+
+    /**
+     * Get the Screen.
+     *
+     * @return the Screen
+     */
+    public Screen getScreen() {
+        return window.getScreen();
+    }
+
     /**
      * Comparison operator.  For various subclasses it sorts on:
      * <ul>
@@ -492,6 +480,12 @@ public abstract class TWidget implements Comparable<TWidget> {
         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);
 
@@ -533,11 +527,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Default constructor for subclasses.
      */
     protected TWidget() {
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
     }
 
     /**
@@ -574,7 +572,7 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
         parent.addChild(this);
     }
 
@@ -594,7 +592,7 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.enabled = enabled;
         this.parent = parent;
         this.window = parent.window;
-        children = new LinkedList<TWidget>();
+        children = new ArrayList<TWidget>();
         parent.addChild(this);
 
         this.x = x;
@@ -603,6 +601,30 @@ public abstract class TWidget implements Comparable<TWidget> {
         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.
@@ -752,6 +774,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.
      *
@@ -971,28 +1020,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.
index 73026953eaa2aa5ffaa5985f6489d9fb1170e2e8..32d94b928a48add6a82c1caf690d97b3b41c7638 100644 (file)
@@ -48,30 +48,33 @@ import static jexer.TKeypress.*;
  */
 public class TWindow extends TWidget {
 
+    // ------------------------------------------------------------------------
+    // Public constants -------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
-     * Window's parent TApplication.
+     * Window is resizable (default yes).
      */
-    private TApplication application;
+    public static final int RESIZABLE   = 0x01;
 
     /**
-     * Get this TWindow's parent TApplication.
-     *
-     * @return this TWindow's parent TApplication
+     * Window is modal (default no).
      */
-    @Override
-    public final TApplication getApplication() {
-        return application;
-    }
+    public static final int MODAL       = 0x02;
 
     /**
-     * Get the Screen.
-     *
-     * @return the Screen
+     * Window is centered (default no).
      */
-    @Override
-    public final Screen getScreen() {
-        return application.getScreen();
-    }
+    public static final int CENTERED    = 0x04;
+
+    // ------------------------------------------------------------------------
+    // Common window attributes -----------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Window flags.  Note package private access.
+     */
+    int flags = RESIZABLE;
 
     /**
      * Window title.
@@ -96,25 +99,34 @@ public class TWindow extends TWidget {
         this.title = title;
     }
 
-    /**
-     * Window is resizable (default yes).
-     */
-    public static final int RESIZABLE   = 0x01;
+    // ------------------------------------------------------------------------
+    // TApplication integration -----------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
-     * Window is modal (default no).
+     * Window's parent TApplication.
      */
-    public static final int MODAL       = 0x02;
+    private TApplication application;
 
     /**
-     * Window is centered (default no).
+     * Get this TWindow's parent TApplication.
+     *
+     * @return this TWindow's parent TApplication
      */
-    public static final int CENTERED    = 0x04;
+    @Override
+    public final TApplication getApplication() {
+        return application;
+    }
 
     /**
-     * Window flags.
+     * Get the Screen.
+     *
+     * @return the Screen
      */
-    private int flags = RESIZABLE;
+    @Override
+    public final Screen getScreen() {
+        return application.getScreen();
+    }
 
     /**
      * Z order.  Lower number means more in-front.
@@ -181,6 +193,37 @@ public class TWindow extends TWidget {
         return keyboardShortcuts.contains(key);
     }
 
+    /**
+     * A window may have a status bar associated with it.  TApplication will
+     * draw this status bar last, and will also route events to it first
+     * before the window.
+     */
+    protected TStatusBar statusBar = null;
+
+    /**
+     * Get the window's status bar, or null if it does not have one.
+     *
+     * @return the status bar, or null
+     */
+    public TStatusBar getStatusBar() {
+        return statusBar;
+    }
+
+    /**
+     * Set the window's status bar to a new one.
+     *
+     * @param text the status bar text
+     * @return the status bar
+     */
+    public TStatusBar newStatusBar(final String text) {
+        statusBar = new TStatusBar(this, text);
+        return statusBar;
+    }
+
+    // ------------------------------------------------------------------------
+    // Window movement/resizing support ---------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * If true, then the user clicked on the title bar and is moving the
      * window.
@@ -238,6 +281,55 @@ public class TWindow extends TWidget {
         this.maximumWindowWidth = maximumWindowWidth;
     }
 
+    /**
+     * Recenter the window on-screen.
+     */
+    public final void center() {
+        if ((flags & CENTERED) != 0) {
+            if (getWidth() < getScreen().getWidth()) {
+                setX((getScreen().getWidth() - getWidth()) / 2);
+            } else {
+                setX(0);
+            }
+            setY(((application.getDesktopBottom()
+                    - application.getDesktopTop()) - getHeight()) / 2);
+            if (getY() < 0) {
+                setY(0);
+            }
+            setY(getY() + application.getDesktopTop());
+        }
+    }
+
+    /**
+     * Maximize window.
+     */
+    private void maximize() {
+        restoreWindowWidth = getWidth();
+        restoreWindowHeight = getHeight();
+        restoreWindowX = getX();
+        restoreWindowY = getY();
+        setWidth(getScreen().getWidth());
+        setHeight(application.getDesktopBottom() - 1);
+        setX(0);
+        setY(1);
+        maximized = true;
+    }
+
+    /**
+     * Restote (unmaximize) window.
+     */
+    private void restore() {
+        setWidth(restoreWindowWidth);
+        setHeight(restoreWindowHeight);
+        setX(restoreWindowX);
+        setY(restoreWindowY);
+        maximized = false;
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructors -----------------------------------------------------------
+    // ------------------------------------------------------------------------
+
     /**
      * Public constructor.  Window will be located at (0, 0).
      *
@@ -325,24 +417,9 @@ public class TWindow extends TWidget {
         application.addWindow(this);
     }
 
-    /**
-     * Recenter the window on-screen.
-     */
-    public final void center() {
-        if ((flags & CENTERED) != 0) {
-            if (getWidth() < getScreen().getWidth()) {
-                setX((getScreen().getWidth() - getWidth()) / 2);
-            } else {
-                setX(0);
-            }
-            setY(((application.getDesktopBottom()
-                    - application.getDesktopTop()) - getHeight()) / 2);
-            if (getY() < 0) {
-                setY(0);
-            }
-            setY(getY() + application.getDesktopTop());
-        }
-    }
+    // ------------------------------------------------------------------------
+    // General behavior -------------------------------------------------------
+    // ------------------------------------------------------------------------
 
     /**
      * Returns true if this window is modal.
@@ -356,57 +433,6 @@ public class TWindow extends TWidget {
         return true;
     }
 
-    /**
-     * Returns true if the mouse is currently on the close button.
-     *
-     * @return true if mouse is currently on the close button
-     */
-    private boolean mouseOnClose() {
-        if ((mouse != null)
-            && (mouse.getAbsoluteY() == getY())
-            && (mouse.getAbsoluteX() == getX() + 3)
-        ) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the mouse is currently on the maximize/restore button.
-     *
-     * @return true if the mouse is currently on the maximize/restore button
-     */
-    private boolean mouseOnMaximize() {
-        if ((mouse != null)
-            && !isModal()
-            && (mouse.getAbsoluteY() == getY())
-            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
-        ) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the mouse is currently on the resizable lower right
-     * corner.
-     *
-     * @return true if the mouse is currently on the resizable lower right
-     * corner
-     */
-    private boolean mouseOnResize() {
-        if (((flags & RESIZABLE) != 0)
-            && !isModal()
-            && (mouse != null)
-            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
-            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
-                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
-        ) {
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Retrieve the background color.
      *
@@ -491,30 +517,6 @@ public class TWindow extends TWidget {
         }
     }
 
-    /**
-     * Subclasses should override this method to cleanup resources.  This is
-     * called by application.closeWindow().
-     */
-    public void onClose() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when this window gets the
-     * focus, and also by application.addWindow().
-     */
-    public void onFocus() {
-        // Default: do nothing
-    }
-
-    /**
-     * Called by application.switchWindow() when another window gets the
-     * focus.
-     */
-    public void onUnfocus() {
-        // Default: do nothing
-    }
-
     /**
      * Called by TApplication.drawChildren() to render on screen.
      */
@@ -582,6 +584,85 @@ public class TWindow extends TWidget {
         }
     }
 
+    // ------------------------------------------------------------------------
+    // Event handlers ---------------------------------------------------------
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns true if the mouse is currently on the close button.
+     *
+     * @return true if mouse is currently on the close button
+     */
+    private boolean mouseOnClose() {
+        if ((mouse != null)
+            && (mouse.getAbsoluteY() == getY())
+            && (mouse.getAbsoluteX() == getX() + 3)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the maximize/restore button.
+     *
+     * @return true if the mouse is currently on the maximize/restore button
+     */
+    private boolean mouseOnMaximize() {
+        if ((mouse != null)
+            && !isModal()
+            && (mouse.getAbsoluteY() == getY())
+            && (mouse.getAbsoluteX() == getX() + getWidth() - 4)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the mouse is currently on the resizable lower right
+     * corner.
+     *
+     * @return true if the mouse is currently on the resizable lower right
+     * corner
+     */
+    private boolean mouseOnResize() {
+        if (((flags & RESIZABLE) != 0)
+            && !isModal()
+            && (mouse != null)
+            && (mouse.getAbsoluteY() == getY() + getHeight() - 1)
+            && ((mouse.getAbsoluteX() == getX() + getWidth() - 1)
+                || (mouse.getAbsoluteX() == getX() + getWidth() - 2))
+        ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Subclasses should override this method to cleanup resources.  This is
+     * called by application.closeWindow().
+     */
+    public void onClose() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when this window gets the
+     * focus, and also by application.addWindow().
+     */
+    public void onFocus() {
+        // Default: do nothing
+    }
+
+    /**
+     * Called by application.switchWindow() when another window gets the
+     * focus.
+     */
+    public void onUnfocus() {
+        // Default: do nothing
+    }
+
     /**
      * Handle mouse button presses.
      *
@@ -624,36 +705,17 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseDown(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseDown(mouse);
     }
 
-    /**
-     * Maximize window.
-     */
-    private void maximize() {
-        restoreWindowWidth = getWidth();
-        restoreWindowHeight = getHeight();
-        restoreWindowX = getX();
-        restoreWindowY = getY();
-        setWidth(getScreen().getWidth());
-        setHeight(application.getDesktopBottom() - 1);
-        setX(0);
-        setY(1);
-        maximized = true;
-    }
-
-    /**
-     * Restote (unmaximize) window.
-     */
-    private void restore() {
-        setWidth(restoreWindowWidth);
-        setHeight(restoreWindowHeight);
-        setX(restoreWindowX);
-        setY(restoreWindowY);
-        maximized = false;
-    }
-
     /**
      * Handle mouse button releases.
      *
@@ -697,6 +759,13 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarMouseUp(mouse)) {
+                return;
+            }
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseUp(mouse);
     }
@@ -718,6 +787,10 @@ public class TWindow extends TWidget {
             if (getY() < application.getDesktopTop()) {
                 setY(application.getDesktopTop());
             }
+            // Don't go below the status bar
+            if (getY() >= application.getDesktopBottom()) {
+                setY(application.getDesktopBottom() - 1);
+            }
             return;
         }
 
@@ -766,6 +839,11 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            statusBar.statusBarMouseMotion(mouse);
+        }
+
         // I didn't take it, pass it on to my children
         super.onMouseMotion(mouse);
     }
@@ -841,6 +919,13 @@ public class TWindow extends TWidget {
             return;
         }
 
+        // Give the shortcut bar a shot at this.
+        if (statusBar != null) {
+            if (statusBar.statusBarKeypress(keypress)) {
+                return;
+            }
+        }
+
         // These keystrokes will typically not be seen unless a subclass
         // overrides onMenu() due to how TApplication dispatches
         // accelerators.
index 626a6c8979bd81a451fdbf2c7ea5466cd0d42fdd..d3a468f416dd3d2986282f7b53653d097ffc4a90 100644 (file)
@@ -437,6 +437,23 @@ public final class ColorTheme {
         color.setBold(true);
         colors.put("tlist.inactive", color);
 
+        // TStatusBar
+        color = new CellAttributes();
+        color.setForeColor(Color.BLACK);
+        color.setBackColor(Color.WHITE);
+        color.setBold(false);
+        colors.put("tstatusbar.text", color);
+        color = new CellAttributes();
+        color.setForeColor(Color.RED);
+        color.setBackColor(Color.WHITE);
+        color.setBold(false);
+        colors.put("tstatusbar.button", color);
+        color = new CellAttributes();
+        color.setForeColor(Color.WHITE);
+        color.setBackColor(Color.BLUE);
+        color.setBold(false);
+        colors.put("tstatusbar.selected", color);
+
         // TEditor
         color = new CellAttributes();
         color.setForeColor(Color.WHITE);
index cd48fc8225fb193532a9765a9e25084ba6458566..8e7a883dd5815d6b5b39bc6d6a4381f79f8d0f24 100644 (file)
@@ -145,4 +145,5 @@ public final class GraphicsChars {
     public static final char WINDOW_RIGHT_TOP_DOUBLE    = CP437[0xBB];
     public static final char WINDOW_LEFT_BOTTOM_DOUBLE  = CP437[0xC8];
     public static final char WINDOW_RIGHT_BOTTOM_DOUBLE = CP437[0xBC];
+    public static final char VERTICAL_BAR               = CP437[0xB3];
 }
index 9ef561c3b1f64f108c4fa28e24d23dc10e37ef89..22d0cecba038515e649bab96faa060716a907c80 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
@@ -78,6 +80,12 @@ public class DemoCheckboxWindow extends TWindow {
                 }
             }
         );
+
+        statusBar = newStatusBar("Radiobuttons and checkboxes");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
 }
index 5d74d7df94ae7198d83760572edb358c32201eb3..febdb4096b7e27064d9a67a9c710dfa94ea9c2cf 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This is the main "demo" application window.  It makes use of the TTimer,
@@ -75,21 +77,19 @@ public class DemoMainWindow extends TWindow {
     private DemoMainWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it will be
         // centered on screen.
-        super(parent, "Demo Window", 0, 0, 60, 24, flags);
+        super(parent, "Demo Window", 0, 0, 60, 22, flags);
 
         int row = 1;
 
         // Add some widgets
-        if (!isModal()) {
-            addLabel("Message Boxes", 1, row);
-            addButton("&MessageBoxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoMsgBoxWindow(getApplication());
-                    }
+        addLabel("Message Boxes", 1, row);
+        addButton("&MessageBoxes", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoMsgBoxWindow(getApplication());
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         addLabel("Open me as modal", 1, row);
@@ -100,29 +100,26 @@ public class DemoMainWindow extends TWindow {
                 }
             }
         );
-
         row += 2;
 
-        addLabel("Variable-width text field:", 1, row);
-        addField(35, row++, 15, false, "Field text");
-        addLabel("Fixed-width text field:", 1, row);
-        addField(35, row++, 15, true);
-        addLabel("Variable-width password:", 1, row);
-        addPasswordField(35, row++, 15, false);
-        addLabel("Fixed-width password:", 1, row);
-        addPasswordField(35, row++, 15, true, "hunter2");
-        row += 1;
+        addLabel("Text fields", 1, row);
+        addButton("Field&s", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoTextFieldWindow(getApplication());
+                }
+            }
+        );
+        row += 2;
 
-        if (!isModal()) {
-            addLabel("Radio buttons and checkboxes", 1, row);
-            addButton("&Checkboxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoCheckboxWindow(getApplication());
-                    }
+        addLabel("Radio buttons and checkboxes", 1, row);
+        addButton("&Checkboxes", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoCheckboxWindow(getApplication());
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         /*
@@ -137,56 +134,48 @@ public class DemoMainWindow extends TWindow {
         row += 2;
          */
 
-        if (!isModal()) {
-            addLabel("Text areas", 1, row);
-            addButton("&Text", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoTextWindow(getApplication());
-                    }
+        addLabel("Text areas", 1, row);
+        addButton("&Text", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoTextWindow(getApplication());
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Tree views", 1, row);
-            addButton("Tree&View", 35, row,
-                new TAction() {
-                    public void DO() {
-                        try {
-                            new DemoTreeViewWindow(getApplication());
-                        } catch (Exception e) {
-                            e.printStackTrace();
-                        }
+        addLabel("Tree views", 1, row);
+        addButton("Tree&View", 35, row,
+            new TAction() {
+                public void DO() {
+                    try {
+                        new DemoTreeViewWindow(getApplication());
+                    } catch (Exception e) {
+                        e.printStackTrace();
                     }
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Terminal", 1, row);
-            addButton("Termi&nal", 35, row,
-                new TAction() {
-                    public void DO() {
-                        getApplication().openTerminal(0, 0);
-                    }
+        addLabel("Terminal", 1, row);
+        addButton("Termi&nal", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().openTerminal(0, 0);
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
-        if (!isModal()) {
-            addLabel("Color editor", 1, row);
-            addButton("Co&lors", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new TEditColorThemeWindow(getApplication());
-                    }
+        addLabel("Color editor", 1, row);
+        addButton("Co&lors", 35, row,
+            new TAction() {
+                public void DO() {
+                    new TEditColorThemeWindow(getApplication());
                 }
-            );
-        }
+            }
+        );
         row += 2;
 
         progressBar = addProgressBar(1, row, 22, 0);
@@ -205,5 +194,11 @@ public class DemoMainWindow extends TWindow {
                 }
             }
         );
+
+        statusBar = newStatusBar("Demo Main Window");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 }
index a56da43760e3b38176f90e4dc33d034530b70d4a..77bc33199d16208f351ebb16b08ac7ef63a9d206 100644 (file)
@@ -29,6 +29,8 @@
 package jexer.demos;
 
 import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TMessageBox and TInputBox widgets.
@@ -53,7 +55,7 @@ public class DemoMsgBoxWindow extends TWindow {
     DemoMsgBoxWindow(final TApplication parent, final int flags) {
         // Construct a demo window.  X and Y don't matter because it
         // will be centered on screen.
-        super(parent, "Message Boxes", 0, 0, 60, 15, flags);
+        super(parent, "Message Boxes", 0, 0, 60, 16, flags);
 
         int row = 1;
 
@@ -154,6 +156,11 @@ public class DemoMsgBoxWindow extends TWindow {
                 }
             }
         );
+
+        statusBar = newStatusBar("Message boxes");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 }
-
diff --git a/src/jexer/demos/DemoTextFieldWindow.java b/src/jexer/demos/DemoTextFieldWindow.java
new file mode 100644 (file)
index 0000000..cd989b8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2016 Kevin Lamonte
+ *
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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.demos;
+
+import jexer.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
+
+/**
+ * This window demonstates the TField and TPasswordField widgets.
+ */
+public class DemoTextFieldWindow extends TWindow {
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     */
+    DemoTextFieldWindow(final TApplication parent) {
+        this(parent, TWindow.CENTERED | TWindow.RESIZABLE);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
+     */
+    DemoTextFieldWindow(final TApplication parent, final int flags) {
+        // Construct a demo window.  X and Y don't matter because it
+        // will be centered on screen.
+        super(parent, "Text Fields", 0, 0, 60, 10, flags);
+
+        int row = 1;
+
+        addLabel("Variable-width text field:", 1, row);
+        addField(35, row++, 15, false, "Field text");
+        addLabel("Fixed-width text field:", 1, row);
+        addField(35, row++, 15, true);
+        addLabel("Variable-width password:", 1, row);
+        addPasswordField(35, row++, 15, false);
+        addLabel("Fixed-width password:", 1, row);
+        addPasswordField(35, row++, 15, true, "hunter2");
+        row += 1;
+
+        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    getApplication().closeWindow(DemoTextFieldWindow.this);
+                }
+            }
+        );
+
+        statusBar = newStatusBar("Text fields");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
+    }
+}
index a2ab68b0ae34bb6c6cb5d9da01da93405584d952..0ca34b0b5bcd7ebcd37bbb4f33857963c3d08690 100644 (file)
@@ -31,6 +31,8 @@ package jexer.demos;
 import jexer.*;
 import jexer.event.*;
 import jexer.menu.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TText, THScroller, and TVScroller widgets.
@@ -54,6 +56,12 @@ public class DemoTextWindow extends TWindow {
 
         super(parent, title, 0, 0, 44, 20, RESIZABLE);
         textField = addText(text, 1, 1, 40, 16);
+
+        statusBar = newStatusBar("Reflowable text window");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
     /**
index a1f4a6152cc342b60e693a846ae32dc62d1b7430..ad588a916acdca4f8957287fac54d24ba3df3381 100644 (file)
@@ -32,6 +32,8 @@ import java.io.IOException;
 
 import jexer.*;
 import jexer.event.*;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
 
 /**
  * This window demonstates the TTreeView widget.
@@ -55,6 +57,12 @@ public class DemoTreeViewWindow extends TWindow {
         // Load the treeview with "stuff"
         treeView = addTreeView(1, 1, 40, 12);
         new TDirectoryTreeItem(treeView, ".", true);
+
+        statusBar = newStatusBar("Treeview demonstration");
+        statusBar.addShortcutKeypress(kbF1, cmHelp, "Help");
+        statusBar.addShortcutKeypress(kbF2, cmShell, "Shell");
+        statusBar.addShortcutKeypress(kbF3, cmOpen, "Open");
+        statusBar.addShortcutKeypress(kbF10, cmExit, "Exit");
     }
 
     /**
index 3063bc4303ae688ba7466c324367fdcd700d0ddc..f7e7870b2030bf444959325193468dd97c46c571 100644 (file)
@@ -29,6 +29,6 @@
 
 /**
  * This package contains the user-facing I/O, including screen, keyboard, and
- * mouse handling..
+ * mouse handling classes.
  */
 package jexer.io;
index b3cb0ac556aded9743240714e087d738988d638c..a282058189fe0dec5eeda5a8725324879303ec71 100644 (file)
@@ -51,7 +51,7 @@ import jexer.io.TimeoutInputStream;
 import static jexer.TKeypress.*;
 
 /**
- * This implements a complex ANSI ECMA-48/ISO 6429/ANSI X3.64 type consoles,
+ * This implements a complex ECMA-48/ISO 6429/ANSI X3.64 type console,
  * including a scrollback buffer.
  *
  * <p>