view mde/gui/widget/Widget.d @ 91:4d5d53e4f881

Shared alignment for dynamic content lists - finally implemented! Lots of smaller changes too. Some debugging improvements. When multiple .mtt files are read for merging, files with invalid headers are ignored and no error is thrown so long as at least one file os valid.
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 16 Oct 2008 17:43:48 +0100
parents ea58f277f487
children 085f2ca31914
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 Widget module.
 *
 * This module contains some base widget classes suitable for widget classes to inherit. However,
 * inheriting one of them is by no means necessary for a widget so long as the IWidget interface
 * is implemented.
 *************************************************************************************************/
module mde.gui.widget.Widget;

public import mde.gui.widget.Ifaces;
import mde.gui.exception;

debug {
    import tango.util.log.Log : Log, Logger;
    private Logger logger;
    static this () {
        logger = Log.getLogger ("mde.gui.widget.Widget");
    }
}


/*************************************************************************************************
 * An abstract base widget class.
 *
 * This abstract class, and the more concrete FixedWidget and ScalableWidget classes provides a
 * useful basic implementation for widgets. Widgets need not inherit these (they only need
 * implement IWidget); they are simply provided for convenience and to promote code reuse.
 *************************************************************************************************/
abstract class Widget : IChildWidget
{
//BEGIN Load and save
    // Base this() for child Widgets.
    this (IWidgetManager mgr, widgetID, WidgetData) {
        this.mgr = mgr;
    }
    
    // Don't save any data: fine for many widgets.
    bool saveChanges (widgetID) {
        return false;
    }
    
    // Very basic implementation which assumes the renderer cannot affect the widget's size.
    bool rendererChanged () {
        return false;
    }
//END Load and save
    
//BEGIN Size and position
    bool isWSizable () {    return false;   }
    bool isHSizable () {    return false;   }
    
    /* Return minimal/fixed size. */
    wdim minWidth () {
        return mw;
    }
    wdim minHeight () {
        return mh;
    }
    
    wdim width () {
        return w;
    }
    wdim height() {
        return h;
    }
    
    deprecated void getCurrentSize (out wdim cw, out wdim ch) {
        cw = w;
        ch = h;
    }
    
    /* Set size: minimal size is (mw,mh). Note that both resizable and fixed widgets should allow
     * enlarging, so in both cases this is a correct implementation. */
    void setWidth (wdim nw, int) {
        debug if (nw < mw) logger.warn ("Widget width set below minimal size");
        w = (nw >= mw ? nw : mw);
    }
    void setHeight (wdim nh, int) {
        debug if (nh < mh) logger.warn ("Widget height set below minimal size");
        h = (nh >= mh ? nh : mh);
    }
    
    void setPosition (wdim nx, wdim ny) {
        x = nx;
        y = ny;
    }
//END Size and position
    
//BEGIN Events
    /* This method is only called when the location is over this widget; hence for all widgets
     * without children this method is valid. */
    IChildWidget getWidget (wdim cx, wdim cy) {
        debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
        return this;
    }
    
    /* Dummy event method (suitable for all widgets which don't respond to events). */
    void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) {}
//END Events
    
    /* Basic draw method: draw the background (all widgets should do this). */
    void draw () {
        mgr.renderer.drawWidgetBack (x,y, w,h);
    }
    
protected:
    /**********************************************************************************************
     * Widgets may use WDCheck as a utility to check what data holds. Its use is encouraged, so
     * that the checks can easily be updated should WidgetData be changed.
     * 
     * Params:
     *  data    = the WidgetData to check lengths of
     *  n_ints  = number of integers wanted
     *  n_strings= number of strings (default 0 since not all widgets use strings)
     *********************************************************************************************/
    void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) {
    if (data.ints.length    != n_ints ||
        data.strings.length != n_strings)
        throw new WidgetDataException (this);
    }
    
    IWidgetManager mgr;		// the enclosing window
    wdim x, y;			// position
    wdim w, h;			// size
    wdim mw = 0, mh = 0;	// minimal or fixed size, depending on whether the widget is
    				// resizible; both types of widgets should actually be expandable.
}

/** A base for fixed-size widgets taking their size from the creation data. */
class FixedWidget : Widget {
    // Check data.length is at least 3 before calling!
    /** Constructor for a fixed-size [blank] widget.
     *
     * Widget uses the initialisation data:
     * [widgetID, w, h]
     * where w, h is the fixed size. */
    this (IWidgetManager mgr, widgetID id, WidgetData data) {
        super (mgr, id, data);
        mw = cast(wdim) data.ints[1];
        mh = cast(wdim) data.ints[2];
        w = mw;
        h = mh;
    }
}

/** A base for resizable widgets. */
class SizableWidget : Widget {
    // Check data.length is at least 1 before calling!
    /// Constructor for a completely resizable [blank] widget.
    this (IWidgetManager mgr, widgetID id, WidgetData data) {
        super (mgr, id, data);
    }
    
    bool isWSizable () {    return true;    }
    bool isHSizable () {    return true;    }
}