#51 wip
[fanfix.git] / src / jexer / TWidget.java
index 2b9d5cc28a17dff4f0b0157197841d7756b25cf3..729a5f5d0c3a1b43056087e43c39fae1b7f39527 100644 (file)
@@ -43,6 +43,7 @@ import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
+import jexer.layout.LayoutManager;
 import jexer.menu.TMenu;
 import jexer.ttree.TTreeItem;
 import jexer.ttree.TTreeView;
@@ -136,6 +137,11 @@ public abstract class TWidget implements Comparable<TWidget> {
      */
     private int cursorY = 0;
 
+    /**
+     * Layout manager.
+     */
+    private LayoutManager layout = null;
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -212,15 +218,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         this.parent = parent;
         children = new ArrayList<TWidget>();
 
-        if (parent != null) {
-            this.window = parent.window;
-            parent.addChild(this);
-        }
-
         this.x = x;
         this.y = y;
         this.width = width;
         this.height = height;
+
+        if (parent != null) {
+            this.window = parent.window;
+            parent.addChild(this);
+        }
     }
 
     /**
@@ -570,6 +576,14 @@ public abstract class TWidget implements Comparable<TWidget> {
         if (resize.getType() == TResizeEvent.Type.WIDGET) {
             width = resize.getWidth();
             height = resize.getHeight();
+            if (layout != null) {
+                if (this instanceof TWindow) {
+                    layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                            width - 2, height - 2));
+                } else {
+                    layout.onResize(resize);
+                }
+            }
         } else {
             // Let children see the screen resize
             for (TWidget widget: children) {
@@ -597,6 +611,67 @@ 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);
@@ -715,6 +790,15 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
     }
 
+    /**
+     * Remove a child widget from this container.
+     *
+     * @param child the child widget to remove
+     */
+    public final void remove(final TWidget child) {
+        remove(child, true);
+    }
+
     /**
      * Remove a child widget from this container.
      *
@@ -732,6 +816,10 @@ public abstract class TWidget implements Comparable<TWidget> {
         }
         children.remove(child);
         child.parent = null;
+        child.window = null;
+        if (layout != null) {
+            layout.remove(this);
+        }
     }
 
     /**
@@ -746,29 +834,31 @@ public abstract class TWidget implements Comparable<TWidget> {
 
         if (parent != null) {
             parent.remove(this, doClose);
+            window = null;
         }
         assert (parent == null);
-        window = newParent.window;
-        newParent.addChild(this);
+        assert (window == null);
+        parent = newParent;
+        setWindow(parent.window);
+        parent.addChild(this);
     }
 
     /**
-     * Set this widget's window to a specific window.  Parent must already be
-     * null.  Having a null parent with a specified window is only used
-     * within Jexer by TStatusBar because TApplication routes events directly
-     * to it and calls its draw() method.  Any other non-parented widgets
-     * will require similar special case functionality to receive events or
-     * be drawn to screen.
+     * Set this widget's window to a specific window.
+     *
+     * Having a null parent with a specified window is only used within Jexer
+     * by TStatusBar because TApplication routes events directly to it and
+     * calls its draw() method.  Any other non-parented widgets will require
+     * similar special case functionality to receive events or be drawn to
+     * screen.
      *
      * @param window the window to use
      */
     public final void setWindow(final TWindow window) {
-
-        if (parent != null) {
-            throw new IllegalArgumentException("Cannot have different " +
-                "windows for parent and child");
-        }
         this.window = window;
+        for (TWidget child: getChildren()) {
+            child.setWindow(window);
+        }
     }
 
     /**
@@ -854,7 +944,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return widget width
      */
-    public final int getWidth() {
+    public int getWidth() {
         return this.width;
     }
 
@@ -863,8 +953,12 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @param width new widget width
      */
-    public final void setWidth(final int width) {
+    public void setWidth(final int width) {
         this.width = width;
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
     }
 
     /**
@@ -872,7 +966,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @return widget height
      */
-    public final int getHeight() {
+    public int getHeight() {
         return this.height;
     }
 
@@ -881,8 +975,12 @@ public abstract class TWidget implements Comparable<TWidget> {
      *
      * @param height new widget height
      */
-    public final void setHeight(final int height) {
+    public void setHeight(final int height) {
         this.height = height;
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
     }
 
     /**
@@ -900,6 +998,39 @@ public abstract class TWidget implements Comparable<TWidget> {
         setY(y);
         setWidth(width);
         setHeight(height);
+        if (layout != null) {
+            layout.onResize(new TResizeEvent(TResizeEvent.Type.WIDGET,
+                    width, height));
+        }
+    }
+
+    /**
+     * Get the layout manager.
+     *
+     * @return the layout manager, or null if not set
+     */
+    public LayoutManager getLayoutManager() {
+        return layout;
+    }
+
+    /**
+     * Set the layout manager.
+     *
+     * @param layout the new layout manager
+     */
+    public void setLayoutManager(LayoutManager layout) {
+        if (this.layout != null) {
+            for (TWidget w: children) {
+                this.layout.remove(w);
+            }
+            this.layout = null;
+        }
+        this.layout = layout;
+        if (this.layout != null) {
+            for (TWidget w: children) {
+                this.layout.add(w);
+            }
+        }
     }
 
     /**
@@ -984,6 +1115,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)
@@ -1036,19 +1172,25 @@ public abstract class TWidget implements Comparable<TWidget> {
     /**
      * Get this TWidget's parent TApplication.
      *
-     * @return the parent TApplication
+     * @return the parent TApplication, or null if not assigned
      */
     public TApplication getApplication() {
-        return window.getApplication();
+        if (window != null) {
+            return window.getApplication();
+        }
+        return null;
     }
 
     /**
      * Get the Screen.
      *
-     * @return the Screen
+     * @return the Screen, or null if not assigned
      */
     public Screen getScreen() {
-        return window.getScreen();
+        if (window != null) {
+            return window.getScreen();
+        }
+        return null;
     }
 
     /**
@@ -1174,6 +1316,10 @@ public abstract class TWidget implements Comparable<TWidget> {
      * Called by parent to render to TWindow.  Note package private access.
      */
     final void drawChildren() {
+        if (window == null) {
+            return;
+        }
+
         // Set my clipping rectangle
         assert (window != null);
         assert (getScreen() != null);
@@ -1190,10 +1336,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)
+            && !(parent instanceof TDesktop)
+        ) {
             absoluteRightEdge -= 1;
         }
-        if (!(this instanceof TWindow) && !(this instanceof THScroller)) {
+        if (!(this instanceof TWindow)
+            && !(this instanceof THScroller)
+            && !(parent instanceof TDesktop)
+        ) {
             absoluteBottomEdge -= 1;
         }
         int myRightEdge = getAbsoluteX() + width;
@@ -1262,6 +1414,9 @@ public abstract class TWidget implements Comparable<TWidget> {
         for (int i = 0; i < children.size(); i++) {
             children.get(i).tabOrder = i;
         }
+        if (layout != null) {
+            layout.add(child);
+        }
     }
 
     /**
@@ -1791,18 +1946,18 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param values the possible values for the box, shown in the drop-down
      * @param valuesIndex the initial index in values, or -1 for no default
      * value
-     * @param valuesHeight the height of the values drop-down when it is
-     * visible
+     * @param maxValuesHeight the maximum height of the values drop-down when
+     * it is visible
      * @param updateAction action to call when a new value is selected from
      * the list or enter is pressed in the edit field
      * @return the new combobox
      */
     public final TComboBox addComboBox(final int x, final int y,
         final int width, final List<String> values, final int valuesIndex,
-        final int valuesHeight, final TAction updateAction) {
+        final int maxValuesHeight, final TAction updateAction) {
 
         return new TComboBox(this, x, y, width, values, valuesIndex,
-            valuesHeight, updateAction);
+            maxValuesHeight, updateAction);
     }
 
     /**
@@ -2431,4 +2586,35 @@ public abstract class TWidget implements Comparable<TWidget> {
             gridRows);
     }
 
+    /**
+     * Convenience function to add a panel to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @return the new panel
+     */
+    public final TPanel addPanel(final int x, final int y, final int width,
+        final int height) {
+
+        return new TPanel(this, x, y, width, height);
+    }
+
+    /**
+     * Convenience function to add a split pane to this container/window.
+     *
+     * @param x column relative to parent
+     * @param y row relative to parent
+     * @param width width of text area
+     * @param height height of text area
+     * @param vertical if true, split vertically
+     * @return the new split pane
+     */
+    public final TSplitPane addSplitPane(final int x, final int y,
+        final int width, final int height, final boolean vertical) {
+
+        return new TSplitPane(this, x, y, width, height, vertical);
+    }
+
 }