view mde/gui/widget/createWidget.d @ 103:42e241e7be3e

ContentList content type; getting content items/lists from Options generically via content.Items, and a new addContent widget function. Several improvements to generic handling of content. New button-with-text widget. Some tidy-up. Some name changes, to increase uniformity. Bug-fix: floating widgets of fixed size could previously be made larger than intended from config dimdata.
author Diggory Hardy <diggory.hardy@gmail.com>
date Tue, 25 Nov 2008 18:01:44 +0000
parents 71f0f1f83620
children 08651e8a8c51
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 code to create a widget based on an enumeration value passed at runtime.
 *
 * It could be a part of the WidgetLoader.makeWidget function, but having it here makes things
 * tidier. */
module mde.gui.widget.createWidget;

import mde.gui.widget.Ifaces;
import mde.gui.exception : WidgetDataException;
import mde.gui.content.Content; //NOTE - maybe move IContent to a separate module

// Widgets to create:
import mde.gui.widget.layout;
import mde.gui.widget.miscWidgets;
import mde.gui.widget.TextWidget;
import mde.gui.widget.miscContent;
import mde.gui.widget.textContent;
import mde.gui.widget.Floating;
import tango.util.log.Log : Log, Logger;

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

/** Create a widget.
 *
 * Usually called by the widget manager's makeWidget function.
 *
 * Widget created of type data.ints[0] (see enum WIDGET_TYPES), with one of the following CTORs:
 * ---
 * this (IWidgetManager mgr, WidgetData data);
 * // Called if (data.ints[0] & WIDGET_TYPES.TAKES_CONTENT):
 * this (IWidgetManager mgr, WidgetData data, IContent content);
 * ---
 *************************************************************************************************/
IChildWidget createWidget (IWidgetManager mgr, widgetID id, WidgetData data, IContent content)
in {
    assert (mgr !is null, "createWidget: mgr is null");
} body {
    if (data.ints.length < 1) {
        logger.error ("No int data; creating a debug widget");
        data.ints = [WIDGET_TYPE.Debug];
    }
    int type = data.ints[0];    // type is first element of data
    
    try {
        //pragma (msg, binarySearch ("type", WIDGETS));
        mixin (binarySearch ("type", WIDGETS)); // creates widget by type: new XWidget (mgr, data [, parent]);
        // Not returned a new widget or thrown:
        logger.error ("Bad widget type: {}; creating a debug widget instead",type);
    } catch (Exception e) {
        logger.error ("Error creating widget: {}; creating a debug widget instead.", e.msg);
    }
    
    return new DebugWidget (mgr, id, data);
}

/+ for converting to a char[] name (unused)
static this() {
    WIDGET_NAMES = [
            FixedBlank : "FixedBlank",
            SizableBlank : "SizableBlank",
            Button : "Button",
            GridLayout : "GridLayout"
                    ];
}+/

private:
/// Widget types.
enum WIDGET_TYPE : int {
    FUNCTION		= 0x2000,   // Function called instead of widget created (no "Widget" appended to fct name)
    TAKES_CONTENT	= 0x4000,   // Flag indicates widget's this should be passed an IContent reference.
    PARENT		= 0x8000,   // widget can have children; not used by code (except in data files)
    
    // Use widget names rather than usual capitals convention
    Unnamed		= 0x0,      // Only for use by widgets not created with createWidget
    
    // blank: 0x1
    FixedBlank		= 0x1,
    SizableBlank	= 0x2,
    Debug		= 0xF,
    
    // buttons: 0x10
    Button		= 0x10,
    TextButton		= 0x11,
    
    // labels: 0x20
    ContentLabel	= TAKES_CONTENT | 0x20,
    TextLabel		= 0x21,
    
    // content functions: 0x30
    editContent		= FUNCTION | TAKES_CONTENT | 0x30,
    addContent		= FUNCTION | 0x31,
    
    // content widgets: 0x40
    DisplayContent	= TAKES_CONTENT | 0x40,
    BoolContent		= TAKES_CONTENT | 0x41,
    ValueContent	= TAKES_CONTENT | 0x42,
    
    GridLayout		= TAKES_CONTENT | PARENT | 0x100,
    ContentList		= TAKES_CONTENT | PARENT | 0x110,
    
    FloatingArea	= PARENT | 0x200,
}

//const char[][int] WIDGET_NAMES;

// Only used for binarySearch algorithm generation; must be ordered by numerical values.
const char[][] WIDGETS = [
        "FixedBlank",
        "SizableBlank",
        "Debug",
        "Button",
	"TextButton",
	"TextLabel",
	"addContent",
	"ContentLabel",
        "DisplayContent",
        "BoolContent",
	"ValueContent",
        "editContent",
        "FloatingArea",
        "GridLayout",
	"ContentList"];

/* Generates a binary search algorithm. */
char[] binarySearch (char[] var, char[][] consts) {
    if (consts.length > 3) {
        return "if (" ~ var ~ " <= WIDGET_TYPE." ~ consts[$/2 - 1] ~ ") {\n" ~
                binarySearch (var, consts[0 .. $/2]) ~
                "} else {\n" ~
                binarySearch (var, consts[$/2 .. $]) ~
                "}\n";
    } else {
        char[] ret;
        foreach (c; consts) {
            ret ~=  "if (" ~ var ~ " == WIDGET_TYPE." ~ c ~ ") {\n" ~
                    "   debug (mdeWidgets) logger.trace (\"Creating new "~c~"Widget.\");\n" ~
                    "   static if (WIDGET_TYPE."~c~" & WIDGET_TYPE.FUNCTION)\n" ~
                    "       return " ~ c ~ " (mgr, id, data, content);\n" ~
                    "   else static if (WIDGET_TYPE."~c~" & WIDGET_TYPE.TAKES_CONTENT)\n" ~
                    "       return new " ~ c ~ "Widget (mgr, id, data, content);\n" ~
                    "   else\n" ~
                    "       return new " ~ c ~ "Widget (mgr, id, data);\n" ~
                    "} else ";
        }
        ret = ret[0..$-6] ~ '\n';  // remove last else
        return ret;
    }
}

debug { // check items in WIDGETS are listed in order
    char[] WIDGETS_check () {
        char[] ret;
        for (int i = WIDGETS.length-2; i > 0; --i) {
            ret ~= "WIDGET_TYPE."~WIDGETS[i] ~" >= WIDGET_TYPE."~ WIDGETS[i+1];
            if (i>1) ret ~= " || ";
        }
        return ret;
    }
    mixin ("static if ("~WIDGETS_check~")
        static assert (false, \"WIDGETS is not in order!\");");
}