changeset 38:8c4c96f04e7f

Windows can now be resized! Windows have both a resize border and a move border with independant size for each side. Windows resizing support. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Mon, 05 May 2008 17:02:21 +0100
parents 052df9b2fe07
children 5132301e9ed7
files data/conf/gui.mtt mde/gui/renderer/IRenderer.d mde/gui/renderer/SimpleRenderer.d mde/gui/widget/Window.d mde/gui/widget/layout.d
diffstat 5 files changed, 153 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/data/conf/gui.mtt	Mon May 05 14:47:25 2008 +0100
+++ b/data/conf/gui.mtt	Mon May 05 17:02:21 2008 +0100
@@ -7,4 +7,4 @@
 {W2}
 <int|x=150>
 <int|y=200>
-<int[][int]|widgetData=[0:[0xB004,1,3,2,3,5],2:[0x1,150,150],3:[0x4010,100,100],5:[0xB004,3,1,6,1,6],6:[0x4010,60,60],1:[0x3001]]>
+<int[][int]|widgetData=[0:[0xB004,1,3,3,1,5],3:[0x4010,160,160],5:[0xB004,3,1,6,1,6],6:[0x4010,60,60],1:[0x3001]]>
--- a/mde/gui/renderer/IRenderer.d	Mon May 05 14:47:25 2008 +0100
+++ b/mde/gui/renderer/IRenderer.d	Mon May 05 17:02:21 2008 +0100
@@ -26,8 +26,30 @@
 interface IRenderer
 {
     //BEGIN Get dimensions
-    /** Return the renderer's window border width. */
-    int windowBorder ();
+    /// A container for the dimensions
+    struct BorderDimensions {
+        /// The dimensions: left, top, right, bottom
+        ubyte l, t, r, b;
+        
+        void opAddAssign (BorderDimensions d) {
+            l += d.l;
+            t += d.t;
+            r += d.r;
+            b += d.b;
+        }
+    }
+    
+    /// An enum for border types
+    enum BORDER_TYPES {
+        WINDOW_TOTAL,
+        WINDOW_RESIZE,
+    }
+    
+    /** Return the renderer's window border width.
+     *
+     * Type should be an element of BORDER_TYPES; if not the method may throw or simply return
+     * without an error. */
+    BorderDimensions getBorder (BORDER_TYPES type);
     
     /** Return the renderer's between-widget spacing (for layout widgets). */
     int layoutSpacing ();
--- a/mde/gui/renderer/SimpleRenderer.d	Mon May 05 14:47:25 2008 +0100
+++ b/mde/gui/renderer/SimpleRenderer.d	Mon May 05 17:02:21 2008 +0100
@@ -29,8 +29,19 @@
 * The renderer is intended to be per-GUI. */
 class SimpleRenderer : IRenderer
 {
-    int windowBorder () {
-        return 20;
+    
+    
+    BorderDimensions getBorder (BORDER_TYPES type) {
+        BorderDimensions dims;
+        with (BORDER_TYPES) with (dims) {
+            if (type == WINDOW_TOTAL) {
+                l = t = r = b = 20;
+            } else if (type == WINDOW_RESIZE) {
+                r = t = 5;
+                l = b = 20;
+            }
+        }
+        return dims;
     }
     
     int layoutSpacing () {
@@ -39,8 +50,11 @@
     
     
     void drawWindow (int x, int y, int w, int h) {
+        gl.setColor (0f, 0f, .7f);
+        gl.drawBox (x,y, w,h);
+        
         gl.setColor (0f, 0f, 1f);
-        gl.drawBox (x,y, w,h);
+        gl.drawBox (x+20,y+5, w-25,h-25);
         
         gl.setColor (.3f, .3f, .3f);
         gl.drawBox (x+20, y+20, w-40, h-40);
--- a/mde/gui/widget/Window.d	Mon May 05 14:47:25 2008 +0100
+++ b/mde/gui/widget/Window.d	Mon May 05 17:02:21 2008 +0100
@@ -68,13 +68,17 @@
         widget = makeWidget (0, this);  // primary widget always has ID 0.
         widgetData = null;  // data is no longer needed: allow GC to collect (cannot safely delete)
         
+        // Get border sizes
+        border = rend.getBorder (BORDER_TYPES.WINDOW_TOTAL);
+        resize = rend.getBorder (BORDER_TYPES.WINDOW_RESIZE);
+        
         widget.setSize (0,0);           // set the minimal size
         widget.getCurrentSize (w,h);    // and get this size
-        w += rend.windowBorder * 2;     // Adjust for border
-        h += rend.windowBorder * 2;
+        w += border.l + border.r;       // Adjust for border
+        h += border.t + border.b;
         
-        widgetX = x + rend.windowBorder;    // widget position
-        widgetY = y + rend.windowBorder;    // must be updated if the window is moved
+        widgetX = x + border.l;         // widget position
+        widgetY = y + border.t;         // must be updated if the window is moved
         widget.setPosition (widgetX, widgetY);
         
         xw = x+w;
@@ -125,9 +129,6 @@
         return gui_;
     }
     
-    /+void requestRedraw () {
-    }+/
-    
     IRenderer renderer () {
         return rend;
     }
@@ -141,9 +142,14 @@
         return widget.isHSizable;
     }
     
-    void getMinimalSize (out int mw, out int mh) {
-        mw = w + rend.windowBorder * 2;
-        mh = h + rend.windowBorder * 2;
+    void getMinimalSize (out int wM, out int hM) {
+        if (mh < 0) {       // calculate if necessary
+            widget.getMinimalSize (mw, mh);
+            mw += border.l + border.r;
+            mh += border.t + border.b;
+        }
+        wM = mw;
+        hM = mh;
     }
     void getCurrentSize (out int cw, out int ch) {
         cw = w;
@@ -151,13 +157,16 @@
     }
     
     void setSize (int nw, int nh) {
-        w = nw;
-        h = nh;
+        getMinimalSize (w,h);
+        if (nw > w) w = nw;     // expand if new size is larger, but don't go smaller
+        if (nh > h) h = nh;
         
         xw = x+w;
         yh = y+h;
         
-        widget.setSize (w - rend.windowBorder * 2, h - rend.windowBorder * 2);
+        widget.setSize (w - border.l - border.r, h - border.t - border.b);
+        
+        gui_.requestRedraw ();  // obviously necessary whenever the window's size is changed
     }
     
     void setPosition (int nx, int ny) {
@@ -167,8 +176,8 @@
         xw = x+w;
         yh = y+h;
         
-        widgetX = x + rend.windowBorder;
-        widgetY = y + rend.windowBorder;
+        widgetX = x + border.l;
+        widgetY = y + border.t;
         
         widget.setPosition (widgetX, widgetY);
         
@@ -178,7 +187,7 @@
     IWidget getWidget (int cx, int cy) {
         if (cx < x || cx >= xw || cy < y || cy >= yh)   // not over window
             return null;
-        if (cx >= widgetX && cx < xw-rend.windowBorder && cy >= widgetY && cy < yh-rend.windowBorder)
+        if (cx >= widgetX && cx < xw-border.r && cy >= widgetY && cy < yh-border.b)
                                                         // over the widget
             return widget.getWidget (cx, cy);
         else                                            // over the window border
@@ -186,11 +195,37 @@
     }
     void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {
         if (b == 1 && state == true) {
-            xDrag = cx;
-            yDrag = cy;
-            
-            gui_.addClickCallback (&dragEndCallback);   // handle repositioning
-            gui_.addMotionCallback (&dragCallback);     // handle repositioning
+            if (cx < x + resize.l || cx >= xw - resize.r ||
+                cy < y + resize.t || cy >= yh - resize.b) { // window is being resized
+                /* check for resizes (different to above; use whole border giving larger area for
+                 * diagonal resizes). */
+                resizeType = RESIZE.NONE;
+                if (cx < x + border.l) {
+                    xDrag = w + cx;
+                    resizeType = RESIZE.L;
+                }
+                else if (cx >= xw - border.r) {
+                    xDrag = w - cx;
+                    resizeType = RESIZE.R;
+                }
+                if (cy < y + border.t) {
+                    yDrag = h + cy;
+                    resizeType |= RESIZE.T;
+                }
+                else if (cy >= yh - border.b) {
+                    yDrag = h - cy;
+                    resizeType |= RESIZE.B;
+                }
+                
+                gui_.addClickCallback (&endCallback);
+                gui_.addMotionCallback (&resizeCallback);
+            } else {                // window is being moved
+                xDrag = cx - x;
+                yDrag = cy - y;
+                
+                gui_.addClickCallback (&endCallback);
+                gui_.addMotionCallback (&moveCallback);
+            }
         }
     }
     
@@ -204,19 +239,50 @@
     //END IWidget methods
     
 private:
-    /* For window dragging. */
-    void dragCallback (ushort cx, ushort cy) {
-        setPosition (x+cx-xDrag, y+cy-yDrag);
-        xDrag = cx;
-        yDrag = cy;
+    alias IRenderer.BorderDimensions    BorderDimensions;
+    alias IRenderer.BORDER_TYPES        BORDER_TYPES;
+    
+    //BEGIN Window moving and resizing
+    void moveCallback (ushort cx, ushort cy) {
+        setPosition (cx-xDrag, cy-yDrag);
     }
-    void dragEndCallback (ushort cx, ushort cy, ubyte b, bool state) {
+    void resizeCallback (ushort cx, ushort cy) {
+        if (resizeType & RESIZE.L) {
+            int mw, nw;
+            getMinimalSize (mw, nw);    // (only want mw)
+            nw = xDrag - cx;
+            if (nw < mw) nw = mw;       // clamp
+            setPosition (x + w - nw, y);
+            setSize (nw, h);
+        }
+        else if (resizeType & RESIZE.R) {
+            setSize (xDrag + cx, h);
+        }
+        if (resizeType & RESIZE.T) {
+            int mh, nh;
+            getMinimalSize (nh, mh);    // (only want mh)
+            nh = yDrag - cy;
+            if (nh < mh) nh = mh;       // clamp
+            setPosition (x, y + h - nh);
+            setSize (w, nh);
+        }
+        else if (resizeType & RESIZE.B) {
+            setSize (w, yDrag + cy);
+        }
+    }
+    void endCallback (ushort cx, ushort cy, ubyte b, bool state) {
         if (b == 1 && state == false) {
-            setPosition (x+cx-xDrag, y+cy-yDrag);
+            // The mouse shouldn't have moved since the motion callback
+            // was last called, so there's nothing else to do now.
             gui_.removeCallbacks (cast(void*) this);
         }
     }
-    int xDrag, yDrag;               // locations where a drag starts (used by dragCallback).
+    int xDrag, yDrag;               // where a drag starts relative to x and y
+    enum RESIZE : ubyte {
+        NONE = 0x0, L = 0x1, R = 0x2, T = 0x4, B = 0x8
+    }
+    RESIZE resizeType;              // Type of current resize
+    //END Window moving and resizing
     
     char[] name;                    // The window's name (id from config file)
     IGui gui_;                      // The gui managing this window
@@ -231,4 +297,8 @@
     int w,h;                        // Window size (calculated from Widgets)
     int xw, yh;                     // x+w, y+h (frequent use by clickEvent)
     int widgetX, widgetY;           // Widget position (= window position plus BORDER_WIDTH)
+    int mw = -1, mh = -1;           // minimal size
+    
+    BorderDimensions border;        // Total border size (move plus resize)
+    BorderDimensions resize;        // The outer resize part of the border
 }
--- a/mde/gui/widget/layout.d	Mon May 05 14:47:25 2008 +0100
+++ b/mde/gui/widget/layout.d	Mon May 05 17:02:21 2008 +0100
@@ -109,10 +109,11 @@
     
     void setSize (int nw, int nh) {
         // Step 1: calculate the minimal row/column sizes.
-        int mw, mh; // FIXME: use w,h directly?
+        alias w mw;             // no need for extra vars, just use these
+        alias h mh;
         getMinimalSize (mw, mh);
-        colW = colWMin;         // start with these dimensions, and increase if necessary
-        rowH = rowHMin;
+        colW = colWMin.dup;     // start with these dimensions, and increase if necessary
+        rowH = rowHMin.dup;     // duplicate, because we may want to resize without recalculating *Min
         
         // Step 2: clamp nw/nh or expand a column/row to achieve the required size
         if (nw <= mw) nw = mw;  // clamp to minimal size
@@ -135,6 +136,9 @@
         foreach (i,widget; subWidgets)
             widget.setSize (colW[i % cols], rowH[i / cols]);
         
+        w = nw;
+        h = nh;
+        
         // Step 4: calculate the column and row positions
         colX.length = cols;
         rowY.length = rows;
@@ -145,16 +149,17 @@
             rowY[i] = cum;
             cum += x + spacing;
         }
-        h = cum - spacing;      // set the total height
-        assert (h == nh);       // FIXME: remove and set w/h directly once this is asserted
         
         cum = 0;
         foreach (i, x; colW) {
             colX[i] = cum;
             cum += x + spacing;
         }
-        w = cum - spacing;      // total width
-        assert (w == nw);
+        
+        // Step 5: position needs resetting
+        // FIXME: find a more efficient method of doing this?
+        // maybe setPosition should ALWAYS be called after setSize?
+        setPosition (x,y);
     }
     
     void setPosition (int x, int y) {