From efb7af1f330223bfe9ac67112149d7a3f1b68421 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Mon, 30 Mar 2015 16:48:47 -0400 Subject: [PATCH] window focus events and enable/disable menu items --- README.md | 1 - src/jexer/TApplication.java | 100 +++++++++++++++++++++++++--- src/jexer/TWindow.java | 16 +++++ src/jexer/demos/DemoTextWindow.java | 31 +++++++-- src/jexer/menu/TMenu.java | 60 ++++------------- src/jexer/menu/TMenuItem.java | 37 +++++++--- 6 files changed, 168 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index afb1ca6..31cf523 100644 --- 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 diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 67c3d3d..5156628 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -430,6 +430,11 @@ public class TApplication implements Runnable { */ private Map accelerators; + /** + * All menu items. + */ + private List menuItems; + /** * Windows and widgets pull colors from this ColorTheme. */ @@ -561,6 +566,7 @@ public class TApplication implements Runnable { subMenus = new LinkedList(); timers = new LinkedList(); accelerators = new HashMap(); + menuItems = new ArrayList(); // 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); + } } } diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 97c607e..bfb0901 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -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. */ diff --git a/src/jexer/demos/DemoTextWindow.java b/src/jexer/demos/DemoTextWindow.java index 265c45d..75fd40b 100644 --- a/src/jexer/demos/DemoTextWindow.java +++ b/src/jexer/demos/DemoTextWindow.java @@ -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); + } + +} diff --git a/src/jexer/menu/TMenu.java b/src/jexer/menu/TMenu.java index 99add70..6dfb5fc 100644 --- a/src/jexer/menu/TMenu.java +++ b/src/jexer/menu/TMenu.java @@ -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); } /** diff --git a/src/jexer/menu/TMenuItem.java b/src/jexer/menu/TMenuItem.java index ce57cc2..e3600bc 100644 --- a/src/jexer/menu/TMenuItem.java +++ b/src/jexer/menu/TMenuItem.java @@ -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); -- 2.27.0