Changeset 39:5132301e9ed7

Show
Ignore:
Timestamp:
05/07/08 08:07:03 (8 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
convert_revision:
37c7e5501ad0e7e227e116cedf35710947dee2d4
Message:

Implemented widget saving.

Widget creation data saving (sub-widgets, etc:) code there but not used.
Widget mutable data saving & loading: window size/position, row/column dimensions saved (still needs a fix in GridWidget?.setSize()).

committer: Diggory Hardy <diggory.hardy@gmail.com>

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • mde/gui/Gui.d

    r36 r39  
    3838import mt = mde.mergetag.exception; 
    3939import mde.mergetag.Reader; 
     40import mde.mergetag.Writer; 
    4041import mde.resource.paths; 
    4142 
     
    5758    //BEGIN Loading code 
    5859    /** Load all windows from the file gui. */ 
    59     void load(char[] fileName) { 
     60    void load (char[] fileName) { 
    6061        if (!confDir.exists (fileName)) { 
    6162            logger.error ("Unable to load GUI: no config file!"); 
     
    6566        IReader reader; 
    6667        try { 
    67             reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY, null, true); 
     68            reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 
    6869            reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 
    6970                return new Window (id); 
     
    7879         
    7980        // Get the renderer 
    80         char[]* p = "Renderer" in reader.dataset.header.Arg!(char[]).Arg; 
     81        char[]* p = RENDERER in reader.dataset.header.Arg!(char[]).Arg; 
    8182        if (p is null || *p is null) { 
    8283            logger.error ("Loading GUI aborted: no renderer specified"); 
    8384            return; 
    8485        } 
    85         rend = createRenderer (*p); 
     86        rendName = *p; 
     87        rend = createRenderer (rendName); 
    8688         
    8789        // get list 
     
    105107        imde.input.addMouseMotionCallback(&motionEvent); 
    106108    } 
     109     
     110    void save (char[] fileName) { 
     111        mt.DataSet ds = new mt.DataSet; 
     112         
     113        // Add header: 
     114        ds.header = new mt.DefaultData; 
     115        ds.header.Arg!(char[]).Arg[RENDERER] = rendName; 
     116         
     117        // Add windows to be saved: 
     118        foreach (window; windows) 
     119            ds.sec [window.name] = window; 
     120         
     121        try {   // Save 
     122            IWriter writer; 
     123            writer = confDir.makeMTWriter (fileName, ds); 
     124            writer.write; 
     125        } catch (mt.MTException e) { 
     126            logger.error ("Saving GUI failed:"); 
     127            logger.error (e.msg); 
     128             
     129            return; 
     130        } 
     131    } 
     132    private static const { 
     133        auto RENDERER = "Renderer"; 
     134    } 
    107135    //END Loading code 
    108136     
     
    172200private: 
    173201    Window[] windows;   // Windows. First window is "on top", others may be obscured. 
    174     IRenderer rend; 
     202     
     203    IRenderer rend;     // Renderer (synonymous with theme) 
     204    char[] rendName;    // Name of renderer; for saving 
     205     
    175206    // callbacks indexed by their frame pointers: 
    176207    void delegate(ushort cx, ushort cy, ubyte b, bool state) [void*] clickCallbacks; 
  • mde/gui/exception.d

    r27 r39  
    3838} 
    3939 
    40 /// Thrown when or createWidget a Widget class's this() is called with invalid data. 
     40/// Thrown when createWidget or a Widget class's this() is called with invalid data. 
    4141class WidgetDataException : WindowLoadException 
    4242{ 
     
    4848    } 
    4949} 
     50 
     51/// Thrown when a Widget class's adjust() is called with invalid data. 
     52class MutableDataException : WindowLoadException 
     53{ 
     54    this () {   // Default, by Widget class's this 
     55        super ("Bad widget mutable data"); 
     56    } 
     57    this (char[] msg) { // From createWidget 
     58        super (msg); 
     59    } 
     60} 
  • mde/gui/renderer/SimpleRenderer.d

    r38 r39  
    3030class SimpleRenderer : IRenderer 
    3131{ 
    32      
    33      
    3432    BorderDimensions getBorder (BORDER_TYPES type) { 
    3533        BorderDimensions dims; 
  • mde/gui/widget/Ifaces.d

    r37 r39  
    2121 
    2222/** Interface for Window, allowing widgets to call some of Window's methods. 
    23 
    24 * Contains the methods in Window available for widgets to call on their root. */ 
     23
     24 * Contains the methods in Window available for widgets to call on their root. */ 
    2525interface IWindow : IWidget 
    2626{ 
    2727    /** Widget ID type. Each ID is unique under this window. 
    28    
    29     * Type is int since this is the widget data type. */ 
     28   
     29    * Type is int since this is the widget data type. */ 
    3030    alias int widgetID; 
    3131     
    3232    /** Get a widget by ID. 
    33     * 
    34     * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet 
    35     * been created, creates it using the Window's widget creation data. 
    36     * 
    37     * Widgets should never be used more than once (must have a unique parent and position!), and 
    38     * this function is only intended for widgets to get child-widgets, hence a warning is logged 
    39     * if a widget is asked for more than once. */ 
    40     //NOTE: possibly revise: parent isn't actually used any more 
    41     IWidget makeWidget (widgetID i, IWidget parent); 
     33     * 
     34     * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet 
     35     * been created, creates it using the Window's widget creation data. */ 
     36    IWidget makeWidget (widgetID i); 
     37     
     38    /** Add widget's saveData to the data to be saved, returning it's widgetID. */ 
     39    widgetID addCreationData (IWidget widget); 
    4240     
    4341    /** Get the managing Gui. */ 
     42    //FIXME: remove and add requestRedraw to allow for only redrawing the window 
    4443    IGui gui (); 
    4544     
     
    5251 
    5352/** Interface for widgets. 
    54 
    55 * Note that Window also implements this interface so that widgets can interact with their parent in 
    56 * a uniform way. 
    57 
    58 * A widget is a region of a GUI window which handles rendering and user-interaction for itself 
    59 * and is able to communicate with it's window and parent/child widgets as necessary. 
    60 
    61 * A widget's constructor should have this prototype: 
    62 * ---------------------------------- 
    63 * this (IWindow window, IWidget parent, int[] data); 
    64 * ---------------------------------- 
    65 * Where window is the root window (the window to which the widget belongs), parent is the parent 
    66 * widget, and data is an array of initialisation data. The method should throw a 
    67 * WidgetDataException (created without parameters) if the data has wrong length or is otherwise 
    68 * invalid. 
    69 
    70 * The widget's size should be set either by it's this() method or by the first call to 
    71 * setSize(). setSize() is called on all widgets immediately after their creation, and throwing an 
    72 * exception at this point (but not on later calls to setSize) is an acceptible method of failure. 
    73 */ 
     53 * 
     54 * Note that Window also implements this interface so that widgets can interact with their parent 
     55 * in a uniform way. 
     56 * 
     57 * A widget is a region of a GUI window which handles rendering and user-interaction for itself 
     58 * and is able to communicate with it's window and parent/child widgets as necessary. 
     59 * 
     60 * A widget's constructor should have this prototype: 
     61 * ---------------------------------- 
     62 * this (IWindow window, int[] data); 
     63 * ---------------------------------- 
     64 * Where window is the root window (the window to which the widget belongs) and data is an array of 
     65 * initialisation data. The method should throw a WidgetDataException (created without parameters) 
     66 * if the data has wrong length or is otherwise invalid. 
     67 * 
     68 * The widget's size should be set either by it's this() method or by the first call to 
     69 * setSize(). setSize() is called on all widgets immediately after their creation, and throwing an 
     70 * exception at this point (but not on later calls to setSize) is an acceptible method of failure. 
     71 */ 
    7472//NOTE: add another this() without the data for default initialization, for the GUI editor? 
    7573interface IWidget 
    7674{ 
     75//BEGIN Load and save 
     76    /** Called after creating widgets to adjust size & other mutable attributes from saved data. 
     77     * 
     78     * Each widget should call adjust on each of its sub-widgets in turn with data, each time 
     79     * replacing data by the return value of the call. It should then take its own mutable data 
     80     * from the beginning of the array and return the remainder of the array. 
     81     * 
     82     * Throws: on error, throw a MutableDataException. */ 
     83    int[] adjust (int[] data); 
     84     
     85    /** Output data suitible for recreating the widget (data to be passed to this()). */ 
     86    int[] getCreationData (); 
     87     
     88    /** Output data containing the widget's current adjustments (data to be passed to adjust()). 
     89     * Should be a concatenation of each sub-widget's mutable data and the widget's own. */ 
     90    int[] getMutableData (); 
     91//END Load and save 
     92     
     93//BEGIN Size and position 
    7794    /** is the width / height resizable? 
    7895     * 
     
    90107    /** Used to adjust the size. 
    91108     * 
     109     * setPosition should always be called after setSize (for layout widgets). Adding this 
     110     * restriction appears to be the most efficient approach without a lot more tests. 
     111     * 
     112     * Implementation: 
    92113     * The size should be clamped to the widget's minimal size, i.e. the size set may be larger 
    93114     * than that given by the parameters. Conversely, the size should not be reduced to the 
     
    99120    /** Set the current position (i.e. called on init and move). */ 
    100121    void setPosition (int x, int y); 
     122//END Size and position 
    101123     
    102      
     124//BEGIN Events 
    103125    /** Recursively scan the widget tree to find the widget under (x,y). 
    104126     * 
     
    119141     * Widget may assume coordinates are on the widget (caller must check). */ 
    120142    void clickEvent (ushort cx, ushort cy, ubyte b, bool state); 
    121      
     143//END Events 
    122144     
    123145    /** Draw, using the stored values of x and y. 
  • mde/gui/widget/Widget.d

    r37 r39  
    3030abstract class Widget : IWidget 
    3131{ 
     32    // Base this(); all widgets must at least check data.length is correct. 
     33    this (IWindow wind, int[] data) { 
     34        window = wind; 
     35        widgetType = data[0]; 
     36    } 
     37     
     38    // Most widgets don't need to do adjustments based on mutable data, however they usually do 
     39    // still need to set their size. 
     40    int[] adjust (int[] data) { 
     41        setSize (0,0); 
     42        return data; 
     43    } 
     44     
     45    // Widget type should always be the first value. 
     46    int[] getCreationData () { 
     47        return [widgetType]; 
     48    } 
     49    // Most widgets don't use mutable data. 
     50    int[] getMutableData () { 
     51        return []; 
     52    } 
     53     
    3254    void getCurrentSize (out int cw, out int ch) { 
    3355        cw = w; 
     
    5577     
    5678protected: 
     79    final int widgetType;   // the type (stored for saving) 
    5780    IWindow window;         // the enclosing window 
    5881    int x, y;               // position 
     
    6184/** A base for fixed-size widgets. */ 
    6285class FixedWidget : Widget { 
     86    this (IWindow wind, int[] data) { 
     87        super (wind, data); 
     88        w = wF = data[1]; 
     89        h = hF = data[2]; 
     90    } 
     91     
     92    int[] getCreationData () { 
     93        return [widgetType, wF, hF]; 
     94    } 
     95     
    6396    bool isWSizable () {    return false;   } 
    6497    bool isHSizable () {    return false;   } 
     
    81114/** A base for resizable widgets. */ 
    82115class SizableWidget : Widget { 
     116    this (IWindow wind, int[] data) { 
     117        super (wind, data); 
     118    } 
     119     
    83120    bool isWSizable () {    return true;    } 
    84121    bool isHSizable () {    return true;    } 
     
    98135class FixedBlankWidget : FixedWidget 
    99136{ 
    100     this (IWindow wind, IWidget, int[] data) { 
    101         if (data.length != 2) throw new WidgetDataException; 
    102          
    103         window = wind; 
    104          
    105         w = wF = data[0]; 
    106         h = hF = data[1]; 
     137    this (IWindow wind, int[] data) { 
     138        if (data.length != 3) throw new WidgetDataException; 
     139        super (wind, data); 
    107140    } 
    108141} 
     
    111144class SizableBlankWidget : SizableWidget 
    112145{ 
    113     this (IWindow wind, IWidget, int[] data) { 
    114         if (data.length != 0) throw new WidgetDataException; 
    115          
    116         window = wind; 
     146    this (IWindow wind, int[] data) { 
     147        if (data.length != 1) throw new WidgetDataException; 
     148        super (wind, data); 
    117149    } 
    118150} 
     
    125157    // it is whether the mouse is over the button after being clicked. 
    126158     
    127     this (IWindow wind, IWidget, int[] data) { 
    128         if (data.length != 2) throw new WidgetDataException; 
    129          
    130         window = wind; 
    131          
    132         w = wF = data[0]; 
    133         h = hF = data[1]; 
     159    this (IWindow wind, int[] data) { 
     160        if (data.length != 3) throw new WidgetDataException; 
     161        super (wind, data); 
    134162    } 
    135163     
  • mde/gui/widget/Window.d

    r38 r39  
    2626import mt = mde.mergetag.DataSet; 
    2727import tango.scrapple.text.convert.parseTo : parseTo; 
    28 // not yet implemented: 
    29 //import tango.scrapple.text.convert.parseFrom : parseFrom; 
     28import tango.scrapple.text.convert.parseFrom : parseFrom; 
    3029 
    3130import tango.util.log.Log : Log, Logger; 
     
    6059    } body { 
    6160        // Check data was loaded: 
    62         if (widgetData is null) throw new WindowLoadException ("No widget data"); 
     61        if (widgetData is null || mutableData is null) 
     62            throw new WindowLoadException ("No widget/mutable data"); 
    6363         
    6464        gui_ = gui; 
    6565        rend = gui.renderer; 
    66          
    67         // Create the primary widget (and indirectly all sub-widgets), throwing on error: 
    68         widget = makeWidget (0, this);  // primary widget always has ID 0. 
    69         widgetData = null;  // data is no longer needed: allow GC to collect (cannot safely delete) 
    7066         
    7167        // Get border sizes 
     
    7369        resize = rend.getBorder (BORDER_TYPES.WINDOW_RESIZE); 
    7470         
    75         widget.setSize (0,0);           // set the minimal size 
     71        // Create the primary widget (and indirectly all sub-widgets), throwing on error: 
     72        widget = makeWidget (0);    // primary widget always has ID 0. 
     73        widgetData = null;  // data is no longer needed: allow GC to collect (cannot safely delete) 
     74         
     75        widget.adjust (mutableData);    // adjust/set size, etc. 
     76        mutableData = null;             // no longer needed 
     77         
    7678        widget.getCurrentSize (w,h);    // and get this size 
    7779        w += border.l + border.r;       // Adjust for border 
     
    8789    //BEGIN Mergetag code 
    8890    void addTag (char[] tp, mt.ID id, char[] dt) { 
    89         if (tp == "int[][int]") { 
    90             if (id == "widgetData") { 
     91        // Priority is HIGH_LOW, so don't overwrite data which has already been loaded. 
     92        if (tp == INTAA) { 
     93            if (id == WDGD && widgetData == null) { 
    9194                widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt); 
    9295            } 
    93         } else if (tp == "int") { 
    94             if (id == "x") { 
     96        } else if (tp == INTA) { 
     97            if (id == MD && mutableData == null) { 
     98                mutableData = parseTo!(int[]) (dt); 
     99            } 
     100        } else if (tp == INT) { 
     101            if (id == X && x == -1) { 
    95102                x = parseTo!(int) (dt); 
    96             } else if (id == "y") { 
     103            } else if (id == Y && y == -1) { 
    97104                y = parseTo!(int) (dt); 
    98105            } 
    99106        } 
    100107    } 
    101     void writeAll (ItemDelg dlg) { 
     108    void writeAll (ItemDelg dlg) 
     109    in { 
     110        assert (widgetData is null, "Window.writeAll: widgetData !is null"); 
     111    } body { 
     112        /+ NOTE: currently editing is impossible... 
     113        if (edited) {   // only save the widget creation data if it's been adjusted: 
     114            addSaveData (widget);       // generate widget save data 
     115            dlg (INTAA, WDGD, parseFrom!(int[][int]) (widgetData)); 
     116        }+/ 
     117        // Save mutable data: 
     118        dlg (INTA, MD, parseFrom!(int[]) (widget.getMutableData)); 
     119        // Save the window position: 
     120        dlg (INT, X, parseFrom!(int) (x)); 
     121        dlg (INT, Y, parseFrom!(int) (y)); 
     122    } 
     123    private static const { 
     124        auto INTAA = "int[][int]"; 
     125        auto INTA = "int[]"; 
     126        auto INT = "int"; 
     127        auto WDGD = "widgetData"; 
     128        auto MD = "mutableData"; 
     129        auto X = "x"; 
     130        auto Y = "y"; 
    102131    } 
    103132    //END Mergetag code 
     
    108137     * 
    109138     * Should $(I only) be called internally and by sub-widgets! */ 
    110     IWidget makeWidget (widgetID i, IWidget parent
     139    IWidget makeWidget (widgetID i
    111140    in { 
    112141        // widgetData is normally left to be garbage collected after widgets have been created: 
     
    121150         
    122151        // Throws WidgetDataException (a WindowLoadException) if bad data: 
    123         IWidget widg = createWidget (this, parent, *d); 
    124         widgets ~= widg; 
    125         return widg; 
    126     } 
    127      
     152        return createWidget (this, *d); 
     153    } 
     154     
     155    /** Add this widget's data to that to be saved, returning it's widgetID. */ 
     156    widgetID addCreationData (IWidget widget) 
     157    { 
     158        widgetID i; 
     159        if (widgetData is null) 
     160            i = 0; 
     161        else 
     162            i = widgetData.keys[$-1] + 1; 
     163         
     164        widgetData[i] = null;   // Make sure the same ID doesn't get used by a recursive call 
     165        widgetData[i] = widget.getCreationData; 
     166         
     167        return i; 
     168    } 
     169 
    128170    IGui gui () { 
    129171        return gui_; 
     
    136178     
    137179    //BEGIN IWidget methods 
     180    //FIXME: how many of these methods are actually needed/used? 
     181    int[] adjust (int[]) {          // simply not relevant (never used) 
     182        return []; 
     183    } 
     184    int[] getCreationData () {      // simply not relevant (never used) 
     185        return []; 
     186    } 
     187    int[] getMutableData () {       // simply not relevant (never used) 
     188        return []; 
     189    } 
     190     
    138191    bool isWSizable () { 
    139192        return widget.isWSizable; 
     
    201254                 * diagonal resizes). */ 
    202255                resizeType = RESIZE.NONE; 
    203                 if (cx < x + border.l) { 
    204                     xDrag = w + cx; 
    205                     resizeType = RESIZE.L; 
     256                 
     257                if (isWSizable) { 
     258                    if (cx < x + border.l) { 
     259                        xDrag = w + cx; 
     260                        resizeType = RESIZE.L; 
     261                    } 
     262                    else if (cx >= xw - border.r) { 
     263                        xDrag = w - cx; 
     264                        resizeType = RESIZE.R; 
     265                    } 
    206266                } 
    207                 else if (cx >= xw - border.r) { 
    208                     xDrag = w - cx; 
    209                     resizeType = RESIZE.R; 
    210                 } 
    211                 if (cy < y + border.t) { 
    212                     yDrag = h + cy; 
    213                     resizeType |= RESIZE.T; 
    214                 } 
    215                 else if (cy >= yh - border.b) { 
    216                     yDrag = h - cy; 
    217                     resizeType |= RESIZE.B; 
     267                if (isHSizable) { 
     268                    if (cy < y + border.t) { 
     269                        yDrag = h + cy; 
     270                        resizeType |= RESIZE.T; 
     271                    } 
     272                    else if (cy >= yh - border.b) { 
     273                        yDrag = h - cy; 
     274                        resizeType |= RESIZE.B; 
     275                    } 
    218276                } 
    219277                 
    220                 gui_.addClickCallback (&endCallback); 
    221                 gui_.addMotionCallback (&resizeCallback); 
     278                if (resizeType != RESIZE.NONE) {    // only if some valid size is being done 
     279                    gui_.addClickCallback (&endCallback); 
     280                    gui_.addMotionCallback (&resizeCallback); 
     281                } 
    222282            } else {                // window is being moved 
    223283                xDrag = cx - x; 
     
    253313            nw = xDrag - cx; 
    254314            if (nw < mw) nw = mw;       // clamp 
    255             setPosition (x + w - nw, y); 
     315            mw = x + w - nw;            // reuse 
    256316            setSize (nw, h); 
     317            setPosition (mw, y); 
    257318        } 
    258319        else if (resizeType & RESIZE.R) { 
    259320            setSize (xDrag + cx, h); 
     321            setPosition (x, y); 
    260322        } 
    261323        if (resizeType & RESIZE.T) { 
    262324            int mh, nh; 
    263             getMinimalSize (nh, mh);    // (only want mh) 
     325            getMinimalSize (nh, mh); 
    264326            nh = yDrag - cy; 
    265             if (nh < mh) nh = mh;       // clamp 
    266             setPosition (x, y + h - nh)
     327            if (nh < mh) nh = mh; 
     328            mh = y + h - nh
    267329            setSize (w, nh); 
     330            setPosition (x, mh); 
    268331        } 
    269332        else if (resizeType & RESIZE.B) { 
    270333            setSize (w, yDrag + cy); 
     334            setPosition (x, y); 
    271335        } 
    272336    } 
     
    285349    //END Window moving and resizing 
    286350     
    287     char[] name;                    // The window's name (id from config file) 
     351    // Load/save data: 
     352    public char[] name;             // The window's name (id from config file) 
     353    //bool edited = false;            // True if any widgets have been edited (excluding scaling) 
     354    // Data used for saving and loading (null in between): 
     355    int[][widgetID] widgetData = null;// Data for all widgets under this window 
     356    int[] mutableData = null;       // Widget's mutable data (adjusted sizes, etc.) 
     357     
    288358    IGui gui_;                      // The gui managing this window 
    289      
    290     int[][widgetID] widgetData;     // Data for all widgets under this window (deleted after loading) 
    291     IWidget[] widgets;              // List of all widgets under this window (created on demand). Use for saving? 
     359    IRenderer rend;                 // The window's renderer 
     360     
    292361    IWidget widget;                 // The primary widget in this window. 
    293362     
    294     IRenderer rend;                 // The window's renderer 
    295     // FIXME: revise which parameters are stored once Gui knows window position 
    296     int x,y;                        // Window position 
     363    int x = -1, y = -1;             // Window position 
    297364    int w,h;                        // Window size (calculated from Widgets) 
    298365    int xw, yh;                     // x+w, y+h (frequent use by clickEvent) 
  • mde/gui/widget/createWidget.d

    r37 r39  
    2525/** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation 
    2626* data [1..$]. */ 
    27 IWidget createWidget (IWindow window, IWidget parent, int[] data) 
     27IWidget createWidget (IWindow window, int[] data) 
    2828in { 
    2929    assert (window !is null, "createWidget: window is null"); 
    30     assert (parent !is null, "createWidget: parent is null"); 
    3130} body { 
    3231    if (data.length < 1) throw new WidgetDataException ("No widget data"); 
    3332    int type = data[0];     // type is first element of data 
    34     data = data[1..$];      // the rest is passed to the Widget 
     33    // the whole of data is passed to the Widget 
    3534     
    3635    mixin (binarySearch ("type", WIDGETS)); 
     
    101100        foreach (c; consts) { 
    102101            ret ~= "if (" ~ var ~ " == WIDGET_TYPE." ~ c ~ ") {\n" ~ 
    103                     indent(indents+1) ~ "return new " ~ c ~ "Widget (window, parent, data);\n" ~ 
     102                    indent(indents+1) ~ "return new " ~ c ~ "Widget (window, data);\n" ~ 
    104103                    indent(indents) ~ "} else "; 
    105104        } 
  • mde/gui/widget/layout.d

    r38 r39  
    1818 
    1919import mde.gui.widget.Widget; 
    20 import mde.gui.exception : WidgetDataException
     20import mde.gui.exception
    2121 
    2222/** Encapsulates a grid of Widgets. 
     
    2525class GridLayoutWidget : Widget 
    2626{ 
    27     this (IWindow wind, IWidget, int[] data) { 
     27    this (IWindow wind, int[] data) { 
    2828        // Get grid size and check data 
    2929        // Check sufficient data for rows, cols, and at least one widget: 
    30         if (data.length < 3) throw new WidgetDataException; 
    31         rows = data[0]; 
    32         cols = data[1]; 
    33         if (data.length != 2 + rows * cols) throw new WidgetDataException; 
     30        if (data.length < 4) throw new WidgetDataException; 
     31        super (wind, data); 
     32         
     33        rows = data[1]; 
     34        cols = data[2]; 
     35        if (data.length != 3 + rows * cols) throw new WidgetDataException; 
    3436        /* data.length >= 3 so besides checking the length is correct, this tells us: 
    35          *      rows * cols >= 3 - 2 = 1            a free check! 
     37         *      rows * cols >= 4 - 3 = 1            a free check! 
    3638         * The only thing not checked is whether both rows and cols are negative, which would 
    3739         * cause an exception when dynamic arrays are allocated later (and is unlikely). */ 
    38          
    39         window = wind; 
    4040         
    4141        // Get all sub-widgets 
    4242        subWidgets.length = rows*cols; 
    4343        foreach (i, inout subWidget; subWidgets) { 
    44             subWidget = window.makeWidget (data[i+2], this); 
    45         } 
     44            subWidget = window.makeWidget (data[i+3]); 
     45        } 
     46    } 
     47     
     48    int[] adjust (int[] data) { 
     49        // Give all sub-widgets their data: 
     50        foreach (widget; subWidgets) 
     51            data = widget.adjust (data); 
     52        if (data.length < rows + cols) throw new MutableDataException; 
     53         
     54        /* We basically short-cut setSize by loading previous col/row sizes and doing the final 
     55         * calculations. There isn't checks that the data is valid/up-to-date... worst case is too 
     56         * small overlapping widgets or huge ones? 
     57         * Note: if setSize gets called afterwards, it should have same dimensions and so not do 
     58         * anything. */ 
     59        colW = data[0..cols]; 
     60        rowH = data[cols..rows+cols]; 
     61        setColRowSizes; 
     62        w = colW[$-1] + colX[$-1]; 
     63        h = rowY[$-1] + rowH[$-1]; 
     64         
     65        return data[rows+cols..$]; 
     66    } 
     67     
     68    int[] getCreationData () { 
     69        int[] ret; 
     70        ret.length = 3 + subWidgets.length; 
     71         
     72        ret [0..3] = [widgetType, rows, cols];  // first data 
     73         
     74        foreach (i,widget; subWidgets)          // sub widgets 
     75            ret[i+3] = window.addCreationData (widget); 
     76         
     77        return ret; 
     78    } 
     79    int[] getMutableData () { 
     80        int[] ret; 
     81        foreach (widget; subWidgets) 
     82            ret ~= widget.getMutableData; 
     83         
     84        ret ~= colW ~ rowH; 
     85        return ret; 
    4686    } 
    4787     
     
    109149     
    110150    void setSize (int nw, int nh) { 
     151        /* For each of width and height, there are several cases: 
     152         *  [new value is] more than old value 
     153         *  ->  enlarge any row/column 
     154         *  same as old value 
     155         *  ->  do nothing 
     156         *  more than min but less than current value 
     157         *  ->  find an enlarged row/col and reduce size 
     158         *  ->  repeat if necessary 
     159         *  minimal value or less 
     160         *  -> clamp to min and use min col/row sizes 
     161         */ 
     162        // FIXME: implement! 
     163         
    111164        // Step 1: calculate the minimal row/column sizes. 
    112165        alias w mw;             // no need for extra vars, just use these 
     
    133186        } 
    134187         
    135         // Step 3: set each sub-widget's size. 
    136         foreach (i,widget; subWidgets) 
    137             widget.setSize (colW[i % cols], rowH[i / cols]); 
    138          
    139188        w = nw; 
    140189        h = nh; 
    141190         
     191        // Step 3: set each sub-widget's size. 
    142192        // Step 4: calculate the column and row positions 
    143         colX.length = cols; 
    144         rowY.length = rows; 
    145         int spacing = window.renderer.layoutSpacing; 
    146          
    147         int cum = 0; 
    148         foreach (i, x; rowH) { 
    149             rowY[i] = cum; 
    150             cum += x + spacing; 
    151         } 
    152          
    153         cum = 0; 
    154         foreach (i, x; colW) { 
    155             colX[i] = cum; 
    156             cum += x + spacing; 
    157         } 
     193        setColRowSizes; 
    158194         
    159195        // Step 5: position needs resetting 
    160         // FIXME: find a more efficient method of doing this? 
    161         // maybe setPosition should ALWAYS be called after setSize? 
    162         setPosition (x,y); 
     196        // Currently this happens by specifying that setPosition should be run after setSize. 
    163197    } 
    164198     
     
    229263    } 
    230264     
     265    void setColRowSizes () { 
     266        // Calculate column and row locations: 
     267        colX.length = cols; 
     268        rowY.length = rows; 
     269        int spacing = window.renderer.layoutSpacing; 
     270         
     271        int cum = 0; 
     272        foreach (i, x; rowH) { 
     273            rowY[i] = cum; 
     274            cum += x + spacing; 
     275        } 
     276         
     277        cum = 0; 
     278        foreach (i, x; colW) { 
     279            colX[i] = cum; 
     280            cum += x + spacing; 
     281        } 
     282         
     283        // Tell subwidgets their new sizes: 
     284        foreach (i,widget; subWidgets) 
     285            widget.setSize (colW[i % cols], rowH[i / cols]); 
     286    } 
     287     
    231288protected: 
    232289    int cols, rows;     // number of cells in grid 
  • mde/scheduler/Scheduler.d

    r30 r39  
    120120    * IDs. */ 
    121121    ID getNewID () { 
    122         // For now use a very simple method to find a vacant ID: iterate 
    123         for (ID i = 0xF000_0000; i < ID.max; ++i) 
    124             if ((i in funcs) is null) 
    125                 return i; 
     122        if (funcs.length == 0) return 0xF000_0000;  // otherwise would get an out-of-bounds error 
     123        // Take the last used ID and add one, making sure it's at least 0xF000_0000. 
     124        // Don't bother checking if it's out of bounds since there's 2^28 available IDs. 
     125        ID i = funcs.keys[$-1] + 1; 
     126        if (i < 0xF000_0000) i = 0xF000_0000; 
     127        return i; 
    126128    } 
    127129     
  • mde/scheduler/init2.d

    r36 r39  
    5151void guiLoad () {   // init func 
    5252    try { 
    53         gui.load ("gui"); 
     53        gui.load (GUI); 
     54        cleanup.addFunc (&guiSave, "guiSave"); 
    5455    } catch (Exception e) { 
    5556        logger.fatal ("guiLoad failed: " ~ e.msg); 
     
    5758    } 
    5859} 
     60void guiSave () {   // cleanup func 
     61    try { 
     62        gui.save (GUI); 
     63    } catch (Exception e) { 
     64        logger.fatal ("guiSave failed: " ~ e.msg); 
     65        setInitFailure; 
     66    } 
     67} 
     68private const GUI = "gui"; 
    5969 
    6070void initInput () { // init func 
  • mde/scheduler/initFunctions.d

    r32 r39  
    6666 
    6767InitStage init;     // all functions called during init (all should be thread-safe) 
     68//FIXME: implement: 
     69InitStage save;     // all functions to be called to save data (possible to run more than once) 
    6870InitStage cleanup;  // all functions called during cleanup (all should be thread-safe) 
    6971