window focus events and enable/disable menu items
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 30 Mar 2015 20:48:47 +0000 (16:48 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 30 Mar 2015 20:48:47 +0000 (16:48 -0400)
README.md
src/jexer/TApplication.java
src/jexer/TWindow.java
src/jexer/demos/DemoTextWindow.java
src/jexer/menu/TMenu.java
src/jexer/menu/TMenuItem.java

index afb1ca65d4ff7d8cacc54c9bd04dd5e0f24e46cf..31cf5230e59da7144681a739591ffda2ad4a59be 100644 (file)
--- a/README.md
+++ b/README.md
@@ -177,7 +177,6 @@ Many tasks remain before calling this version 1.0:
 
 0.0.4: NEW STUFF
 
-- Making TMenu keyboard accelerators active/inactive
 - TStatusBar
 - TEditor
 - TWindow
index 67c3d3d50bcc438bd6e469df6d5f2826b983aeda..51566287286b7ef5af238a4ec1f22dae5df08a51 100644 (file)
@@ -430,6 +430,11 @@ public class TApplication implements Runnable {
      */
     private Map<TKeypress, TMenuItem> accelerators;
 
+    /**
+     * All menu items.
+     */
+    private List<TMenuItem> menuItems;
+
     /**
      * Windows and widgets pull colors from this ColorTheme.
      */
@@ -561,6 +566,7 @@ public class TApplication implements Runnable {
         subMenus        = new LinkedList<TMenu>();
         timers          = new LinkedList<TTimer>();
         accelerators    = new HashMap<TKeypress, TMenuItem>();
+        menuItems       = new ArrayList<TMenuItem>();
 
         // Setup the main consumer thread
         primaryEventHandler = new WidgetEventHandler(this, true);
@@ -1075,6 +1081,7 @@ public class TApplication implements Runnable {
         synchronized (windows) {
             int z = window.getZ();
             window.setZ(-1);
+            window.onUnfocus();
             Collections.sort(windows);
             windows.remove(0);
             TWindow activeWindow = null;
@@ -1083,10 +1090,14 @@ public class TApplication implements Runnable {
                     w.setZ(w.getZ() - 1);
                     if (w.getZ() == 0) {
                         w.setActive(true);
+                        w.onFocus();
                         assert (activeWindow == null);
                         activeWindow = w;
                     } else {
-                        w.setActive(false);
+                        if (w.isActive()) {
+                            w.setActive(false);
+                            w.onUnfocus();
+                        }
                     }
                 }
             }
@@ -1152,8 +1163,10 @@ public class TApplication implements Runnable {
             }
             windows.get(activeWindowI).setActive(false);
             windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ());
+            windows.get(activeWindowI).onUnfocus();
             windows.get(nextWindowI).setZ(0);
             windows.get(nextWindowI).setActive(true);
+            windows.get(nextWindowI).onFocus();
 
         } // synchronized (windows)
 
@@ -1171,12 +1184,16 @@ public class TApplication implements Runnable {
                 assert (window.isModal());
             }
             for (TWindow w: windows) {
-                w.setActive(false);
+                if (w.isActive()) {
+                    w.setActive(false);
+                    w.onUnfocus();
+                }
                 w.setZ(w.getZ() + 1);
             }
             windows.add(window);
-            window.setActive(true);
             window.setZ(0);
+            window.setActive(true);
+            window.onFocus();
         }
     }
 
@@ -1320,10 +1337,12 @@ public class TApplication implements Runnable {
                     // We will be switching to another window
                     assert (windows.get(0).isActive());
                     assert (!window.isActive());
+                    windows.get(0).onUnfocus();
                     windows.get(0).setActive(false);
                     windows.get(0).setZ(window.getZ());
                     window.setZ(0);
                     window.setActive(true);
+                    window.onFocus();
                     return;
                 }
             }
@@ -1500,17 +1519,76 @@ public class TApplication implements Runnable {
     }
 
     /**
-     * Add a keyboard accelerator to the global hash.
+     * Add a menu item to the global list.  If it has a keyboard accelerator,
+     * that will be added the global hash.
      *
-     * @param item menu item this accelerator relates to
-     * @param keypress keypress that will dispatch a TMenuEvent
+     * @param item the menu item
      */
-    public final void addAccelerator(final TMenuItem item,
-        final TKeypress keypress) {
+    public final void addMenuItem(final TMenuItem item) {
+        menuItems.add(item);
+
+        TKeypress key = item.getKey();
+        if (key != null) {
+            synchronized (accelerators) {
+                assert (accelerators.get(key) == null);
+                accelerators.put(key.toLowerCase(), item);
+            }
+        }
+    }
+
+    /**
+     * Disable one menu item.
+     *
+     * @param id the menu item ID
+     */
+    public final void disableMenuItem(final int id) {
+        for (TMenuItem item: menuItems) {
+            if (item.getId() == id) {
+                item.setEnabled(false);
+            }
+        }
+    }
 
-        synchronized (accelerators) {
-            assert (accelerators.get(keypress) == null);
-            accelerators.put(keypress, item);
+    /**
+     * Disable the range of menu items with ID's between lower and upper,
+     * inclusive.
+     *
+     * @param lower the lowest menu item ID
+     * @param upper the highest menu item ID
+     */
+    public final void disableMenuItems(final int lower, final int upper) {
+        for (TMenuItem item: menuItems) {
+            if ((item.getId() >= lower) && (item.getId() <= upper)) {
+                item.setEnabled(false);
+            }
+        }
+    }
+
+    /**
+     * Enable one menu item.
+     *
+     * @param id the menu item ID
+     */
+    public final void enableMenuItem(final int id) {
+        for (TMenuItem item: menuItems) {
+            if (item.getId() == id) {
+                item.setEnabled(true);
+            }
+        }
+    }
+
+    /**
+     * Enable the range of menu items with ID's between lower and upper,
+     * inclusive.
+     *
+     * @param lower the lowest menu item ID
+     * @param upper the highest menu item ID
+     */
+    public final void enableMenuItems(final int lower, final int upper) {
+        for (TMenuItem item: menuItems) {
+            if ((item.getId() >= lower) && (item.getId() <= upper)) {
+                item.setEnabled(true);
+            }
         }
     }
 
index 97c607e42f8e687537c82154489f77e0d440bb8f..bfb09017f75fd615aa4422e2c56c0411bb7ee997 100644 (file)
@@ -457,6 +457,22 @@ public class TWindow extends TWidget {
         // 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.
      */
index 265c45db7f6f73ae1d8666f78d1772a2b519b6b5..75fd40bd47f69cb969970ea6af7f90bb3b49b1bc 100644 (file)
@@ -32,6 +32,7 @@ package jexer.demos;
 
 import jexer.*;
 import jexer.event.*;
+import jexer.menu.*;
 
 /**
  * This window demonstates the TText, THScroller, and TVScroller widgets.
@@ -56,7 +57,7 @@ public class DemoTextWindow extends TWindow {
         super(parent, title, 0, 0, 44, 20, RESIZABLE);
         textField = addText(text, 1, 1, 40, 16);
     }
-    
+
     /**
      * Public constructor.
      *
@@ -66,6 +67,8 @@ public class DemoTextWindow extends TWindow {
         this(parent, "Text Area",
 "This is an example of a reflowable text field.  Some example text follows.\n" +
 "\n" +
+"Notice that some menu items should be disabled when this window has focus.\n" +
+"\n" +
 "This library implements a text-based windowing system loosely\n" +
 "reminiscient of Borland's [Turbo\n" +
 "Vision](http://en.wikipedia.org/wiki/Turbo_Vision) library.  For those\n" +
@@ -73,11 +76,6 @@ public class DemoTextWindow extends TWindow {
 "Sigala's updated version](http://tvision.sourceforge.net/) that runs\n" +
 "on many more platforms.\n" +
 "\n" +
-"Currently the only console platform supported is Posix (tested on\n" +
-"Linux).  Input/output is handled through terminal escape sequences\n" +
-"generated by the library itself: ncurses is not required or linked to. \n" +
-"xterm mouse tracking using UTF8 coordinates is supported.\n" +
-"\n" +
 "This library is licensed LGPL (\"GNU Lesser General Public License\")\n" +
 "version 3 or greater.  See the file COPYING for the full license text,\n" +
 "which includes both the GPL v3 and the LGPL supplemental terms.\n" +
@@ -105,5 +103,24 @@ public class DemoTextWindow extends TWindow {
             widget.onResize(event);
         }
     }
-}
 
+    /**
+     * Play with menu items.
+     */
+    public void onFocus() {
+        getApplication().enableMenuItem(2001);
+        getApplication().disableMenuItem(TMenu.MID_SHELL);
+        getApplication().disableMenuItem(TMenu.MID_EXIT);
+    }
+
+    /**
+     * Called by application.switchWindow() when another window gets the
+     * focus.
+     */
+    public void onUnfocus() {
+        getApplication().disableMenuItem(2001);
+        getApplication().enableMenuItem(TMenu.MID_SHELL);
+        getApplication().enableMenuItem(TMenu.MID_EXIT);
+    }
+
+}
index 99add703c18fe2d9e59d241bb325b549c16638f1..6dfb5fcfabe71f2f1d961572c0a877045b0e85a2 100644 (file)
@@ -309,18 +309,15 @@ public final class TMenu extends TWindow {
     }
 
     /**
-     * Convenience function to add a custom menu item.
+     * Convenience function to add a menu item.
      *
      * @param id menu item ID.  Must be greater than 1024.
      * @param label menu item label
-     * @param key global keyboard accelerator
      * @return the new menu item
      */
-    public final TMenuItem addItem(final int id, final String label,
-        final TKeypress key) {
-
+    public final TMenuItem addItem(final int id, final String label) {
         assert (id >= 1024);
-        return addItemInternal(id, label, key);
+        return addItemInternal(id, label, null);
     }
 
     /**
@@ -331,51 +328,29 @@ public final class TMenu extends TWindow {
      * @param key global keyboard accelerator
      * @return the new menu item
      */
-    private TMenuItem addItemInternal(final int id, final String label,
+    public final TMenuItem addItem(final int id, final String label,
         final TKeypress key) {
 
-        int newY = getChildren().size() + 1;
-        assert (newY < getHeight());
-
-        TMenuItem menuItem = new TMenuItem(this, id, 1, newY, label);
-        menuItem.setKey(key);
-        setHeight(getHeight() + 1);
-        if (menuItem.getWidth() + 2 > getWidth()) {
-            setWidth(menuItem.getWidth() + 2);
-        }
-        for (TWidget widget: getChildren()) {
-            widget.setWidth(getWidth() - 2);
-        }
-        getApplication().addAccelerator(menuItem, key.toLowerCase());
-        getApplication().recomputeMenuX();
-        activate(0);
-        return menuItem;
+        assert (id >= 1024);
+        return addItemInternal(id, label, key);
     }
 
     /**
-     * Convenience function to add a menu item.
+     * Convenience function to add a custom menu item.
      *
      * @param id menu item ID.  Must be greater than 1024.
      * @param label menu item label
+     * @param key global keyboard accelerator
      * @return the new menu item
      */
-    public final TMenuItem addItem(final int id, final String label) {
-        assert (id >= 1024);
-        return addItemInternal(id, label);
-    }
+    private TMenuItem addItemInternal(final int id, final String label,
+        final TKeypress key) {
 
-    /**
-     * Convenience function to add a menu item.
-     *
-     * @param id menu item ID
-     * @param label menu item label
-     * @return the new menu item
-     */
-    private TMenuItem addItemInternal(final int id, final String label) {
         int newY = getChildren().size() + 1;
         assert (newY < getHeight());
 
         TMenuItem menuItem = new TMenuItem(this, id, 1, newY, label);
+        menuItem.setKey(key);
         setHeight(getHeight() + 1);
         if (menuItem.getWidth() + 2 > getWidth()) {
             setWidth(menuItem.getWidth() + 2);
@@ -383,6 +358,7 @@ public final class TMenu extends TWindow {
         for (TWidget widget: getChildren()) {
             widget.setWidth(getWidth() - 2);
         }
+        getApplication().addMenuItem(menuItem);
         getApplication().recomputeMenuX();
         activate(0);
         return menuItem;
@@ -401,7 +377,6 @@ public final class TMenu extends TWindow {
 
         String label;
         TKeypress key = null;
-        boolean hasKey = true;
 
         switch (id) {
 
@@ -412,7 +387,6 @@ public final class TMenu extends TWindow {
 
         case MID_SHELL:
             label = "O&S Shell";
-            hasKey = false;
             break;
 
         case MID_OPEN_FILE:
@@ -434,21 +408,17 @@ public final class TMenu extends TWindow {
             break;
         case MID_CLEAR:
             label = "C&lear";
-            hasKey = false;
             // key = kbDel;
             break;
 
         case MID_TILE:
             label = "&Tile";
-            hasKey = false;
             break;
         case MID_CASCADE:
             label = "C&ascade";
-            hasKey = false;
             break;
         case MID_CLOSE_ALL:
             label = "Cl&ose All";
-            hasKey = false;
             break;
         case MID_WINDOW_MOVE:
             label = "&Size/Move";
@@ -468,7 +438,6 @@ public final class TMenu extends TWindow {
             break;
         case MID_WINDOW_CLOSE:
             label = "&Close";
-            hasKey = false;
             // key = kbCtrlW;
             break;
 
@@ -476,10 +445,7 @@ public final class TMenu extends TWindow {
             throw new IllegalArgumentException("Invalid menu ID: " + id);
         }
 
-        if (hasKey) {
-            return addItemInternal(id, label, key);
-        }
-        return addItemInternal(id, label);
+        return addItemInternal(id, label, key);
     }
 
     /**
index ce57cc24e2c1095acefecb966c8e8ec440ac278e..e3600bca4f9c79a2cd9ca2ef1f5460cca7cea999 100644 (file)
@@ -56,6 +56,15 @@ public class TMenuItem extends TWidget {
      */
     private int id = TMenu.MID_UNUSED;
 
+    /**
+     * Get the menu item ID.
+     *
+     * @return the id
+     */
+    public final int getId() {
+        return id;
+    }
+
     /**
      * When true, this item can be checked or unchecked.
      */
@@ -69,7 +78,7 @@ public class TMenuItem extends TWidget {
     public final void setCheckable(final boolean checkable) {
         this.checkable = checkable;
     }
-    
+
     /**
      * When true, this item is checked.
      */
@@ -80,11 +89,6 @@ public class TMenuItem extends TWidget {
      */
     private TKeypress key;
 
-    /**
-     * When true, a global accelerator can be used to select this item.
-     */
-    private boolean hasKey = false;
-
     /**
      * The title string.  Use '&' to specify a mnemonic, i.e. "&File" will
      * highlight the 'F' and allow 'f' or 'F' to select it.
@@ -100,18 +104,29 @@ public class TMenuItem extends TWidget {
         return mnemonic;
     }
 
+    /**
+     * Get a global accelerator key for this menu item.
+     *
+     * @return global keyboard accelerator, or null if no key is associated
+     * with this item
+     */
+    public final TKeypress getKey() {
+        return key;
+    }
+
     /**
      * Set a global accelerator key for this menu item.
      *
      * @param key global keyboard accelerator
      */
     public final void setKey(final TKeypress key) {
-        hasKey = true;
         this.key = key;
 
-        int newWidth = (label.length() + 4 + key.toString().length() + 2);
-        if (newWidth > getWidth()) {
-            setWidth(newWidth);
+        if (key != null) {
+            int newWidth = (label.length() + 4 + key.toString().length() + 2);
+            if (newWidth > getWidth()) {
+                setWidth(newWidth);
+            }
         }
     }
 
@@ -219,7 +234,7 @@ public class TMenuItem extends TWidget {
 
         getScreen().hLineXY(1, 0, getWidth() - 2, ' ', menuColor);
         getScreen().putStringXY(2, 0, mnemonic.getRawLabel(), menuColor);
-        if (hasKey) {
+        if (key != null) {
             String keyLabel = key.toString();
             getScreen().putStringXY((getWidth() - keyLabel.length() - 2), 0,
                 keyLabel, menuColor);