#51 wip
[fanfix.git] / src / jexer / TWidget.java
index bca2be02f21f03cc305985a56fbf82c5314ae75c..ba18989bfc43d38ea0549e42f5f20dfb8358e1ab 100644 (file)
@@ -611,67 +611,6 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param menu menu event
      */
     public void onMenu(final TMenuEvent menu) {
-
-        // Special case: if a split command comes in, insert a TPanel and
-        // TSplitPane in the hierarchy here.
-        TPanel panel = null;
-        TSplitPane pane = null;
-        List<TWidget> widgets = null;
-        switch (menu.getId()) {
-        case TMenu.MID_SPLIT_VERTICAL:
-            if (children.size() == 0) {
-                break;
-            }
-            panel = new TPanel(null, x, y, width, height);
-            pane = new TSplitPane(null, x, y, width, height, true);
-            widgets = new ArrayList<TWidget>(children);
-            for (TWidget w: widgets) {
-                w.setParent(panel, false);
-            }
-            children.clear();
-            pane.setParent(this, false);
-            pane.setLeft(panel);
-            activate(pane);
-            for (TWidget w: widgets) {
-                assert (w.window != null);
-                assert (w.parent != null);
-            }
-            assert (pane.getWindow() != null);
-            assert (pane.getParent() != null);
-            assert (panel.getWindow() != null);
-            assert (panel.getParent() != null);
-            assert (pane.isActive() == true);
-            assert (panel.isActive() == true);
-            return;
-        case TMenu.MID_SPLIT_HORIZONTAL:
-            if (children.size() == 0) {
-                break;
-            }
-            panel = new TPanel(null, x, y, width, height);
-            pane = new TSplitPane(null, x, y, width, height, false);
-            widgets = new ArrayList<TWidget>(children);
-            for (TWidget w: widgets) {
-                w.setParent(panel, false);
-            }
-            children.clear();
-            pane.setParent(this, false);
-            pane.setTop(panel);
-            activate(pane);
-            for (TWidget w: widgets) {
-                assert (w.window != null);
-                assert (w.parent != null);
-            }
-            assert (pane.getWindow() != null);
-            assert (pane.getParent() != null);
-            assert (panel.getWindow() != null);
-            assert (panel.getParent() != null);
-            assert (pane.isActive() == true);
-            assert (panel.isActive() == true);
-            return;
-        default:
-            break;
-        }
-
         // Default: do nothing, pass to children instead
         for (TWidget widget: children) {
             widget.onMenu(menu);
@@ -994,10 +933,10 @@ public abstract class TWidget implements Comparable<TWidget> {
     public final void setDimensions(final int x, final int y, final int width,
         final int height) {
 
-        setX(x);
-        setY(y);
-        setWidth(width);
-        setHeight(height);
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
         if (layout != null) {
             layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
                     width, height));
@@ -1115,6 +1054,11 @@ public abstract class TWidget implements Comparable<TWidget> {
 
         assert (window != null);
 
+        if (window instanceof TDesktop) {
+            // Desktop doesn't have a window border.
+            return cursorVisible;
+        }
+
         // If cursor is out of my window's bounds, it is not visible.
         if ((getCursorAbsoluteX() >= window.getAbsoluteX()
                 + window.getWidth() - 1)
@@ -1299,6 +1243,25 @@ public abstract class TWidget implements Comparable<TWidget> {
         return window.getApplication().getTheme();
     }
 
+    /**
+     * See if this widget can be drawn onto a screen.
+     *
+     * @return true if this widget is part of the hierarchy that can draw to
+     * a screen
+     */
+    public final boolean isDrawable() {
+        if ((window == null)
+            || (window.getScreen() == null)
+            || (parent == null)
+        ) {
+            return false;
+        }
+        if (parent == this) {
+            return true;
+        }
+        return (parent.isDrawable());
+    }
+
     /**
      * Draw my specific widget.  When called, the screen rectangle I draw
      * into is already setup (offset and clipping).
@@ -1311,6 +1274,10 @@ public abstract class TWidget implements Comparable<TWidget> {
      * Called by parent to render to TWindow.  Note package private access.
      */
     final void drawChildren() {
+        if (!isDrawable()) {
+            return;
+        }
+
         // Set my clipping rectangle
         assert (window != null);
         assert (getScreen() != null);
@@ -1327,10 +1294,16 @@ public abstract class TWidget implements Comparable<TWidget> {
 
         int absoluteRightEdge = window.getAbsoluteX() + window.getWidth();
         int absoluteBottomEdge = window.getAbsoluteY() + window.getHeight();
-        if (!(this instanceof TWindow) && !(this instanceof TVScroller)) {
+        if (!(this instanceof TWindow)
+            && !(this instanceof TVScroller)
+            && !(window instanceof TDesktop)
+        ) {
             absoluteRightEdge -= 1;
         }
-        if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
+        if (!(this instanceof TWindow)
+            && !(this instanceof THScroller)
+            && !(window instanceof TDesktop)
+        ) {
             absoluteBottomEdge -= 1;
         }
         int myRightEdge = getAbsoluteX() + width;
@@ -1356,6 +1329,12 @@ public abstract class TWidget implements Comparable<TWidget> {
 
         // Draw me
         draw();
+        if (!isDrawable()) {
+            // An action taken by a draw method unhooked me from the UI.
+            // Bail out.
+            return;
+        }
+
         assert (visible == true);
 
         // Continue down the chain.  Draw the active child last so that it
@@ -1363,6 +1342,11 @@ public abstract class TWidget implements Comparable<TWidget> {
         for (TWidget widget: children) {
             if (widget.isVisible() && (widget != activeChild)) {
                 widget.drawChildren();
+                if (!isDrawable()) {
+                    // An action taken by a draw method unhooked me from the UI.
+                    // Bail out.
+                    return;
+                }
             }
         }
         if (activeChild != null) {
@@ -1593,6 +1577,155 @@ public abstract class TWidget implements Comparable<TWidget> {
         return this;
     }
 
+    /**
+     * Insert a vertical split between this widget and parent, and optionally
+     * put another widget in the other side of the split.
+     *
+     * @param newWidgetOnLeft if true, the new widget (if specified) will be
+     * on the left pane, and this widget will be placed on the right pane
+     * @param newWidget the new widget to add to the other pane, or null
+     * @return the new split pane widget
+     */
+    public TSplitPane splitVertical(final boolean newWidgetOnLeft,
+        final TWidget newWidget) {
+
+        TSplitPane splitPane = new TSplitPane(null, x, y, width, height, true);
+        TWidget myParent = parent;
+        remove(false);
+        if (myParent instanceof TSplitPane) {
+            // TSplitPane has a left/right/top/bottom link to me somewhere,
+            // replace it with a link to splitPane.
+            ((TSplitPane) myParent).replaceWidget(this, splitPane);
+        }
+        splitPane.setParent(myParent, false);
+        if (newWidgetOnLeft) {
+            splitPane.setLeft(newWidget);
+            splitPane.setRight(this);
+        } else {
+            splitPane.setLeft(this);
+            splitPane.setRight(newWidget);
+        }
+        splitPane.activate();
+        if (newWidget != null) {
+            newWidget.activate();
+        } else {
+            activate();
+        }
+
+        assert (parent != null);
+        assert (window != null);
+        assert (splitPane.getWindow() != null);
+        assert (splitPane.getParent() != null);
+        assert (splitPane.isActive() == true);
+        assert (parent == splitPane);
+        if (newWidget != null) {
+            assert (newWidget.parent == parent);
+            assert (newWidget.active == true);
+            assert (active == false);
+        } else {
+            assert (active == true);
+        }
+        return splitPane;
+    }
+
+    /**
+     * Insert a horizontal split between this widget and parent, and
+     * optionally put another widget in the other side of the split.
+     *
+     * @param newWidgetOnTop if true, the new widget (if specified) will be
+     * on the top pane, and this widget's children will be placed on the
+     * bottom pane
+     * @param newWidget the new widget to add to the other pane, or null
+     * @return the new split pane widget
+     */
+    public TSplitPane splitHorizontal(final boolean newWidgetOnTop,
+        final TWidget newWidget) {
+
+        TSplitPane splitPane = new TSplitPane(null, x, y, width, height, false);
+        TWidget myParent = parent;
+        remove(false);
+        if (myParent instanceof TSplitPane) {
+            // TSplitPane has a left/right/top/bottom link to me somewhere,
+            // replace it with a link to splitPane.
+            ((TSplitPane) myParent).replaceWidget(this, splitPane);
+        }
+        splitPane.setParent(myParent, false);
+        if (newWidgetOnTop) {
+            splitPane.setTop(newWidget);
+            splitPane.setBottom(this);
+        } else {
+            splitPane.setTop(this);
+            splitPane.setBottom(newWidget);
+        }
+        splitPane.activate();
+        if (newWidget != null) {
+            newWidget.activate();
+        } else {
+            activate();
+        }
+
+        assert (parent != null);
+        assert (window != null);
+        assert (splitPane.getWindow() != null);
+        assert (splitPane.getParent() != null);
+        assert (splitPane.isActive() == true);
+        assert (parent == splitPane);
+        if (newWidget != null) {
+            assert (newWidget.parent == parent);
+            assert (newWidget.active == true);
+            assert (active == false);
+        } else {
+            assert (active == true);
+        }
+        return splitPane;
+    }
+
+    /**
+     * Generate a human-readable string for this widget.
+     *
+     * @return a human-readable string
+     */
+    @Override
+    public String toString() {
+        return String.format("%s(%8x) position (%d, %d) geometry %dx%d " +
+            "active %s enabled %s visible %s", getClass().getName(),
+            hashCode(), x, y, width, height, active, enabled, visible);
+    }
+
+    /**
+     * Generate a string for this widget's hierarchy.
+     *
+     * @param prefix a prefix to use for this widget's place in the hierarchy
+     * @return a pretty-printable string of this hierarchy
+     */
+    protected String toPrettyString(final String prefix) {
+        StringBuilder sb = new StringBuilder(prefix);
+        sb.append(toString());
+        String newPrefix = "";
+        for (int i = 0; i < prefix.length(); i++) {
+            newPrefix += " ";
+        }
+        for (int i = 0; i < children.size(); i++) {
+            TWidget child= children.get(i);
+            sb.append("\n");
+            if (i == children.size() - 1) {
+                sb.append(child.toPrettyString(newPrefix + " \u2514\u2500"));
+            } else {
+                sb.append(child.toPrettyString(newPrefix + " \u251c\u2500"));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Generate a string for this widget's hierarchy.
+     *
+     * @return a pretty-printable string of this hierarchy
+     */
+    public String toPrettyString() {
+        return toPrettyString("");
+    }
+
     // ------------------------------------------------------------------------
     // Passthru for Screen functions ------------------------------------------
     // ------------------------------------------------------------------------