view mde/gui/widget/AParentWidget.d @ 133:9fd705793568

Fixed menu popup bug, improved recursion detection. Menu popups can now determine whether or not they are sub-menus. Recursion detection can now also check content (if not the same, there's not a risk of infinite recursion).
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 23 Jan 2009 16:05:05 +0000
parents 9cff74f68b84
children 9f035cd139c6
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/>. */

/******************************************************************************
 * This module contains base widget classes for parent widgets.
 * 
 * Abstract widget classes have an 'A' prepended to the name, similar to the
 * 'I' convention for interfaces.
 *****************************************************************************/
module mde.gui.widget.AParentWidget;

public import mde.gui.widget.AChildWidget;
import mde.gui.exception;
import mde.content.Content;

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

/******************************************************************************
 * Abstract base widget classes to facilitate parent widgets.
 * 
 * To improve code sharing, there's no separate version for parents taking a
 * single subwidget.
 * 
 * Parent widgets probably need to overload these functions (from AChildWidget):
 * setup, saveChanges, setPosition, getWidget, draw, setWidth and setHeight.
 *****************************************************************************/
abstract class AParentWidget : AChildWidget, IParentWidget
{
    protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) {
        super (mgr, parent, id);
    }
    
    override bool setup (uint n, uint flags) {
        debug (mdeWidgets) logger.trace ("AParentWidget.setup");
        bool c = false;
	foreach (w; subWidgets) {
            debug assert (w, "AParentWidget: w is null");
	    c |= w.setup (n,flags);
	}
	return c;
    }
    
    override bool saveChanges () {
        bool c = false;
        foreach (w; subWidgets)
            c |= w.saveChanges;
        return c;
    }
    
    size_t getWidgetIndex (IChildWidget widg) {
        foreach (i,w; subWidgets)
            if (w is widg)
                return i;
        
        throw new GuiException ("getWidgetIndex: widget not found (code error)");
    }
    
    // Parents taking a content should override, only throwing if both the
    // widget id and the content are the same (as its it and content).
    override void recursionCheck (widgetID wID, IContent c) {
        debug assert (id !is null && parent !is null, "recursionCheck called before parent and id set");
        if (wID is id)
            throw new WidgetRecursionException (wID);
        parent.recursionCheck (wID, c);
    }
    
    IPopupParentWidget getParentIPPW () {
        return parent.getParentIPPW;
    }
    
    // Most parent widgets need to implement these, although not all
    void minWChange (IChildWidget widget, wdim mw) {}
    void minHChange (IChildWidget widget, wdim mh) {}
    
    debug override void logWidgetSize () {
        super.logWidgetSize;
        foreach (widg; subWidgets)
            widg.logWidgetSize;
    }
    
protected:
    IChildWidget[] subWidgets;
}


/******************************************************************************
 * Base code for implementing IPopupParentWidget.
 * 
 * The current intention is that an IPPW (excluding the widget manager) may
 * only have one popup; this class follows this intention.
 *****************************************************************************/
abstract class APopupParentWidget : AParentWidget, IPopupParentWidget
{
    protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) {
        super (mgr, parent, id);
        
        parentIPPW = parent.getParentIPPW;
    }
    
    override IPopupParentWidget getParentIPPW () {
        return this;
    }
    
    override void addChildIPPW (IPopupParentWidget ippw) {
        if (childIPPW)
            childIPPW.removedIPPW;
        childIPPW = ippw;
        mgr.requestRedraw;
    }
    override bool removeChildIPPW (IPopupParentWidget ippw) {
        if (childIPPW !is ippw) return false;
        childIPPW.removedIPPW;
        childIPPW = null;
        mgr.requestRedraw;
        mAIPPW = false;
        return true;
    }
    
    // If this function is overriden, you MUST still call it (super.removedIPPW)!
    // Or invariant will throw assert errors.
    override void removedIPPW () {
        if (childIPPW) {
            childIPPW.removedIPPW;
            childIPPW = null;
        }
        mAIPPW = false;
    }
    
    override void menuActive (bool mA) {
        mAIPPW = mA;
        if (childIPPW)
            childIPPW.menuActive = mA;
    }
    override bool menuActive () {
        return mAIPPW;
    }
    override bool parentMenuActive () {
        return parentIPPW.menuActive;
    }
   
    override void menuDone () {	// default actions, for popup menus:
        parentIPPW.removeChildIPPW (this);	// remove self
        parentIPPW.menuDone;			// and propegate
    }
    
    override IChildWidget getPopupWidget (wdabs cx, wdabs cy, bool closePopup) {
        IChildWidget ret;
        if (childIPPW) {
            ret = childIPPW.getPopupWidget (cx, cy, closePopup);
            if (closePopup && ret is null) {
                menuActive = false;
                removeChildIPPW (childIPPW);
            }
        }
        if (ret is null) {
            if (popup.onSelf (cx, cy))
            	ret = popup.getWidget (cx, cy);
            else if (onSelf (cx, cy))
                ret = getWidget (cx, cy);
        }
        return ret;
    }
    
    override void drawPopup () {
        popup.draw;
        if (childIPPW)
            childIPPW.drawPopup;
    }
    
    debug invariant () {
        // True as long as removedIPPW gets called:
        if (!parentIPPW.isChild (this)) {
            assert (childIPPW is null, "APPW: childIPPW");
            assert (mAIPPW is false, "APPW: mAIPPW");
        }
    }
    
protected:
    // How to activate a popup:
    /+void activatePopup (IChildWidget widget) {
        parentIPPW.addChildIPPW (this);
        popup = widget;
        mgr.positionPopup (this, popup);
    }+/
    
    debug override bool isChild (IPopupParentWidget ippw) {
        return ippw is childIPPW;
    }
    
    IPopupParentWidget parentIPPW;
    IPopupParentWidget childIPPW;
    IChildWidget popup;
    bool mAIPPW;
}