view mde/gui/widget/Window.d @ 36:57d000574d75

Enabled drawing on demand, and made the polling interval configurable. Renamed mde.global to mde.imde. Enabled drawing on demand. Allowed options to take double values. Made the main loop's polling interval (sleep duration) settable from config files. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 02 May 2008 17:38:43 +0100
parents 928db3c75ed3
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/>. */

/** The Window class. Hopefully eventually this will become a widget to make things a bit more
* generic. */
module mde.gui.widget.Window;

import mde.gui.widget.Ifaces;
import mde.gui.widget.createWidget;

import mde.gui.IGui;
import mde.gui.exception;

import mt = mde.mergetag.DataSet;
import tango.scrapple.text.convert.parseTo : parseTo;
// not yet implemented:
//import tango.scrapple.text.convert.parseFrom : parseFrom;

import tango.util.log.Log : Log, Logger;

private Logger logger;
static this () {
    logger = Log.getLogger ("mde.gui.widget.Window");
}

/** GUI Window class
 *
 * A window class instance does two things: (1) specify a region of the screen upon which the window
 * and its associated widgets are drawn, and (2) load, save, and generally manage all its widgets.
 *
 * Let the window load a table of widget data, of type int[][widgetID]. Each widget will, when
 * created, be given its int[] of data, which this() must confirm is valid (or throw).
 */
class Window : mt.IDataSection, IWindow
{
    //BEGIN Methods for GUI
    this (char[] id) {
        name = id;
    }
    
    /** Call after loading is finished to setup the window and confirm that it's valid.
     *
     * Throws: WindowLoadException. Do not use the instance in this case! */
    void finalise (IGui gui)
    in {
        assert (gui !is null, "Window.finalise ("~name~"): gui is null");
    } body {
        // Check data was loaded:
        if (widgetData is null) throw new WindowLoadException ("No widget data");
        
        gui_ = gui;
        rend = gui.renderer;
        
        // Create the primary widget (and indirectly all sub-widgets), throwing on error:
        widget = makeWidget (0, this);// primary widget always has ID 0.
        
        widgetData = null;          // data is no longer needed: allow GC to collect (cannot safely delete)
        
        widgetX = x + rend.windowBorder;  // widget position
        widgetY = y + rend.windowBorder;  // must be updated if the window is moved
        widget.setPosition (widgetX, widgetY);
        
        widget.getCurrentSize (w,h);// Find the initial size
        w += rend.windowBorder * 2;       // Adjust for border
        h += rend.windowBorder * 2;
        
        xw = x+w;
        yh = y+h;
    }
    //BEGIN Mergetag code
    void addTag (char[] tp, mt.ID id, char[] dt) {
        if (tp == "int[][int]") {
            if (id == "widgetData") {
                widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt);
            }
        } else if (tp == "int") {
            if (id == "x") {
                x = parseTo!(int) (dt);
            } else if (id == "y") {
                y = parseTo!(int) (dt);
            }
        }
    }
    void writeAll (ItemDelg dlg) {
    }
    //END Mergetag code
    //END Methods for GUI
    
    //BEGIN IWindow methods
    /** Get/create a widget by ID.
     *
     * Should $(I only) be called internally and by sub-widgets! */
    IWidget makeWidget (widgetID i, IWidget parent)
    in {
        // widgetData is normally left to be garbage collected after widgets have been created:
        assert (widgetData !is null, "Window.makeWidget ("~name~"): widgetData is null");
    } body {
        /* Each widget returned should be a unique object; if multiple widgets are requested with
        * the same ID, a new widget is created each time. */
        
        int[]* d = i in widgetData;
        if (d is null)
            throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found");
        
        // Throws WidgetDataException (a WindowLoadException) if bad data:
        IWidget widg = createWidget (this, parent, *d);
        widgets ~= widg;
        return widg;
    }
    
    IGui gui () {
        return gui_;
    }
    
    /+void requestRedraw () {
    }+/
    
    IRenderer renderer () {
        return rend;
    }
    //END IWindow methods
    
    //BEGIN IWidget methods
    void getMinimumSize (out int w, out int h) {
        widget.getMinimumSize (x,y);
        w += rend.windowBorder * 2;       // Adjust for border
        h += rend.windowBorder * 2;
    }
    void getCurrentSize (out int cw, out int ch) {
        cw = w;
        ch = h;
    }
    
    void setPosition (int x, int y) {
        // Currently only used internally
        this.x = x;
        this.y = y;
        
        xw = x+w;
        yh = y+h;
        
        widgetX = x + rend.windowBorder;
        widgetY = y + rend.windowBorder;
        
        widget.setPosition (widgetX, widgetY);
        
        gui_.requestRedraw ();  // obviously necessary whenever the window is moved
    }
    
    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)
                                                        // over the widget
            return widget.getWidget (cx, cy);
        else                                            // over the window border
            return this;
    }
    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
        }
    }
    
    void draw () {
        // background
        rend.drawWindow (x,y, w,h);
        
        // Tell the widget to draw itself:
        widget.draw();
    }
    //END IWidget methods
    
private:
    /* For window dragging. */
    void dragCallback (ushort cx, ushort cy) {
        setPosition (x+cx-xDrag, y+cy-yDrag);
        xDrag = cx;
        yDrag = cy;
    }
    void dragEndCallback (ushort cx, ushort cy, ubyte b, bool state) {
        if (b == 1 && state == false) {
            setPosition (x+cx-xDrag, y+cy-yDrag);
            gui_.removeCallbacks (cast(void*) this);
        }
    }
    int xDrag, yDrag;               // locations where a drag starts (used by dragCallback).
    
    char[] name;                    // The window's name (id from config file)
    IGui gui_;                      // The gui managing this window
    
    int[][widgetID] widgetData;     // Data for all widgets under this window (deleted after loading)
    IWidget[] widgets;              // List of all widgets under this window (created on demand). Use for saving?
    IWidget widget;                 // The primary widget in this window.
    
    IRenderer rend;                 // The window's renderer
    // FIXME: revise which parameters are stored once Gui knows window position
    int x,y;                        // Window position
    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)
}