view mde/gui/widget/Widget.d @ 37:052df9b2fe07

Allowed widget resizing, changed widget IDs and made Input catch any callback exceptions. Enabled widget resizing. Removed IRenderer's temporary drawBox method and added drawButton for ButtonWidget. Made the Widget class abstract and added FixedWidget and SizableWidget classes. Rewrote much of createWidget to use meta-code; changed widget IDs. Made Input catch callback exceptions and report error messages. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Mon, 05 May 2008 14:47:25 +0100
parents 57d000574d75
children 5132301e9ed7
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.
module mde.gui.widget.Widget;

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

import tango.io.Stdout;

/** 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 : IWidget
{
    void getCurrentSize (out int cw, out int ch) {
        cw = w;
        ch = h;
    }
    
    void setPosition (int nx, int ny) {
        x = nx;
        y = ny;
    }
    
    /* Return self, since we don't have child widgets and the method wouldn't have been called
    * unless the location was over us. Valid for all widgets without children. */
    IWidget getWidget (int,int) {
        return this;
    }
    
    /* Dummy event method (widget doesn't respond to events) */
    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {}
    
    /* Basic draw method: draw the background (all widgets should do this) */
    void draw () {
        window.renderer.drawWidgetBack (x,y, w,h);
    }
    
protected:
    IWindow window;         // the enclosing window
    int x, y;               // position
    int w, h;               // size
}
/** A base for fixed-size widgets. */
class FixedWidget : Widget {
    bool isWSizable () {    return false;   }
    bool isHSizable () {    return false;   }
    
    /* Not resizable, so return current size. */
    void getMinimalSize (out int mw, out int mh) {
        mw = wF;
        mh = hF;
    }
    
    /* Ignore: a fixed size widget. */
    void setSize (int nw, int nh) {
        w = (nw >= wF ? nw : wF);
        h = (nh >= hF ? nh : hF);
    }
    
protected:
    int wF, hF;             // The "fixed" size, i.e. the preferred & minimal size
}
/** A base for resizable widgets. */
class SizableWidget : Widget {
    bool isWSizable () {    return true;    }
    bool isHSizable () {    return true;    }
    
    /* Return zero. */
    void getMinimalSize (out int mw, out int mh) {}
    
    /* Set size: a fully resizable widget. */
    void setSize (int nw, int nh) {
        w = (nw >= 0 ? nw : 0);
        h = (nh >= 0 ? nh : 0);
    }
}

//BEGIN Widgets
/// A fixed-size blank widget.
class FixedBlankWidget : FixedWidget
{
    this (IWindow wind, IWidget, int[] data) {
        if (data.length != 2) throw new WidgetDataException;
        
        window = wind;
        
        w = wF = data[0];
        h = hF = data[1];
    }
}

/// A completely resizable blank widget (initial size zero).
class SizableBlankWidget : SizableWidget
{
    this (IWindow wind, IWidget, int[] data) {
        if (data.length != 0) throw new WidgetDataException;
        
        window = wind;
    }
}

/// First interactible widget
class ButtonWidget : FixedWidget
{
    bool pushed = false;    // true if button is pushed in (visually)
    // pushed is not the same as the button being clicked but not yet released.
    // it is whether the mouse is over the button after being clicked.
    
    this (IWindow wind, IWidget, int[] data) {
        if (data.length != 2) throw new WidgetDataException;
        
        window = wind;
        
        w = wF = data[0];
        h = hF = data[1];
    }
    
    void draw () {
        window.renderer.drawButton (x,y, w,h, pushed);
    }
    
    void clickEvent (ushort, ushort, ubyte b, bool state) {
        if (b == 1 && state == true) {
            pushed = true;
            window.gui.requestRedraw;
            window.gui.addClickCallback (&clickWhileHeld);
            window.gui.addMotionCallback (&motionWhileHeld);
        }
    }
    // Called when a mouse motion/click event occurs while (held == true)
    void clickWhileHeld (ushort cx, ushort cy, ubyte b, bool state) {
        if (cx >= x && cx < x+w && cy >= y && cy < y+h) // button event
            Stdout ("Button clicked!").newline;
        
        pushed = false;
        window.gui.requestRedraw;
        window.gui.removeCallbacks (cast(void*) this);
    }
    void motionWhileHeld (ushort cx, ushort cy) {
        bool oldPushed = pushed;
        if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true;
        else pushed = false;
        if (oldPushed != pushed)
            window.gui.requestRedraw;
    }
}
//END Widgets