From: Kevin Lamonte Date: Sat, 15 Jul 2017 13:29:27 +0000 (-0400) Subject: #14 TDesktop bug fixes, more TWindow API X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=9245321388306b5b49d6385ce2f46ea6a82ab619;p=nikiroo-utils.git #14 TDesktop bug fixes, more TWindow API --- diff --git a/README.md b/README.md index 161b8e6..6bf20a9 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,11 @@ Jexer currently supports three backends: TCP socket. jexer.demos.Demo3 demonstrates how one might use a 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 80x25, which - is set in jexer.session.SwingSession. For the demo application, - this is the default backend on Windows and Mac platforms. +* Java Swing UI. The default window size for Swing is 80x25, which is + set in jexer.session.SwingSession. For the demo applications, this + is the default backend on Windows and Mac platforms. This backend + can be explicitly selected for the demo applications by setting + jexer.Swing=true. Additional backends can be created by subclassing jexer.backend.Backend and passing it into the TApplication @@ -125,12 +126,12 @@ it and you'll see an application like this: ![The Example Code Above](/screenshots/readme_application.png?raw=true "The application in the text of README.md") See the files in jexer.demos for many more detailed examples showing -all of the existing UI controls. The demo can be run in three -different ways: +all of the existing UI controls. The available demos can be run as +follows: * 'java -jar jexer.jar' . This will use System.in/out with - xterm-like sequences on non-Windows platforms. On Windows it will - use a Swing JFrame. + xterm-like sequences on non-Windows non-Mac platforms. On Windows + and Mac it will use a Swing JFrame. * 'java -Djexer.Swing=true -jar jexer.jar' . This will always use Swing on any platform. @@ -140,6 +141,14 @@ different ways: protocol to establish an 8-bit clean channel and be aware of screen size changes. + * 'java -cp jexer.jar jexer.demos.Demo3' . This will use + System.in/out with xterm-like sequences. One can see in the code + how to pass a different InputReader and OutputReader to + TApplication, permitting a different encoding than UTF-8. + + * 'java -cp jexer.jar jexer.demos.Demo4' . This demonstrates hidden + windows and a custom TDesktop. + More Screenshots @@ -159,9 +168,10 @@ The following properties control features of Jexer: jexer.Swing ----------- - Used only by jexer.demos.Demo1. If true, use the Swing interface - for the demo application. Default: true on Windows platforms - (os.name starts with "Windows"), false on non-Windows platforms. + Used only by jexer.demos.Demo1 and jexer.demos.Demo4. If true, use + the Swing interface for the demo application. Default: true on + Windows (os.name starts with "Windows") and Mac (os.name starts with + "Mac"), false on non-Windows and non-Mac platforms. jexer.Swing.cursorStyle ----------------------- @@ -172,9 +182,11 @@ The following properties control features of Jexer: jexer.Swing.tripleBuffer ------------------------ - Used by jexer.io.SwingScreen. If false, use naive Swing thread - drawing. This may be faster on slower systems, but will also be - more likely to have screen tearing. Default: true. + Used by jexer.io.SwingScreen. If true, use triple-buffering which + reduces screen tearing but may also be slower to draw on slower + systems. If false, use naive Swing thread drawing, which may be + faster on slower systems but also more likely to have screen + tearing. Default: true. @@ -218,7 +230,7 @@ ambiguous. This section describes such issues. - jexer.io.ECMA48Terminal calls 'stty' to perform the equivalent of cfmakeraw() when using System.in/out. System.out is also - (blindly!) put in 'stty sane cooked' mode when exiting. + (blindly!) put in 'stty sane cooked' mode when exiting. diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 4b0efa9..d8bec5e 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -449,7 +449,7 @@ public class TApplication implements Runnable { private List subMenus; /** - * The currently acive menu. + * The currently active menu. */ private TMenu activeMenu = null; @@ -482,6 +482,11 @@ public class TApplication implements Runnable { */ private List windows; + /** + * The currently acive window. + */ + private TWindow activeWindow = null; + /** * Timers that are being ticked. */ @@ -555,6 +560,26 @@ public class TApplication implements Runnable { return desktop; } + /** + * Get the current active window. + * + * @return the active window, or null if it is not set + */ + public final TWindow getActiveWindow() { + return activeWindow; + } + + /** + * Get the list of windows. + * + * @return a copy of the list of windows for this application + */ + public final List getAllWindows() { + List result = new LinkedList(); + result.addAll(windows); + return result; + } + // ------------------------------------------------------------------------ // General behavior ------------------------------------------------------- // ------------------------------------------------------------------------ @@ -757,7 +782,9 @@ public class TApplication implements Runnable { } Collections.reverse(sorted); for (TWindow window: sorted) { - window.drawChildren(); + if (window.isShown()) { + window.drawChildren(); + } } // Draw the blank menubar line - reset the screen clipping first so @@ -857,7 +884,7 @@ public class TApplication implements Runnable { while (!quit) { // Timeout is in milliseconds, so default timeout after 1 second // of inactivity. - long timeout = 0; + long timeout = 1000; // If I've got no updates to render, wait for something from the // backend or a timer. @@ -1078,13 +1105,12 @@ public class TApplication implements Runnable { // shortcutted by the active window, and if so dispatch the menu // event. boolean windowWillShortcut = false; - for (TWindow window: windows) { - if (window.isActive()) { - if (window.isShortcutKeypress(keypress.getKey())) { - // We do not process this key, it will be passed to - // the window instead. - windowWillShortcut = true; - } + if (activeWindow != null) { + assert (activeWindow.isShown()); + if (activeWindow.isShortcutKeypress(keypress.getKey())) { + // We do not process this key, it will be passed to the + // window instead. + windowWillShortcut = true; } } @@ -1123,30 +1149,30 @@ public class TApplication implements Runnable { // Dispatch events to the active window ------------------------------- boolean dispatchToDesktop = true; - for (TWindow window: windows) { - if (window.isActive()) { - if (event instanceof TMouseEvent) { - TMouseEvent mouse = (TMouseEvent) event; - // Convert the mouse relative x/y to window coordinates - assert (mouse.getX() == mouse.getAbsoluteX()); - assert (mouse.getY() == mouse.getAbsoluteY()); - mouse.setX(mouse.getX() - window.getX()); - mouse.setY(mouse.getY() - window.getY()); - - if (window.mouseWouldHit(mouse)) { - dispatchToDesktop = false; - } - } else if (event instanceof TKeypressEvent) { + TWindow window = activeWindow; + if (window != null) { + assert (window.isActive()); + assert (window.isShown()); + if (event instanceof TMouseEvent) { + TMouseEvent mouse = (TMouseEvent) event; + // Convert the mouse relative x/y to window coordinates + assert (mouse.getX() == mouse.getAbsoluteX()); + assert (mouse.getY() == mouse.getAbsoluteY()); + mouse.setX(mouse.getX() - window.getX()); + mouse.setY(mouse.getY() - window.getY()); + + if (window.mouseWouldHit(mouse)) { dispatchToDesktop = false; } + } else if (event instanceof TKeypressEvent) { + dispatchToDesktop = false; + } - if (debugEvents) { - System.err.printf("TApplication dispatch event: %s\n", - event); - } - window.handleEvent(event); - break; + if (debugEvents) { + System.err.printf("TApplication dispatch event: %s\n", + event); } + window.handleEvent(event); } if (dispatchToDesktop) { // This event is fair game for the desktop to process. @@ -1233,12 +1259,170 @@ public class TApplication implements Runnable { for (TWindow window: windows) { window.onIdle(); } + if (desktop != null) { + desktop.onIdle(); + } } // ------------------------------------------------------------------------ // TWindow management ----------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Return the total number of windows. + * + * @return the total number of windows + */ + public final int windowCount() { + return windows.size(); + } + + /** + * Return the number of windows that are visible. + * + * @return the number of windows that are visible + */ + public final int shownWindowCount() { + int n = 0; + for (TWindow w: windows) { + if (w.isShown()) { + n++; + } + } + return n; + } + + /** + * Check if a window instance is in this application's window list. + * + * @param window window to look for + * @return true if this window is in the list + */ + public final boolean hasWindow(final TWindow window) { + if (windows.size() == 0) { + return false; + } + for (TWindow w: windows) { + if (w == window) { + return true; + } + } + return false; + } + + /** + * Activate a window: bring it to the top and have it receive events. + * + * @param window the window to become the new active window + */ + public void activateWindow(final TWindow window) { + if (hasWindow(window) == false) { + /* + * Someone has a handle to a window I don't have. Ignore this + * request. + */ + return; + } + + assert (windows.size() > 0); + + if (window.isHidden()) { + // Unhiding will also activate. + showWindow(window); + return; + } + assert (window.isShown()); + + if (windows.size() == 1) { + assert (window == windows.get(0)); + if (activeWindow == null) { + activeWindow = window; + window.setZ(0); + activeWindow.setActive(true); + activeWindow.onFocus(); + } + + assert (window.isActive()); + assert (activeWindow == window); + return; + } + + if (activeWindow == window) { + assert (window.isActive()); + + // Window is already active, do nothing. + return; + } + + assert (!window.isActive()); + if (activeWindow != null) { + assert (activeWindow.getZ() == 0); + + activeWindow.onUnfocus(); + activeWindow.setActive(false); + activeWindow.setZ(window.getZ()); + } + activeWindow = window; + activeWindow.setZ(0); + activeWindow.setActive(true); + activeWindow.onFocus(); + return; + } + + /** + * Hide a window. + * + * @param window the window to hide + */ + public void hideWindow(final TWindow window) { + if (hasWindow(window) == false) { + /* + * Someone has a handle to a window I don't have. Ignore this + * request. + */ + return; + } + + assert (windows.size() > 0); + + if (!window.hidden) { + if (window == activeWindow) { + if (shownWindowCount() > 1) { + switchWindow(true); + } else { + activeWindow = null; + window.setActive(false); + window.onUnfocus(); + } + } + window.hidden = true; + window.onHide(); + } + } + + /** + * Show a window. + * + * @param window the window to show + */ + public void showWindow(final TWindow window) { + if (hasWindow(window) == false) { + /* + * Someone has a handle to a window I don't have. Ignore this + * request. + */ + return; + } + + assert (windows.size() > 0); + + if (window.hidden) { + window.hidden = false; + window.onShow(); + activateWindow(window); + } + } + /** * Close window. Note that the window's destructor is NOT called by this * method, instead the GC is assumed to do the cleanup. @@ -1246,13 +1430,21 @@ public class TApplication implements Runnable { * @param window the window to remove */ public final void closeWindow(final TWindow window) { + if (hasWindow(window) == false) { + /* + * Someone has a handle to a window I don't have. Ignore this + * request. + */ + return; + } + synchronized (windows) { int z = window.getZ(); window.setZ(-1); window.onUnfocus(); Collections.sort(windows); windows.remove(0); - TWindow activeWindow = null; + activeWindow = null; for (TWindow w: windows) { if (w.getZ() > z) { w.setZ(w.getZ() - 1); @@ -1288,6 +1480,13 @@ public class TApplication implements Runnable { secondaryEventHandler.notify(); } } + + // Permit desktop to be active if it is the only thing left. + if (desktop != null) { + if (windows.size() == 0) { + desktop.setActive(true); + } + } } /** @@ -1301,21 +1500,25 @@ public class TApplication implements Runnable { if (windows.size() < 2) { return; } + assert (activeWindow != null); synchronized (windows) { // Swap z/active between active window and the next in the list int activeWindowI = -1; for (int i = 0; i < windows.size(); i++) { - if (windows.get(i).isActive()) { + if (windows.get(i) == activeWindow) { + assert (activeWindow.isActive()); activeWindowI = i; break; + } else { + assert (!windows.get(0).isActive()); } } assert (activeWindowI >= 0); // Do not switch if a window is modal - if (windows.get(activeWindowI).isModal()) { + if (activeWindow.isModal()) { return; } @@ -1329,13 +1532,8 @@ public class TApplication implements Runnable { nextWindowI = activeWindowI - 1; } } - 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(); + activateWindow(windows.get(nextWindowI)); } // synchronized (windows) } @@ -1364,18 +1562,24 @@ public class TApplication implements Runnable { if (modalWindowActive()) { window.flags |= TWindow.MODAL; window.flags |= TWindow.CENTERED; + window.hidden = false; } - for (TWindow w: windows) { - if (w.isActive()) { - w.setActive(false); - w.onUnfocus(); + if (window.isShown()) { + for (TWindow w: windows) { + if (w.isActive()) { + w.setActive(false); + w.onUnfocus(); + } + w.setZ(w.getZ() + 1); } - w.setZ(w.getZ() + 1); } windows.add(window); - window.setZ(0); - window.setActive(true); - window.onFocus(); + if (window.isShown()) { + activeWindow = window; + activeWindow.setZ(0); + activeWindow.setActive(true); + activeWindow.onFocus(); + } if (((window.flags & TWindow.CENTERED) == 0) && smartWindowPlacement) { @@ -1383,6 +1587,11 @@ public class TApplication implements Runnable { doSmartPlacement(window); } } + + // Desktop cannot be active over any other window. + if (desktop != null) { + desktop.setActive(false); + } } /** @@ -1751,18 +1960,27 @@ public class TApplication implements Runnable { for (TWindow window: windows) { assert (!window.isModal()); + + if (window.isHidden()) { + assert (!window.isActive()); + continue; + } + if (window.mouseWouldHit(mouse)) { if (window == windows.get(0)) { // Clicked on the same window, nothing to do + assert (window.isActive()); return; } // We will be switching to another window assert (windows.get(0).isActive()); + assert (windows.get(0) == activeWindow); assert (!window.isActive()); - windows.get(0).onUnfocus(); - windows.get(0).setActive(false); - windows.get(0).setZ(window.getZ()); + activeWindow.onUnfocus(); + activeWindow.setActive(false); + activeWindow.setZ(window.getZ()); + activeWindow = window; window.setZ(0); window.setActive(true); window.onFocus(); @@ -2321,4 +2539,69 @@ public class TApplication implements Runnable { return box.getFilename(); } + /** + * Convenience function to create a new window and make it active. + * Window will be located at (0, 0). + * + * @param title window title, will be centered along the top border + * @param width width of window + * @param height height of window + */ + public final TWindow addWindow(final String title, final int width, + final int height) { + + TWindow window = new TWindow(this, title, 0, 0, width, height); + return window; + } + /** + * Convenience function to create a new window and make it active. + * Window will be located at (0, 0). + * + * @param title window title, will be centered along the top border + * @param width width of window + * @param height height of window + * @param flags bitmask of RESIZABLE, CENTERED, or MODAL + */ + public final TWindow addWindow(final String title, + final int width, final int height, final int flags) { + + TWindow window = new TWindow(this, title, 0, 0, width, height, flags); + return window; + } + + /** + * Convenience function to create a new window and make it active. + * + * @param title window title, will be centered along the top border + * @param x column relative to parent + * @param y row relative to parent + * @param width width of window + * @param height height of window + */ + public final TWindow addWindow(final String title, + final int x, final int y, final int width, final int height) { + + TWindow window = new TWindow(this, title, x, y, width, height); + return window; + } + + /** + * Convenience function to create a new window and make it active. + * + * @param application TApplication that manages this window + * @param title window title, will be centered along the top border + * @param x column relative to parent + * @param y row relative to parent + * @param width width of window + * @param height height of window + * @param flags mask of RESIZABLE, CENTERED, or MODAL + */ + public final TWindow addWindow(final String title, + final int x, final int y, final int width, final int height, + final int flags) { + + TWindow window = new TWindow(this, title, x, y, width, height, flags); + return window; + } + } diff --git a/src/jexer/TDesktop.java b/src/jexer/TDesktop.java index 68e394e..07a29a3 100644 --- a/src/jexer/TDesktop.java +++ b/src/jexer/TDesktop.java @@ -37,7 +37,18 @@ import jexer.event.TResizeEvent; /** * TDesktop is a special-class window that is drawn underneath everything - * else. + * else. Like a TWindow, it can contain widgets and perform "background" + * processing via onIdle(). But unlike a TWindow, it cannot be hidden, + * moved, or resized. + * + *

+ * Events are passed to TDesktop as follows: + *

    + *
  • Mouse events are seen if they do not cover any other windows.
  • + *
  • Keypress events are seen if no other windows are open.
  • + *
  • Menu events are seen if no other windows are open.
  • + *
  • Command events are seen if no other windows are open.
  • + *
      */ public class TDesktop extends TWindow { @@ -63,6 +74,62 @@ public class TDesktop extends TWindow { putAll(GraphicsChars.HATCH, background); } + /** + * Hide window. This is a NOP for TDesktop. + */ + @Override + public final void hide() {} + + /** + * Show window. This is a NOP for TDesktop. + */ + @Override + public final void show() {} + + /** + * Called by hide(). This is a NOP for TDesktop. + */ + @Override + public final void onHide() {} + + /** + * Called by show(). This is a NOP for TDesktop. + */ + @Override + public final void onShow() {} + + /** + * Returns true if the mouse is currently on the close button. + * + * @return true if mouse is currently on the close button + */ + @Override + protected final boolean mouseOnClose() { + 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 + */ + @Override + protected final boolean mouseOnMaximize() { + 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 + */ + @Override + protected final boolean mouseOnResize() { + return false; + } + /** * Handle mouse button presses. * diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index afb0bc0..cca51d6 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -303,7 +303,11 @@ public class TWindow extends TWidget { /** * Maximize window. */ - private void maximize() { + public void maximize() { + if (maximized) { + return; + } + restoreWindowWidth = getWidth(); restoreWindowHeight = getHeight(); restoreWindowX = getX(); @@ -318,7 +322,11 @@ public class TWindow extends TWidget { /** * Restore (unmaximize) window. */ - private void restore() { + public void restore() { + if (!maximized) { + return; + } + setWidth(restoreWindowWidth); setHeight(restoreWindowHeight); setX(restoreWindowX); @@ -326,6 +334,59 @@ public class TWindow extends TWidget { maximized = false; } + // ------------------------------------------------------------------------ + // Window visibility ------------------------------------------------------ + // ------------------------------------------------------------------------ + + /** + * Hidden flag. A hidden window will still have its onIdle() called, and + * will also have onClose() called at application exit. Note package + * private access: TApplication will force hidden false if a modal window + * is active. + */ + boolean hidden = false; + + /** + * Returns true if this window is hidden. + * + * @return true if this window is hidden, false if the window is shown + */ + public final boolean isHidden() { + return hidden; + } + + /** + * Returns true if this window is shown. + * + * @return true if this window is shown, false if the window is hidden + */ + public final boolean isShown() { + return !hidden; + } + + /** + * Hide window. A hidden window will still have its onIdle() called, and + * will also have onClose() called at application exit. Hidden windows + * will not receive any other events. + */ + public void hide() { + application.hideWindow(this); + } + + /** + * Show window. + */ + public void show() { + application.showWindow(this); + } + + /** + * Activate window (bring to top and receive events). + */ + public void activate() { + application.activateWindow(this); + } + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -593,7 +654,7 @@ public class TWindow extends TWidget { * * @return true if mouse is currently on the close button */ - private boolean mouseOnClose() { + protected boolean mouseOnClose() { if ((mouse != null) && (mouse.getAbsoluteY() == getY()) && (mouse.getAbsoluteX() == getX() + 3) @@ -608,7 +669,7 @@ public class TWindow extends TWidget { * * @return true if the mouse is currently on the maximize/restore button */ - private boolean mouseOnMaximize() { + protected boolean mouseOnMaximize() { if ((mouse != null) && !isModal() && (mouse.getAbsoluteY() == getY()) @@ -626,7 +687,7 @@ public class TWindow extends TWidget { * @return true if the mouse is currently on the resizable lower right * corner */ - private boolean mouseOnResize() { + protected boolean mouseOnResize() { if (((flags & RESIZABLE) != 0) && !isModal() && (mouse != null) @@ -663,6 +724,20 @@ public class TWindow extends TWidget { // Default: do nothing } + /** + * Called by application.hideWindow(). + */ + public void onHide() { + // Default: do nothing + } + + /** + * Called by application.showWindow(). + */ + public void onShow() { + // Default: do nothing + } + /** * Handle mouse button presses. * @@ -930,37 +1005,41 @@ public class TWindow extends TWidget { // overrides onMenu() due to how TApplication dispatches // accelerators. - // Ctrl-W - close window - if (keypress.equals(kbCtrlW)) { - application.closeWindow(this); - return; - } + if (!(this instanceof TDesktop)) { - // F6 - behave like Alt-TAB - if (keypress.equals(kbF6)) { - application.switchWindow(true); - return; - } + // Ctrl-W - close window + if (keypress.equals(kbCtrlW)) { + application.closeWindow(this); + return; + } - // Shift-F6 - behave like Shift-Alt-TAB - if (keypress.equals(kbShiftF6)) { - application.switchWindow(false); - return; - } + // F6 - behave like Alt-TAB + if (keypress.equals(kbF6)) { + application.switchWindow(true); + return; + } - // F5 - zoom - if (keypress.equals(kbF5)) { - if (maximized) { - restore(); - } else { - maximize(); + // Shift-F6 - behave like Shift-Alt-TAB + if (keypress.equals(kbShiftF6)) { + application.switchWindow(false); + return; } - } - // Ctrl-F5 - size/move - if (keypress.equals(kbCtrlF5)) { - inKeyboardResize = !inKeyboardResize; - } + // F5 - zoom + if (keypress.equals(kbF5)) { + if (maximized) { + restore(); + } else { + maximize(); + } + } + + // Ctrl-F5 - size/move + if (keypress.equals(kbCtrlF5)) { + inKeyboardResize = !inKeyboardResize; + } + + } // if (!(this instanceof TDesktop)) // I didn't take it, pass it on to my children super.onKeypress(keypress); @@ -978,33 +1057,37 @@ public class TWindow extends TWidget { // overrides onMenu() due to how TApplication dispatches // accelerators. - if (command.equals(cmWindowClose)) { - application.closeWindow(this); - return; - } + if (!(this instanceof TDesktop)) { - if (command.equals(cmWindowNext)) { - application.switchWindow(true); - return; - } + if (command.equals(cmWindowClose)) { + application.closeWindow(this); + return; + } - if (command.equals(cmWindowPrevious)) { - application.switchWindow(false); - return; - } + if (command.equals(cmWindowNext)) { + application.switchWindow(true); + return; + } - if (command.equals(cmWindowMove)) { - inKeyboardResize = true; - return; - } + if (command.equals(cmWindowPrevious)) { + application.switchWindow(false); + return; + } - if (command.equals(cmWindowZoom)) { - if (maximized) { - restore(); - } else { - maximize(); + if (command.equals(cmWindowMove)) { + inKeyboardResize = true; + return; + } + + if (command.equals(cmWindowZoom)) { + if (maximized) { + restore(); + } else { + maximize(); + } } - } + + } // if (!(this instanceof TDesktop)) // I didn't take it, pass it on to my children super.onCommand(command); @@ -1017,34 +1100,39 @@ public class TWindow extends TWidget { */ @Override public void onMenu(final TMenuEvent menu) { - if (menu.getId() == TMenu.MID_WINDOW_CLOSE) { - application.closeWindow(this); - return; - } - if (menu.getId() == TMenu.MID_WINDOW_NEXT) { - application.switchWindow(true); - return; - } + if (!(this instanceof TDesktop)) { - if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) { - application.switchWindow(false); - return; - } + if (menu.getId() == TMenu.MID_WINDOW_CLOSE) { + application.closeWindow(this); + return; + } - if (menu.getId() == TMenu.MID_WINDOW_MOVE) { - inKeyboardResize = true; - return; - } + if (menu.getId() == TMenu.MID_WINDOW_NEXT) { + application.switchWindow(true); + return; + } - if (menu.getId() == TMenu.MID_WINDOW_ZOOM) { - if (maximized) { - restore(); - } else { - maximize(); + if (menu.getId() == TMenu.MID_WINDOW_PREVIOUS) { + application.switchWindow(false); + return; } - return; - } + + if (menu.getId() == TMenu.MID_WINDOW_MOVE) { + inKeyboardResize = true; + return; + } + + if (menu.getId() == TMenu.MID_WINDOW_ZOOM) { + if (maximized) { + restore(); + } else { + maximize(); + } + return; + } + + } // if (!(this instanceof TDesktop)) // I didn't take it, pass it on to my children super.onMenu(menu); diff --git a/src/jexer/demos/DesktopDemoApplication.java b/src/jexer/demos/DesktopDemoApplication.java index 395817d..2a2ca34 100644 --- a/src/jexer/demos/DesktopDemoApplication.java +++ b/src/jexer/demos/DesktopDemoApplication.java @@ -68,6 +68,68 @@ public class DesktopDemoApplication extends TApplication { } } ); + + final TWindow windowA = addWindow("Window A", 20, 14); + final TWindow windowB = addWindow("Window B", 20, 14); + windowA.addButton("&Show Window B", 2, 2, + new TAction() { + public void DO() { + windowB.show(); + } + } + ); + windowA.addButton("H&ide Window B", 2, 4, + new TAction() { + public void DO() { + windowB.hide(); + } + } + ); + windowB.addButton("&Show Window A", 2, 2, + new TAction() { + public void DO() { + windowA.show(); + } + } + ); + windowB.addButton("H&ide Window A", 2, 4, + new TAction() { + public void DO() { + windowA.hide(); + } + } + ); + + desktop.addButton("&Show Window B", 25, 2, + new TAction() { + public void DO() { + windowB.show(); + } + } + ); + desktop.addButton("H&ide Window B", 25, 5, + new TAction() { + public void DO() { + windowB.hide(); + } + } + ); + desktop.addButton("Sh&ow Window A", 25, 8, + new TAction() { + public void DO() { + windowA.show(); + } + } + ); + desktop.addButton("Hid&e Window A", 25, 11, + new TAction() { + public void DO() { + windowA.hide(); + } + } + ); + + } /**