view mde/gui/widget/layout.d @ 34:6b4116e6355c

Work on the Gui: some of the framework for drag & drop. Also made Window an IWidget. Implemented getWidget(x,y) to find the widget under this location for IWidgets (but not Gui). Made Window an IWidget and made it work a little more similarly to widgets. Implemented callbacks on the Gui for mouse events (enabling drag & drop, etc.). committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 02 May 2008 16:03:52 +0100
parents 316b0230a849
children 052df9b2fe07
line wrap: on
line source

/* LICENSE BLOCK
Part of mde: a Modular D game-oriented Engine
Copyright © 2007-2008 Diggory Hardy

This program is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation, either
version 2 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 General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */

/// Gui layout widgets.
module mde.gui.widget.layout;

import mde.gui.widget.Widget;
import mde.gui.exception : WidgetDataException;

/// Encapsulates a grid of Widgets
class GridWidget : Widget
{
    this (IWindow wind, IWidget, int[] data) {
        // Get grid size
        if (data.length < 2) throw new WidgetDataException;
        rows = data[0];
        cols = data[1];
        
        window = wind;
        
        // Get all sub-widgets
        // Check: correct data length and rows*cols >= 0 (know data.length - 2 >= 0).
        if (data.length != 2 + rows * cols) throw new WidgetDataException;
        subWidgets.length = rows*cols;
        foreach (i, inout subWidget; subWidgets) {
            subWidget = window.makeWidget (data[i+2], this);
        }
        
        getMinimumSize (w,h);   // Calculate the size (current size is not saved)
    }
    
    // Calculates from all rows and columns of widgets.
    void getMinimumSize (out int w, out int h) {
        if (rows*cols == 0) {    // special case
            w = h = 0;
            return;
        }
        
        // Find the sizes of all subWidgets
        int[] widgetW = new int[subWidgets.length]; // dimensions
        int[] widgetH = new int[subWidgets.length];
        foreach (i,widget; subWidgets) widget.getCurrentSize (widgetW[i],widgetH[i]);
        
        // Find row heights and column widths (non cumulative)
        rowH.length = rows;
        colW.length = cols; //WARNING: code reliant on these being initialised to zero
        for (uint i = 0; i < subWidgets.length; ++i) {
            uint n = i / cols;  // row
            if (rowH[n] < widgetH[i]) rowH[n] = widgetH[i];
            n = i % cols;       // column
            if (colW[n] < widgetW[i]) colW[n] = widgetW[i];
        }
        
        // rowY / colX
        rowY.length = rows;
        colX.length = cols;
        int spacing = window.renderer.layoutSpacing;
        
        int cum = 0;
        foreach (i, x; rowH) {
            rowY[i] = cum;
            cum += x + spacing;
        }
        h = cum - spacing;      // total height
        cum = 0;
        foreach (i, x; colW) {
            colX[i] = cum;
            cum += x + spacing;
        }
        w = cum - spacing;      // total width
    }
    
    void setPosition (int x, int y) {
        this.x = x;
        this.y = y;
        
        foreach (i,widget; subWidgets)
            widget.setPosition (x + colX[i % cols], y + rowY[i / cols]);
    }
    
    // Find the relevant widget.
    IWidget getWidget (int cx, int cy) {
        if (rows*cols == 0) return this;    // special case
        
        int lx = cx - x, ly = cy - y;       // use coords relative to this widget
        
        // Find the column
        int i = cols - 1;                   // starting from right...
        while (lx < colX[i]) {              // decrement while left of this column
            if (i == 0) return this;        // left of first column
            --i;
        }                                   // now (lx >= colX[i])
        if (lx >= colX[i] + colW[i]) return this;   // between columns
        
        // Find the row;
        int j = rows - 1;
        while (ly < rowY[j]) {
            if (j == 0) return this;
            --j;
        }
        if (ly >= rowY[j] + rowH[j]) return this;
        
        // Now we know it's in widget (i,j)'s cell (but the widget may not take up the whole cell)
        lx -= colX[i];
        ly -= rowY[j];
        IWidget widg = subWidgets[i + j*cols];
        widg.getCurrentSize (i,j);
        if (lx < i && ly < j)
            return widg.getWidget (cx, cy);
    }
    
    void draw () {
        super.draw ();
        
        foreach (widget; subWidgets)
            widget.draw ();
    }
    
protected:
    int rows, cols;     // number of cells in grid
    int[] rowH;         // row height (highest widget in the row)
    int[] colW;         // column width (widest widget)
    int[] rowY;         // cumulative rowH[i-1] + border and padding
    int[] colX;         // cumulative colW[i-1] + border and padding
    IWidget[] subWidgets;   // all widgets in the grid (by row):
    /* SubWidget order:    [ 0 1 ]
    *                      [ 2 3 ] */
}