double buffer swing
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sun, 22 Mar 2015 22:25:14 +0000 (18:25 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sun, 22 Mar 2015 22:25:14 +0000 (18:25 -0400)
README.md
src/jexer/TApplication.java
src/jexer/backend/SwingBackend.java
src/jexer/demos/Demo1.java
src/jexer/demos/DemoApplication.java [new file with mode: 0644]
src/jexer/demos/DemoCheckboxWindow.java [new file with mode: 0644]
src/jexer/demos/DemoMainWindow.java [new file with mode: 0644]
src/jexer/demos/DemoMsgBoxWindow.java [new file with mode: 0644]
src/jexer/demos/DemoTextWindow.java [new file with mode: 0644]
src/jexer/io/SwingScreen.java
src/jexer/io/SwingTerminal.java

index 1e1bb544bbaa83f05eda45babc25eef10c57b1dc..d014852d18b2a6139ce463347badd152ea10a875 100644 (file)
--- a/README.md
+++ b/README.md
@@ -93,9 +93,6 @@ Some arbitrary design decisions had to be made when either the
 obviously expected behavior did not happen or when a specification was
 ambiguous.  This section describes such issues.
 
-  TTerminalWindow
-  ---------------
-
   - TTerminalWindow will hang on input from the remote if the
     TApplication is exited before the TTerminalWindow's process has
     closed on its own.  This is due to a Java limitation/interaction
@@ -115,20 +112,15 @@ ambiguous.  This section describes such issues.
     checking for a tty: script launches $SHELL in a pseudo-tty.  This
     works on Linux but might not on other Posix-y platforms.
 
-  ECMA48 Backend
-  --------------
-
-  - Java's InputStreamReader requires a valid UTF-8 stream.  The
-    default X10 encoding for mouse coordinates outside (160,94) can
-    corrupt that stream, at best putting garbage keyboard events in
-    the input queue but at worst causing the backend reader thread to
-    throw an Exception and exit and make the entire UI unusable.
-    Mouse support therefore requires a terminal that can deliver
-    either UTF-8 coordinates (1005 mode) or SGR coordinates (1006
-    mode).  Most modern terminals can do this.
-
-  Use of 'stty'
-  -------------
+  - Java's InputStreamReader as used by the ECMA48 backend requires a
+    valid UTF-8 stream.  The default X10 encoding for mouse
+    coordinates outside (160,94) can corrupt that stream, at best
+    putting garbage keyboard events in the input queue but at worst
+    causing the backend reader thread to throw an Exception and exit
+    and make the entire UI unusable.  Mouse support therefore requires
+    a terminal that can deliver either UTF-8 coordinates (1005 mode)
+    or SGR coordinates (1006 mode).  Most modern terminals can do
+    this.
 
   - jexer.session.TTYSession calls 'stty size' once every second to
     check the current window size, performing the same function as
@@ -187,6 +179,7 @@ Many tasks remain before calling this version 1.0:
 0.0.5: BUG HUNT
 
 - TSubMenu keyboard mnemonic not working
+- Swing performance.  Even with double buffering it isn't great.
 
 0.1.0: BETA RELEASE
 
index 8be6af5af54a9a024d67976a7f6960fe2d4bba90..72f807818d3daae7103c586899e49260b68b9d32 100644 (file)
@@ -691,9 +691,11 @@ public class TApplication implements Runnable {
             if (!repaint
                 && ((mouseX == oldMouseX) && (mouseY == oldMouseY))
             ) {
-                // Never sleep longer than 100 millis, to get windows with
-                // background tasks an opportunity to update the display.
-                timeout = getSleepTime(100);
+                // Never sleep longer than 50 millis.  We need time for
+                // windows with background tasks to update the display, and
+                // still flip buffers reasonably quickly in
+                // backend.flushPhysical().
+                timeout = getSleepTime(50);
 
                 // See if there are any definitely events waiting to be
                 // processed.  If so, do not wait -- either there is I/O
index 3f95866fe2b3737c4f2a53ec11ad4bb1ddb89dd3..0f5b5bcbd7c967f0b69ccf6530104df9fbabf82b 100644 (file)
@@ -90,7 +90,7 @@ public final class SwingBackend extends Backend {
      */
     @Override
     public void shutdown() {
-        terminal.shutdown();
+        ((SwingScreen) screen).shutdown();
     }
 
 }
index f8ede8578465370bd0348000bff37958ef7227ee..53c205f89ad37955e9cc97c403143358fee5472a 100644 (file)
@@ -35,457 +35,8 @@ import jexer.event.*;
 import jexer.menu.*;
 
 /**
- * This window demonstates the TText, THScroller, and TVScroller widgets.
- */
-class DemoTextWindow extends TWindow {
-
-    /**
-     * Hang onto my TText so I can resize it with the window.
-     */
-    private TText textField;
-
-    /**
-     * Public constructor.
-     *
-     * @param parent the main application
-     */
-    public DemoTextWindow(final TApplication parent) {
-        super(parent, "Text Areas", 0, 0, 44, 20, RESIZABLE);
-
-        textField = addText(
-"This is an example of a reflowable text field.  Some example text follows.\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" +
-"wishing to use the actual C++ Turbo Vision library, see [Sergio\n" +
-"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" +
-"\n",
-            1, 1, 40, 16);
-    }
-
-    /**
-     * Handle window/screen resize events.
-     *
-     * @param event resize event
-     */
-    @Override
-    public void onResize(final TResizeEvent event) {
-        if (event.getType() == TResizeEvent.Type.WIDGET) {
-            // Resize the text field
-            textField.setWidth(event.getWidth() - 4);
-            textField.setHeight(event.getHeight() - 4);
-            textField.reflow();
-            return;
-        }
-
-        // Pass to children instead
-        for (TWidget widget: getChildren()) {
-            widget.onResize(event);
-        }
-    }
-}
-
-/**
- * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
- * widgets.
- */
-class DemoCheckboxWindow extends TWindow {
-
-    /**
-     * Constructor.
-     *
-     * @param parent the main application
-     */
-    DemoCheckboxWindow(final TApplication parent) {
-        this(parent, CENTERED | RESIZABLE);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param parent the main application
-     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
-     */
-    DemoCheckboxWindow(final TApplication parent, final int flags) {
-        // Construct a demo window.  X and Y don't matter because it will be
-        // centered on screen.
-        super(parent, "Radiobuttons and Checkboxes", 0, 0, 60, 15, flags);
-
-        int row = 1;
-
-        // Add some widgets
-        addLabel("Check box example 1", 1, row);
-        addCheckbox(35, row++, "Checkbox 1", false);
-        addLabel("Check box example 2", 1, row);
-        addCheckbox(35, row++, "Checkbox 2", true);
-        row += 2;
-
-        TRadioGroup group = addRadioGroup(1, row, "Group 1");
-        group.addRadioButton("Radio option 1");
-        group.addRadioButton("Radio option 2");
-        group.addRadioButton("Radio option 3");
-
-        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
-            new TAction() {
-                public void DO() {
-                    DemoCheckboxWindow.this.getApplication()
-                        .closeWindow(DemoCheckboxWindow.this);
-                }
-            }
-        );
-    }
-
-}
-
-
-/**
- * This window demonstates the TMessageBox and TInputBox widgets.
- */
-class DemoMsgBoxWindow extends TWindow {
-
-    /**
-     * Constructor.
-     *
-     * @param parent the main application
-     */
-    DemoMsgBoxWindow(final TApplication parent) {
-        this(parent, TWindow.CENTERED | TWindow.RESIZABLE);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param parent the main application
-     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
-     */
-    DemoMsgBoxWindow(final TApplication parent, final int flags) {
-        // Construct a demo window.  X and Y don't matter because it
-        // will be centered on screen.
-        super(parent, "Message Boxes", 0, 0, 60, 15, flags);
-
-        int row = 1;
-
-        // Add some widgets
-        addLabel("Default OK message box", 1, row);
-        addButton("Open O&K MB", 35, row,
-            new TAction() {
-                public void DO() {
-                    getApplication().messageBox("OK MessageBox",
-"This is an example of a OK MessageBox.  This is the\n" +
-"default MessageBox.\n" +
-"\n" +
-"Note that the MessageBox text can span multiple\n" +
-"lines.\n" +
-"\n" +
-"The default result (if someone hits the top-left\n" +
-"close button) is OK.\n",
-                        TMessageBox.Type.OK);
-                }
-            }
-        );
-        row += 2;
-
-        addLabel("OK/Cancel message box", 1, row);
-        addButton("O&pen OKC MB", 35, row,
-            new TAction() {
-                public void DO() {
-                    getApplication().messageBox("OK/Cancel MessageBox",
-"This is an example of a OK/Cancel MessageBox.\n" +
-"\n" +
-"Note that the MessageBox text can span multiple\n" +
-"lines.\n" +
-"\n" +
-"The default result (if someone hits the top-left\n" +
-"close button) is CANCEL.\n",
-                        TMessageBox.Type.OKCANCEL);
-                }
-            }
-        );
-        row += 2;
-
-        addLabel("Yes/No message box", 1, row);
-        addButton("Open &YN MB", 35, row,
-            new TAction() {
-                public void DO() {
-                    getApplication().messageBox("Yes/No MessageBox",
-"This is an example of a Yes/No MessageBox.\n" +
-"\n" +
-"Note that the MessageBox text can span multiple\n" +
-"lines.\n" +
-"\n" +
-"The default result (if someone hits the top-left\n" +
-"close button) is NO.\n",
-                        TMessageBox.Type.YESNO);
-                }
-            }
-        );
-        row += 2;
-
-        addLabel("Yes/No/Cancel message box", 1, row);
-        addButton("Ope&n YNC MB", 35, row,
-            new TAction() {
-                public void DO() {
-                    getApplication().messageBox("Yes/No/Cancel MessageBox",
-"This is an example of a Yes/No/Cancel MessageBox.\n" +
-"\n" +
-"Note that the MessageBox text can span multiple\n" +
-"lines.\n" +
-"\n" +
-"The default result (if someone hits the top-left\n" +
-"close button) is CANCEL.\n",
-                        TMessageBox.Type.YESNOCANCEL);
-                }
-            }
-        );
-        row += 2;
-
-        addLabel("Input box", 1, row);
-        addButton("Open &input box", 35, row,
-            new TAction() {
-                public void DO() {
-                    TInputBox in = getApplication().inputBox("Input Box",
-"This is an example of an InputBox.\n" +
-"\n" +
-"Note that the InputBox text can span multiple\n" +
-"lines.\n",
-                        "some input text");
-                    getApplication().messageBox("Your InputBox Answer",
-                        "You entered: " + in.getText());
-                }
-            }
-        );
-
-        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
-            new TAction() {
-                public void DO() {
-                    getApplication().closeWindow(DemoMsgBoxWindow.this);
-                }
-            }
-        );
-    }
-}
-
-/**
- * This is the main "demo" application window.  It makes use of the TTimer,
- * TProgressBox, TLabel, TButton, and TField widgets.
- */
-class DemoMainWindow extends TWindow {
-
-    // Timer that increments a number.
-    private TTimer timer;
-
-    // Timer label is updated with timer ticks.
-    TLabel timerLabel;
-
-    /**
-     * We need to override onClose so that the timer will no longer be called
-     * after we close the window.  TTimers currently are completely unaware
-     * of the rest of the UI classes.
-     */
-    @Override
-    public void onClose() {
-        getApplication().removeTimer(timer);
-    }
-
-    /**
-     * Construct demo window.  It will be centered on screen.
-     *
-     * @param parent the main application
-     */
-    public DemoMainWindow(final TApplication parent) {
-        this(parent, CENTERED | RESIZABLE);
-    }
-
-    // These are used by the timer loop.  They have to be at class scope so
-    // that they can be accessed by the anonymous TAction class.
-    int timerI = 0;
-    TProgressBar progressBar;
-
-    /**
-     * Constructor.
-     *
-     * @param parent the main application
-     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
-     */
-    private DemoMainWindow(final TApplication parent, final int flags) {
-        // Construct a demo window.  X and Y don't matter because it will be
-        // centered on screen.
-        super(parent, "Demo Window", 0, 0, 60, 23, flags);
-
-        int row = 1;
-
-        // Add some widgets
-        if (!isModal()) {
-            addLabel("Message Boxes", 1, row);
-            addButton("&MessageBoxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoMsgBoxWindow(getApplication());
-                    }
-                }
-            );
-        }
-        row += 2;
-
-        addLabel("Open me as modal", 1, row);
-        addButton("W&indow", 35, row,
-            new TAction() {
-                public void DO() {
-                    new DemoMainWindow(getApplication(), MODAL);
-                }
-            }
-        );
-
-        row += 2;
-
-        addLabel("Variable-width text field:", 1, row);
-        addField(35, row++, 15, false, "Field text");
-        addLabel("Fixed-width text field:", 1, row);
-        addField(35, row++, 15, true);
-        addLabel("Variable-width password:", 1, row);
-        addPasswordField(35, row++, 15, false);
-        addLabel("Fixed-width password:", 1, row);
-        addPasswordField(35, row++, 15, true, "hunter2");
-        row += 2;
-
-        if (!isModal()) {
-            addLabel("Radio buttons and checkboxes", 1, row);
-            addButton("&Checkboxes", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoCheckboxWindow(getApplication());
-                    }
-                }
-            );
-        }
-        row += 2;
-
-        /*
-        if (!isModal()) {
-            addLabel("Editor window", 1, row);
-            addButton("Edito&r", 35, row,
-                {
-                    new TEditor(application, 0, 0, 60, 15);
-                }
-            );
-        }
-        row += 2;
-         */
-
-        if (!isModal()) {
-            addLabel("Text areas", 1, row);
-            addButton("&Text", 35, row,
-                new TAction() {
-                    public void DO() {
-                        new DemoTextWindow(getApplication());
-                    }
-                }
-            );
-        }
-        row += 2;
-
-        /*
-        if (!isModal()) {
-            addLabel("Tree views", 1, row);
-            addButton("Tree&View", 35, row,
-                {
-                    new DemoTreeViewWindow(application);
-                }
-            );
-        }
-        row += 2;
-         */
-
-        if (!isModal()) {
-            addLabel("Terminal", 1, row);
-            addButton("Termi&nal", 35, row,
-                new TAction() {
-                    public void DO() {
-                        getApplication().openTerminal(0, 0);
-                    }
-                }
-            );
-        }
-        row += 2;
-
-        progressBar = addProgressBar(1, row, 22, 0);
-        row++;
-        timerLabel = addLabel("Timer", 1, row);
-        timer = getApplication().addTimer(250, true,
-            new TAction() {
-
-                public void DO() {
-                    timerLabel.setLabel(String.format("Timer: %d", timerI));
-                    timerLabel.setWidth(timerLabel.getLabel().length());
-                    if (timerI < 100) {
-                        timerI++;
-                    }
-                    progressBar.setValue(timerI);
-                }
-            }
-        );
-    }
-}
-
-/**
- * The demo application itself.
- */
-class DemoApplication extends TApplication {
-
-    /**
-     * Public constructor.
-     *
-     * @param backendType one of the TApplication.BackendType values
-     * @throws Exception if TApplication can't instantiate the Backend.
-     */
-    public DemoApplication(BackendType backendType) throws Exception {
-        super(backendType);
-        new DemoMainWindow(this);
-
-        // Add the menus
-        addFileMenu();
-        addEditMenu();
-
-        TMenu demoMenu = addMenu("&Demo");
-        TMenuItem item = demoMenu.addItem(2000, "&Checkable");
-        item.setCheckable(true);
-        item = demoMenu.addItem(2001, "Disabled");
-        item.setEnabled(false);
-        item = demoMenu.addItem(2002, "&Normal");
-        TSubMenu subMenu = demoMenu.addSubMenu("Sub-&Menu");
-        item = demoMenu.addItem(2010, "N&ormal A&&D");
-
-        item = subMenu.addItem(2000, "&Checkable (sub)");
-        item.setCheckable(true);
-        item = subMenu.addItem(2001, "Disabled (sub)");
-        item.setEnabled(false);
-        item = subMenu.addItem(2002, "&Normal (sub)");
-
-        subMenu = subMenu.addSubMenu("Sub-&Menu");
-        item = subMenu.addItem(2000, "&Checkable (sub)");
-        item.setCheckable(true);
-        item = subMenu.addItem(2001, "Disabled (sub)");
-        item.setEnabled(false);
-        item = subMenu.addItem(2002, "&Normal (sub)");
-
-        addWindowMenu();
-
-    }
-}
-
-/**
- * This class provides a simple demonstration of Jexer's capabilities.
+ * This class is the main driver for a simple demonstration of Jexer's
+ * capabilities.
  */
 public class Demo1 {
     /**
diff --git a/src/jexer/demos/DemoApplication.java b/src/jexer/demos/DemoApplication.java
new file mode 100644 (file)
index 0000000..4c5ba31
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * The demo application itself.
+ */
+class DemoApplication extends TApplication {
+
+    /**
+     * Public constructor.
+     *
+     * @param backendType one of the TApplication.BackendType values
+     * @throws Exception if TApplication can't instantiate the Backend.
+     */
+    public DemoApplication(BackendType backendType) throws Exception {
+        super(backendType);
+        new DemoMainWindow(this);
+
+        // Add the menus
+        addFileMenu();
+        addEditMenu();
+
+        TMenu demoMenu = addMenu("&Demo");
+        TMenuItem item = demoMenu.addItem(2000, "&Checkable");
+        item.setCheckable(true);
+        item = demoMenu.addItem(2001, "Disabled");
+        item.setEnabled(false);
+        item = demoMenu.addItem(2002, "&Normal");
+        TSubMenu subMenu = demoMenu.addSubMenu("Sub-&Menu");
+        item = demoMenu.addItem(2010, "N&ormal A&&D");
+
+        item = subMenu.addItem(2000, "&Checkable (sub)");
+        item.setCheckable(true);
+        item = subMenu.addItem(2001, "Disabled (sub)");
+        item.setEnabled(false);
+        item = subMenu.addItem(2002, "&Normal (sub)");
+
+        subMenu = subMenu.addSubMenu("Sub-&Menu");
+        item = subMenu.addItem(2000, "&Checkable (sub)");
+        item.setCheckable(true);
+        item = subMenu.addItem(2001, "Disabled (sub)");
+        item.setEnabled(false);
+        item = subMenu.addItem(2002, "&Normal (sub)");
+
+        addWindowMenu();
+
+    }
+}
diff --git a/src/jexer/demos/DemoCheckboxWindow.java b/src/jexer/demos/DemoCheckboxWindow.java
new file mode 100644 (file)
index 0000000..8f92bff
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * This window demonstates the TRadioGroup, TRadioButton, and TCheckbox
+ * widgets.
+ */
+class DemoCheckboxWindow extends TWindow {
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     */
+    DemoCheckboxWindow(final TApplication parent) {
+        this(parent, CENTERED | RESIZABLE);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
+     */
+    DemoCheckboxWindow(final TApplication parent, final int flags) {
+        // Construct a demo window.  X and Y don't matter because it will be
+        // centered on screen.
+        super(parent, "Radiobuttons and Checkboxes", 0, 0, 60, 15, flags);
+
+        int row = 1;
+
+        // Add some widgets
+        addLabel("Check box example 1", 1, row);
+        addCheckbox(35, row++, "Checkbox 1", false);
+        addLabel("Check box example 2", 1, row);
+        addCheckbox(35, row++, "Checkbox 2", true);
+        row += 2;
+
+        TRadioGroup group = addRadioGroup(1, row, "Group 1");
+        group.addRadioButton("Radio option 1");
+        group.addRadioButton("Radio option 2");
+        group.addRadioButton("Radio option 3");
+
+        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    DemoCheckboxWindow.this.getApplication()
+                        .closeWindow(DemoCheckboxWindow.this);
+                }
+            }
+        );
+    }
+
+}
diff --git a/src/jexer/demos/DemoMainWindow.java b/src/jexer/demos/DemoMainWindow.java
new file mode 100644 (file)
index 0000000..cff9bbd
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * This is the main "demo" application window.  It makes use of the TTimer,
+ * TProgressBox, TLabel, TButton, and TField widgets.
+ */
+class DemoMainWindow extends TWindow {
+
+    // Timer that increments a number.
+    private TTimer timer;
+
+    // Timer label is updated with timer ticks.
+    TLabel timerLabel;
+
+    /**
+     * We need to override onClose so that the timer will no longer be called
+     * after we close the window.  TTimers currently are completely unaware
+     * of the rest of the UI classes.
+     */
+    @Override
+    public void onClose() {
+        getApplication().removeTimer(timer);
+    }
+
+    /**
+     * Construct demo window.  It will be centered on screen.
+     *
+     * @param parent the main application
+     */
+    public DemoMainWindow(final TApplication parent) {
+        this(parent, CENTERED | RESIZABLE);
+    }
+
+    // These are used by the timer loop.  They have to be at class scope so
+    // that they can be accessed by the anonymous TAction class.
+    int timerI = 0;
+    TProgressBar progressBar;
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
+     */
+    private DemoMainWindow(final TApplication parent, final int flags) {
+        // Construct a demo window.  X and Y don't matter because it will be
+        // centered on screen.
+        super(parent, "Demo Window", 0, 0, 60, 23, flags);
+
+        int row = 1;
+
+        // Add some widgets
+        if (!isModal()) {
+            addLabel("Message Boxes", 1, row);
+            addButton("&MessageBoxes", 35, row,
+                new TAction() {
+                    public void DO() {
+                        new DemoMsgBoxWindow(getApplication());
+                    }
+                }
+            );
+        }
+        row += 2;
+
+        addLabel("Open me as modal", 1, row);
+        addButton("W&indow", 35, row,
+            new TAction() {
+                public void DO() {
+                    new DemoMainWindow(getApplication(), MODAL);
+                }
+            }
+        );
+
+        row += 2;
+
+        addLabel("Variable-width text field:", 1, row);
+        addField(35, row++, 15, false, "Field text");
+        addLabel("Fixed-width text field:", 1, row);
+        addField(35, row++, 15, true);
+        addLabel("Variable-width password:", 1, row);
+        addPasswordField(35, row++, 15, false);
+        addLabel("Fixed-width password:", 1, row);
+        addPasswordField(35, row++, 15, true, "hunter2");
+        row += 2;
+
+        if (!isModal()) {
+            addLabel("Radio buttons and checkboxes", 1, row);
+            addButton("&Checkboxes", 35, row,
+                new TAction() {
+                    public void DO() {
+                        new DemoCheckboxWindow(getApplication());
+                    }
+                }
+            );
+        }
+        row += 2;
+
+        /*
+        if (!isModal()) {
+            addLabel("Editor window", 1, row);
+            addButton("Edito&r", 35, row,
+                {
+                    new TEditor(application, 0, 0, 60, 15);
+                }
+            );
+        }
+        row += 2;
+         */
+
+        if (!isModal()) {
+            addLabel("Text areas", 1, row);
+            addButton("&Text", 35, row,
+                new TAction() {
+                    public void DO() {
+                        new DemoTextWindow(getApplication());
+                    }
+                }
+            );
+        }
+        row += 2;
+
+        /*
+        if (!isModal()) {
+            addLabel("Tree views", 1, row);
+            addButton("Tree&View", 35, row,
+                {
+                    new DemoTreeViewWindow(application);
+                }
+            );
+        }
+        row += 2;
+         */
+
+        if (!isModal()) {
+            addLabel("Terminal", 1, row);
+            addButton("Termi&nal", 35, row,
+                new TAction() {
+                    public void DO() {
+                        getApplication().openTerminal(0, 0);
+                    }
+                }
+            );
+        }
+        row += 2;
+
+        progressBar = addProgressBar(1, row, 22, 0);
+        row++;
+        timerLabel = addLabel("Timer", 1, row);
+        timer = getApplication().addTimer(250, true,
+            new TAction() {
+
+                public void DO() {
+                    timerLabel.setLabel(String.format("Timer: %d", timerI));
+                    timerLabel.setWidth(timerLabel.getLabel().length());
+                    if (timerI < 100) {
+                        timerI++;
+                    }
+                    progressBar.setValue(timerI);
+                }
+            }
+        );
+    }
+}
diff --git a/src/jexer/demos/DemoMsgBoxWindow.java b/src/jexer/demos/DemoMsgBoxWindow.java
new file mode 100644 (file)
index 0000000..25115ea
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * This window demonstates the TMessageBox and TInputBox widgets.
+ */
+class DemoMsgBoxWindow extends TWindow {
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     */
+    DemoMsgBoxWindow(final TApplication parent) {
+        this(parent, TWindow.CENTERED | TWindow.RESIZABLE);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param parent the main application
+     * @param flags bitmask of MODAL, CENTERED, or RESIZABLE
+     */
+    DemoMsgBoxWindow(final TApplication parent, final int flags) {
+        // Construct a demo window.  X and Y don't matter because it
+        // will be centered on screen.
+        super(parent, "Message Boxes", 0, 0, 60, 15, flags);
+
+        int row = 1;
+
+        // Add some widgets
+        addLabel("Default OK message box", 1, row);
+        addButton("Open O&K MB", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().messageBox("OK MessageBox",
+"This is an example of a OK MessageBox.  This is the\n" +
+"default MessageBox.\n" +
+"\n" +
+"Note that the MessageBox text can span multiple\n" +
+"lines.\n" +
+"\n" +
+"The default result (if someone hits the top-left\n" +
+"close button) is OK.\n",
+                        TMessageBox.Type.OK);
+                }
+            }
+        );
+        row += 2;
+
+        addLabel("OK/Cancel message box", 1, row);
+        addButton("O&pen OKC MB", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().messageBox("OK/Cancel MessageBox",
+"This is an example of a OK/Cancel MessageBox.\n" +
+"\n" +
+"Note that the MessageBox text can span multiple\n" +
+"lines.\n" +
+"\n" +
+"The default result (if someone hits the top-left\n" +
+"close button) is CANCEL.\n",
+                        TMessageBox.Type.OKCANCEL);
+                }
+            }
+        );
+        row += 2;
+
+        addLabel("Yes/No message box", 1, row);
+        addButton("Open &YN MB", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().messageBox("Yes/No MessageBox",
+"This is an example of a Yes/No MessageBox.\n" +
+"\n" +
+"Note that the MessageBox text can span multiple\n" +
+"lines.\n" +
+"\n" +
+"The default result (if someone hits the top-left\n" +
+"close button) is NO.\n",
+                        TMessageBox.Type.YESNO);
+                }
+            }
+        );
+        row += 2;
+
+        addLabel("Yes/No/Cancel message box", 1, row);
+        addButton("Ope&n YNC MB", 35, row,
+            new TAction() {
+                public void DO() {
+                    getApplication().messageBox("Yes/No/Cancel MessageBox",
+"This is an example of a Yes/No/Cancel MessageBox.\n" +
+"\n" +
+"Note that the MessageBox text can span multiple\n" +
+"lines.\n" +
+"\n" +
+"The default result (if someone hits the top-left\n" +
+"close button) is CANCEL.\n",
+                        TMessageBox.Type.YESNOCANCEL);
+                }
+            }
+        );
+        row += 2;
+
+        addLabel("Input box", 1, row);
+        addButton("Open &input box", 35, row,
+            new TAction() {
+                public void DO() {
+                    TInputBox in = getApplication().inputBox("Input Box",
+"This is an example of an InputBox.\n" +
+"\n" +
+"Note that the InputBox text can span multiple\n" +
+"lines.\n",
+                        "some input text");
+                    getApplication().messageBox("Your InputBox Answer",
+                        "You entered: " + in.getText());
+                }
+            }
+        );
+
+        addButton("&Close Window", (getWidth() - 14) / 2, getHeight() - 4,
+            new TAction() {
+                public void DO() {
+                    getApplication().closeWindow(DemoMsgBoxWindow.this);
+                }
+            }
+        );
+    }
+}
+
diff --git a/src/jexer/demos/DemoTextWindow.java b/src/jexer/demos/DemoTextWindow.java
new file mode 100644 (file)
index 0000000..aa68f89
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import jexer.*;
+import jexer.event.*;
+import jexer.menu.*;
+
+/**
+ * This window demonstates the TText, THScroller, and TVScroller widgets.
+ */
+class DemoTextWindow extends TWindow {
+
+    /**
+     * Hang onto my TText so I can resize it with the window.
+     */
+    private TText textField;
+
+    /**
+     * Public constructor.
+     *
+     * @param parent the main application
+     */
+    public DemoTextWindow(final TApplication parent) {
+        super(parent, "Text Areas", 0, 0, 44, 20, RESIZABLE);
+
+        textField = addText(
+"This is an example of a reflowable text field.  Some example text follows.\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" +
+"wishing to use the actual C++ Turbo Vision library, see [Sergio\n" +
+"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" +
+"\n",
+            1, 1, 40, 16);
+    }
+
+    /**
+     * Handle window/screen resize events.
+     *
+     * @param event resize event
+     */
+    @Override
+    public void onResize(final TResizeEvent event) {
+        if (event.getType() == TResizeEvent.Type.WIDGET) {
+            // Resize the text field
+            textField.setWidth(event.getWidth() - 4);
+            textField.setHeight(event.getHeight() - 4);
+            textField.reflow();
+            return;
+        }
+
+        // Pass to children instead
+        for (TWidget widget: getChildren()) {
+            widget.onResize(event);
+        }
+    }
+}
+
index 2a96122b00bfd6035d2652fbd367931bfc2554be..8677fdff122d1746d5368b27812bab17cde8cb6b 100644 (file)
@@ -41,6 +41,7 @@ import java.awt.Rectangle;
 import java.awt.Toolkit;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
+import java.awt.image.BufferStrategy;
 import java.io.InputStream;
 import java.util.Date;
 import javax.swing.JFrame;
@@ -55,6 +56,11 @@ import jexer.session.SwingSessionInfo;
  */
 public final class SwingScreen extends Screen {
 
+    /**
+     * If true, use double buffering thread.
+     */
+    private static final boolean doubleBuffer = true;
+
     /**
      * Cursor style to draw.
      */
@@ -137,6 +143,11 @@ public final class SwingScreen extends Screen {
          */
         private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
 
+        /**
+         * The BufferStrategy object needed for double-buffering.
+         */
+        private BufferStrategy bufferStrategy;
+
         /**
          * The TUI Screen data.
          */
@@ -197,12 +208,6 @@ public final class SwingScreen extends Screen {
          * @return the Swing Color
          */
         private Color attrToForegroundColor(final CellAttributes attr) {
-            /*
-             * TODO:
-             *   reverse
-             *   blink
-             *   underline
-             */
             if (attr.isBold()) {
                 if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
                     return MYBOLD_BLACK;
@@ -250,12 +255,6 @@ public final class SwingScreen extends Screen {
          * @return the Swing Color
          */
         private Color attrToBackgroundColor(final CellAttributes attr) {
-            /*
-             * TODO:
-             *   reverse
-             *   blink
-             *   underline
-             */
             if (attr.getBackColor().equals(jexer.bits.Color.BLACK)) {
                 return MYBLACK;
             } else if (attr.getBackColor().equals(jexer.bits.Color.RED)) {
@@ -328,6 +327,13 @@ public final class SwingScreen extends Screen {
 
             // Save the text cell width/height
             getFontDimensions();
+
+            // Setup double-buffering
+            if (screen.doubleBuffer) {
+                setIgnoreRepaint(true);
+                createBufferStrategy(2);
+                bufferStrategy = getBufferStrategy();
+            }
         }
 
         /**
@@ -447,6 +453,14 @@ public final class SwingScreen extends Screen {
                         Cell pCell = screen.physical[x][y];
 
                         if (!lCell.equals(pCell) || reallyCleared) {
+
+                            /*
+                             * TODO:
+                             *   reverse
+                             *   blink
+                             *   underline
+                             */
+
                             // Draw the background rectangle, then the
                             // foreground character.
                             gr.setColor(attrToBackgroundColor(lCell));
@@ -500,6 +514,13 @@ public final class SwingScreen extends Screen {
      */
     SwingFrame frame;
 
+    /**
+     * Restore terminal to normal state.
+     */
+    public void shutdown() {
+        frame.dispose();
+    }
+
     /**
      * Public constructor.
      */
@@ -547,7 +568,15 @@ public final class SwingScreen extends Screen {
 
         if (reallyCleared) {
             // Really refreshed, do it all
-            frame.repaint();
+            if (doubleBuffer) {
+                Graphics gr = frame.bufferStrategy.getDrawGraphics();
+                frame.paint(gr);
+                gr.dispose();
+                frame.bufferStrategy.show();
+                Toolkit.getDefaultToolkit().sync();
+            } else {
+                frame.repaint();
+            }
             return;
         }
 
@@ -603,8 +632,20 @@ public final class SwingScreen extends Screen {
         }
 
         // Repaint the desired area
-        frame.repaint(xMin, yMin, xMax - xMin, yMax - yMin);
-        // System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax, yMin, yMax);
+        // System.err.printf("REPAINT X %d %d Y %d %d\n", xMin, xMax,
+        //     yMin, yMax);
+        if (doubleBuffer) {
+            Graphics gr = frame.bufferStrategy.getDrawGraphics();
+            Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin,
+                yMax - yMin);
+            gr.setClip(bounds);
+            frame.paint(gr);
+            gr.dispose();
+            frame.bufferStrategy.show();
+            Toolkit.getDefaultToolkit().sync();
+        } else {
+            frame.repaint(xMin, yMin, xMax - xMin, yMax - yMin);
+        }
     }
 
     /**
index 3f3d8e3976246becca2043c69c4a888d1a06b3cf..a7a4c33d58437ecf7d1214d9865021adb367468b 100644 (file)
@@ -151,14 +151,6 @@ public final class SwingTerminal implements ComponentListener, KeyListener,
         screen.frame.addMouseWheelListener(this);
     }
 
-    /**
-     * Restore terminal to normal state.
-     */
-    public void shutdown() {
-        // System.err.println("=== shutdown() ==="); System.err.flush();
-        screen.frame.dispose();
-    }
-
     /**
      * Return any events in the IO queue.
      *