#14 TDesktop bug fixes, more TWindow API
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 15 Jul 2017 13:29:27 +0000 (09:29 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 15 Jul 2017 13:29:27 +0000 (09:29 -0400)
README.md
src/jexer/TApplication.java
src/jexer/TDesktop.java
src/jexer/TWindow.java
src/jexer/demos/DesktopDemoApplication.java

index 161b8e6de0dc4043515367e4f22c4cd540a24fb5..6bf20a962e3e0046a4141b8472ecf0682702d712 100644 (file)
--- 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.
 
 
 
index 4b0efa9412f947a739a651ae802b7e97b68b78d2..d8bec5e5d07b00dd54faca3f0660fbfcf9e4884a 100644 (file)
@@ -449,7 +449,7 @@ public class TApplication implements Runnable {
     private List<TMenu> subMenus;
 
     /**
-     * The currently acive menu.
+     * The currently active menu.
      */
     private TMenu activeMenu = null;
 
@@ -482,6 +482,11 @@ public class TApplication implements Runnable {
      */
     private List<TWindow> 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<TWindow> getAllWindows() {
+        List<TWindow> result = new LinkedList<TWindow>();
+        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;
+    }
+
 }
index 68e394e88a7d5f4035d9c61c794879e6a40984c4..07a29a3397b64847bb0a0d6fa8e439ac11bfa583 100644 (file)
@@ -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.
+ *
+ * <p>
+ * Events are passed to TDesktop as follows:
+ * <ul>
+ * <li>Mouse events are seen if they do not cover any other windows.</li>
+ * <li>Keypress events are seen if no other windows are open.</li>
+ * <li>Menu events are seen if no other windows are open.</li>
+ * <li>Command events are seen if no other windows are open.</li>
+ * <ul>
  */
 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.
      *
index afb0bc049933e426b5542c35283013e36927ef55..cca51d673795aaf1aebbad968af9e42762a5f86e 100644 (file)
@@ -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);
index 395817d073fe541eb18a9cef77cf2ce20b166981..2a2ca34cbf7719cf224c0f7aaf305273beb0ddb8 100644 (file)
@@ -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();
+                }
+            }
+        );
+
+
     }
 
     /**