Changeset 91:4d5d53e4f881

Show
Ignore:
Timestamp:
10/16/08 12:43:48 (3 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/jobs.txt

    r90 r91  
    44 
    55In progress: 
    6 Layout alignment sharing for instances of same widgetID. 
    76 
    87 
    98 
    109Bugs: 
    11 Sometimes nothing is drawn until a resize, and fonts are blocks? External bug? 
     10Sometimes nothing is drawn until a resize, and fonts are blocks? External bug? Info: doesn't happen when limited to one thread. 
    1211 
    1312 
  • codeDoc/todo.txt

    r90 r91  
    33 
    44 
     5Content: 
     6Need a way for a single content to contain multiple "sub-contents", and a way for widgets to be bound to a sub-content. 
     7->  how 
     8    ->  Use indecies; widget's use an integer value to choose index (or a string)? 
     9    ->  Protection: read-only and read-write contents? 
     10->  why 
     11    ->  Dynamic lists of name, value and possibly description fields. 
     12        ->  Done in a basic case, so is it needed? 
     13Generic Content creation: 
     14->  Created centrally, e.g. from options or translation strings. 
     15    ->  Creator keeps a hash-map of all created content, so that if the same content is asked for again the same content object will be returned, not a new object (besides efficiency, this keeps content synchronized). 
     16->  Generic ContentList type for instancing on at run-time. 
     17->  Items in ContentList may represent a cluster of items; e.g. an option plus it's name and description. These may be special classes, but should use a generic interface allowing getting sub-contents (e.g. value/name/desc.) via an index. 
     18 
     19 
    520GUI: 
    621->  Widgets: 
    7 ->  rethink how widgets are created and receive creation data, so that they don't have to be created by the Window 
    822->  scripted widgets 
    923->  decent rendering/theme system 
    10 ->  lists from content lists 
    1124 
    1225 
    1326Scratchpad area for ideas: 
    14  
    15  
    16 Redesign: 
    17 ->  possibilities 
    18     ->  How widgets get their sub-widgets: 
    19         ->  (current) Ask manager to create widget from manager's data with ID from widget's data. 
    20             ->  seems to work well; allows widgets some control over creation of children 
    21             ->  now extended to support instancing & passing content; seems to fulfill requirements 
    22         ->  (alternate) Pass widget a list of pointers to all sub-widgets 
    23             ->  manager creates all static (from data files) widets 
    24             ->  other widgets can create dynamic widgets as or to pass to sub-widgets 
    25             ->  widget data needs a portion which is explicitly IDs for subwidgets, or data needs reforming into a tree 
  • data/conf/gui.mtt

    r90 r91  
    33<char[]|Design="Working"> 
    44{Working} 
    5 <WidgetData|root={0:[0xC100,3,3],1:["square","blank","square","blank","content","blank","square","blank","square"]}> 
     5<WidgetData|root={0:[0xC100,0,3,3],1:["square","blank","square","blank","content","blank","square","blank","square"]}> 
    66<WidgetData|square={0:[0x1,6,6]}> 
    7 <WidgetData|content={0:[0xC100,4,2],1:["floating","button","blank","blank","blank","opts","blank","blank"]}> 
    8 <WidgetData|button={0:[0x10,200,200]}> 
     7<WidgetData|content={0:[0xC100,0,4,2],1:["floating","button","blank","blank","blank","opts","blank","blank"]}> 
     8<WidgetData|button={0:[0x10,50,50]}> 
    99<WidgetData|blank={0:[0x2]}> 
    10 <WidgetData|opts={0:[0x8110],1:["optBox"]}> 
    11 <WidgetData|optBox={0:[0xC100, 1,3],1:["optVal","optSep","optVal"]}> 
    12 <WidgetData|optVal={0:[0x4020, 0xfe8c00]}> 
    13 <WidgetData|optSep={0:[0x21, 0xff],1:["-=-"]}> 
     10<WidgetData|opts={0:[0x8110,0],1:["optBox"]}> 
     11<WidgetData|optBox={0:[0xC100,1,1,3],1:["optName","optSep","optVal"]}> 
     12<WidgetData|optName={0:[0x4020, 1, 0xfe8c00]}> 
     13<WidgetData|optVal={0:[0x4020, 0, 0xBF00]}> 
     14<WidgetData|optSep={0:[0x21, 0xff],1:["="]}> 
    1415<WidgetData|floating={0:[0x8200,20,20],1:["text"]}> 
    1516<WidgetData|text={0:[0x21,0xFF0000],1:["Floating text"]}> 
  • data/conf/options.mtt

    r89 r91  
    11{MT01} 
    22{misc} 
    3 <int|maxThreads=4
     3<int|maxThreads=1
    44<bool|exitImmediately=false> 
    55<char[]|L10n="en-GB"> 
    66<int|logOptions=0x3000> 
    77<double|pollInterval=0.01> 
     8<char[]|a="tildb\naeouc\ngpqyg"> 
     9<char[]|b="tildb"> 
     10<char[]|c="aeouc"> 
     11<char[]|g="gpqy"> 
     12<char[]|z="fghijklpq"> 
    813 
    914{font} 
  • mde/gui/WidgetDataSet.d

    r81 r91  
    6767    } 
    6868     
    69     // Per-widget data
    70     protected WidgetData[widgetID] widgetData; 
     69protected
     70    WidgetData[widgetID] widgetData;    // Per-widget data: 
    7171} 
    7272 
  • mde/gui/WidgetManager.d

    r90 r91  
    9696            if (dg (cast(wdabs)cx, cast(wdabs)cy, b, state)) return; 
    9797         
    98         // NOTE: do we need to test if the click was on the gui (and thus child)? 
    99         // FIXME: yes, unless we can guarantee this! 
     98        // test the click was on the child widget 
     99        // cx/cy are unsigned, thus >= 0. Widget starts at (0,0) 
     100        if (cx >= child.width || cy >= child.height) { 
     101            debug logger.warn ("WidgetManager received click not on child; potentially an error"); 
     102            return; 
     103        } 
    100104        IChildWidget widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); 
    101105        if (widg !is null) 
     
    388392    IChildWidget makeWidget (widgetID id, IContent content = null) { 
    389393        debug (mdeWidgets) logger.trace ("Creating widget \""~id~'"'); 
    390         return createWidget (this, curData[id], content); 
     394        return createWidget (this, id, curData[id], content); 
    391395    } 
    392396     
  • mde/gui/content/Content.d

    r90 r91  
    3535 * state, and a tabbed box could show a tab based on this. Or could represent an option. 
    3636 */ 
     37//TODO - write a generic IContent displaying widget. Also a generic editable? 
    3738// Don't include dimension/drawing stuff because it's renderer specific and content should not be! 
    3839// NOTE: an interface or a class? 
     
    5152    +/ 
    5253     
    53     /** Every Content should be convertible to a string, which, if possible, should be a sensible 
    54      * conversion of its content. */ 
    55     char[] toString (); 
     54     
     55     
     56    /** Generically return strings. 
     57     * 
     58     * Every Content should return a string for i == 0; preferably its value. Other values of i 
     59     * can be used to return other strings. For unsupported values of i, null should be returned. 
     60     */ 
     61    char[] toString (uint i); 
    5662} 
    57  
     63/+ 
     64/** Extension to interface providing text-specific tools. */ 
     65interface IContentText : IContent 
     66
     67    char[] text ();            /// Get/set the value. 
     68    void text (char[] v);      /// ditto 
     69
     70+/ 
     71/+ FIXME - use content lists or drop? 
    5872/** Get a content from the list (what list?). */ 
    5973ContentText getContentText (char[] id) { 
     
    6579    return new ContentInt (42); // forget the list for now 
    6680} 
     81+/ 
    6782 
     83/+FIXME - currently unused 
    6884/** Text content. */ 
    6985/* May end up extending a universal content type. 
     
    136152    int int_; 
    137153} 
     154+/ 
  • mde/gui/content/options.d

    r79 r91  
    5454} 
    5555 
     56//FIXME - todo.txt 
    5657class ContentOptionText : ContentOption 
    5758{ 
     
    6364    } 
    6465     
    65     char[] toString () { 
     66    char[] toString (uint i) { 
     67        if (i == 0) 
     68            return opts.get!(char[])(symb); 
     69        else if (i == 1) 
     70            return name_; 
     71        else if (i == 2) 
     72            return desc_; 
     73    } 
     74    /+char[] value () { 
    6675        return opts.get!(char[])(symb); 
    6776    } 
    6877    void value (char[] v) { 
    6978        opts.set!(char[])(symb, v); 
    70     } 
     79    }+/ 
    7180} 
    7281 
    7382abstract class ContentOption : IContent 
    7483{ 
    75     abstract char[] toString (); 
    76      
    77     //char[] value ();      /// Get/set the value. 
    78     void value (char[] v);  /// ditto 
    79      
    8084    // Get the symbol name (useful?) 
    81      
     85    /+ 
    8286    /// Get the translated name 
    8387    char[] name () { 
     
    8993        return desc_; 
    9094    } 
    91      
     95    +/ 
    9296protected: 
    9397    Options opts;   // the set of options within which our option lies 
  • mde/gui/exception.d

    r75 r91  
    3333class WidgetDataException : GuiException 
    3434{ 
    35     this () {   // Default, by Widget class's this 
    36         super ("Bad widget data"); 
     35    this (Object o) {   // Default, by Widget class's this / WDCheck 
     36        super ("Bad widget data for "~o.classinfo.name); 
    3737    } 
    38     this (char[] msg) { // From createWidget 
     38
     39 
     40class ContentException : GuiException 
     41
     42    char[] getSymbol () { 
     43        return super.getSymbol ~ ".content"; 
     44    } 
     45    this () { 
     46        super ("Unexpected content type"); 
     47    } 
     48    this (char[] msg) { 
    3949        super (msg); 
    4050    } 
  • mde/gui/renderer/SimpleRenderer.d

    r80 r91  
    9595    } 
    9696 
    97     void drawWidgetBack (wdim x, wdim y, wdim w, wdim h) {} 
     97    void drawWidgetBack (wdim x, wdim y, wdim w, wdim h) { 
     98        debug { 
     99            gl.setColor (0f, .2f, .2f); 
     100            gl.drawBox (x,y, w,h); 
     101        } 
     102    } 
    98103     
    99104    void drawBlank (wdim x, wdim y, wdim w, wdim h) { 
  • mde/gui/widget/Floating.d

    r90 r91  
    5656class FloatingAreaWidget : SizableWidget 
    5757{ 
    58     this (IWidgetManager mgr, WidgetData data) { 
     58    this (IWidgetManager mgr, widgetID id, WidgetData data) { 
    5959        subWidgets.length = data.strings.length; 
    6060        foreach (i,s; data.strings) 
     
    6464        if (data.ints.length != 1) { 
    6565            if (data.ints.length != 2*subWidgets.length + 1) { 
    66                 throw new WidgetDataException
     66                throw new WidgetDataException (this)
    6767            } 
    6868            foreach (i, ref c; sWCoords) { 
     
    7272        } 
    7373         
    74         super (mgr, data); 
     74        super (mgr, id, data); 
    7575         
    7676        foreach (w; subWidgets) { 
  • mde/gui/widget/Ifaces.d

    r90 r91  
    6262    IChildWidget makeWidget (widgetID id, IContent content = null); 
    6363     
    64     /** Record some changes, for saving. */ 
     64    /** Record some changes, for saving. Should only be called from IWidget.saveChanges() to avoid 
     65     * multiple calls for instanced widgets of same id. */ 
    6566    void setData (widgetID id, WidgetData); 
    6667     
     
    146147     * If the widget has subwidgets, it should also be recursively called on these (passing their  
    147148     * ids). */ 
     149    // FIXME - no longer necessary to pass id! 
    148150    bool saveChanges (widgetID id); 
    149151     
     
    165167     
    166168//BEGIN Size and position 
    167     /** is the width / height resizable? 
    168      * 
    169      * If not, the widget has fixed dimensions equal the output of getMinimalSize. */ 
     169    /** Is the width / height resizable? 
     170     * 
     171     * This really means does the widget benifit from being enlarged? Any widget should occupy 
     172     * additional area when expanded. 
     173     * 
     174     * If not, the widget has fixed dimensions equal to it's minimal size. */ 
    170175    bool isWSizable (); 
    171176    bool isHSizable (); /// ditto 
     
    173178    /** The minimal size the widget could be shrunk to (or its fixed size). 
    174179     * 
    175      * Takes into account child-widgets and any other contents. */ 
     180     * Takes into account child-widgets and any other contents. 
     181     *  
     182     * Note: layout uses these calls to initialize it's alignment device. So, after creating a 
     183     * (layout) widget, minWidth should be the first function called on it! */ 
    176184    wdim minWidth (); 
    177185    wdim minHeight ();  /// ditto 
    178186     
    179     /** Get the current size of the widget. 
    180      *  
    181      * Deprecated: is it needed now? 
    182      */ 
    183     deprecated void getCurrentSize (out wdim w, out wdim h); 
     187    /** Get the current size of the widget. */ 
     188    wdim width (); 
     189    wdim height();      /// ditto 
    184190     
    185191    /** Used to adjust the size. 
     
    192198     *        Most widgets can simply ignore it. 
    193199     * 
    194      * If called with dimensions less than minWidth/minHeight return: the widget may set its size 
    195      * to either the dimension given or its minimal dimension (even though this is larger). If the 
    196      * larger size is set, events won't be received in the extra area. FIXME: sort out rendering. 
    197      * Otherwise, the dimensions should always be set exactly. 
     200     * A widget should never be resized smaller than it's minimal size (if it is, it should assume 
     201     * it's minimal size and print a warning when in debug mode). 
     202     * A "fixed" size widget should enlarge itself as requested. 
    198203     * 
    199204     * setPosition must be called after calling either setWidth or setHeight. */ 
  • mde/gui/widget/TextWidget.d

    r90 r91  
    2727import mde.font.font; 
    2828 
     29/// Adapter to ease use of ContentLabelWidget 
     30struct TextAdapter { 
     31    void set (char[] c, int col) { 
     32        //FIXME tie font to renderer or so 
     33        if (font is null) font = FontStyle.get("default"); 
     34         
     35        content = c; 
     36        colour = Colour (col); 
     37    } 
     38     
     39    void getDimensions (out wdsize w, out wdsize h) { 
     40        font.updateBlock (content, textCache); 
     41        w = cast(wdim) textCache.w; 
     42        h = cast(wdim) textCache.h; 
     43    } 
     44     
     45    void draw (wdabs x, wdabs y) { 
     46        font.textBlock (x,y, content, textCache, colour); 
     47    } 
     48     
     49    char[] content; 
     50    TextBlock textCache; 
     51    Colour colour; 
     52    static FontStyle font; 
     53} 
     54 
    2955/// Basic text widget 
    3056class TextLabelWidget : Widget 
     
    3662     * where contentID is an ID for the string ID of the contained content 
    3763     * and colour is an 8-bit-per-channel RGB colour of the form 0xRRGGBB. */ 
    38     this (IWidgetManager mgr, WidgetData data) { 
     64    this (IWidgetManager mgr, widgetID id, WidgetData data) { 
    3965        WDCheck (data, 2, 1); 
    40         if (font is null) font = FontStyle.get("default"); 
    41         font.updateBlock (data.strings[0], textCache); 
    42         mw = cast(wdim) textCache.w; 
    43         mh = cast(wdim) textCache.h; 
    44         colour = Colour (data.ints[1]); 
    45         super (mgr,data); 
    46     } 
    47      
    48     void draw () { 
    49         super.draw(); 
    50         font.textBlock (x,y, text, textCache, colour); 
    51     } 
    52      
    53 protected: 
    54     char[] text; 
    55     Colour colour; 
    56     TextBlock textCache; 
    57     static FontStyle font; 
    58 
    59  
    60  
    61 /// Adapter to ease use of ContentLabelWidget 
    62 struct ContentLabelAdapter { 
    63     void set (IContent c, int col) { 
    64         if (font is null) font = FontStyle.get("default"); 
    65          
    66         content = c; 
    67         colour = Colour (cast(ubyte) (col >> 16u), 
    68                          cast(ubyte) (col >> 8u), 
    69                          cast(ubyte) col ); 
    70     } 
    71      
    72     void getDimensions (out wdsize w, out wdsize h) { 
    73         font.updateBlock (content.toString, textCache); 
    74         w = cast(wdim) textCache.w; 
    75         h = cast(wdim) textCache.h; 
    76     } 
    77      
    78     void draw (wdabs x, wdabs y) { 
    79         font.textBlock (x,y, content.toString, textCache, colour); 
    80     } 
    81      
    82     IContent content; 
    83     TextBlock textCache; 
    84     Colour colour; 
    85     static FontStyle font; 
    86 
    87  
    88 /// Basic widget displaying a label from a content. 
    89 class ContentLabelWidget : Widget 
    90 
    91     this (IWidgetManager mgr, WidgetData data, IContent c) { 
    92         WDCheck (data, 2, 0); 
    93         adapter.set (c, data.ints[1]); 
     66        adapter.set (data.strings[0], data.ints[1]); 
    9467        adapter.getDimensions (mw, mh); 
    95         super (mgr,data); 
     68        super (mgr, id, data); 
    9669    } 
    9770     
     
    10275     
    10376protected: 
    104     ContentLabelAdapter adapter; 
     77    TextAdapter adapter; 
    10578} 
     79 
     80/// Basic widget displaying a label from a content. 
     81class ContentLabelWidget : Widget 
     82{ 
     83    this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { 
     84        WDCheck (data, 3, 0); 
     85        content = c; 
     86        index = data.ints[1]; 
     87        adapter.set (content.toString(index), data.ints[2]); 
     88        adapter.getDimensions (mw, mh); 
     89        super (mgr, id,data); 
     90    } 
     91     
     92    void draw () { 
     93        super.draw(); 
     94        adapter.draw (x,y); 
     95    } 
     96     
     97protected: 
     98    TextAdapter adapter; 
     99    IContent content; 
     100    int index; 
     101} 
  • mde/gui/widget/Widget.d

    r80 r91  
    2626import mde.gui.exception; 
    2727 
    28  
    29 /************************************************************************************************* 
    30  * Widgets may use WDCheck as a utility to check what data holds. Its use is encouraged, so that 
    31  * the checks can easily be updated should WidgetData be changed. 
    32  *  
    33  * Params: 
    34  *  data    = the WidgetData to check lengths of 
    35  *  n_ints  = number of integers wanted 
    36  *  n_strings= number of strings (default 0 since not all widgets use strings) 
    37  *************************************************************************************************/ 
    38 void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) { 
    39     if (data.ints.length    != n_ints || 
    40         data.strings.length != n_strings) 
    41         throw new WidgetDataException; 
     28debug { 
     29    import tango.util.log.Log : Log, Logger; 
     30    private Logger logger; 
     31    static this () { 
     32        logger = Log.getLogger ("mde.gui.widget.Widget"); 
     33    } 
    4234} 
    4335 
     
    5446//BEGIN Load and save 
    5547    // Base this() for child Widgets. 
    56     this (IWidgetManager mgr, WidgetData data) { 
     48    this (IWidgetManager mgr, widgetID, WidgetData) { 
    5749        this.mgr = mgr; 
    5850    } 
     
    8173    } 
    8274     
     75    wdim width () { 
     76        return w; 
     77    } 
     78    wdim height() { 
     79        return h; 
     80    } 
     81     
    8382    deprecated void getCurrentSize (out wdim cw, out wdim ch) { 
    8483        cw = w; 
     
    8988     * enlarging, so in both cases this is a correct implementation. */ 
    9089    void setWidth (wdim nw, int) { 
     90        debug if (nw < mw) logger.warn ("Widget width set below minimal size"); 
    9191        w = (nw >= mw ? nw : mw); 
    9292    } 
    9393    void setHeight (wdim nh, int) { 
     94        debug if (nh < mh) logger.warn ("Widget height set below minimal size"); 
    9495        h = (nh >= mh ? nh : mh); 
    9596    } 
     
    104105    /* This method is only called when the location is over this widget; hence for all widgets 
    105106     * without children this method is valid. */ 
    106     IChildWidget getWidget (wdim,wdim) { 
     107    IChildWidget getWidget (wdim cx, wdim cy) { 
     108        debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)"); 
    107109        return this; 
    108110    } 
     
    118120     
    119121protected: 
     122    /********************************************************************************************** 
     123     * Widgets may use WDCheck as a utility to check what data holds. Its use is encouraged, so 
     124     * that the checks can easily be updated should WidgetData be changed. 
     125     *  
     126     * Params: 
     127     *  data    = the WidgetData to check lengths of 
     128     *  n_ints  = number of integers wanted 
     129     *  n_strings= number of strings (default 0 since not all widgets use strings) 
     130     *********************************************************************************************/ 
     131    void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) { 
     132    if (data.ints.length    != n_ints || 
     133        data.strings.length != n_strings) 
     134        throw new WidgetDataException (this); 
     135    } 
     136     
    120137    IWidgetManager mgr;     // the enclosing window 
    121138    wdim x, y;          // position 
     
    133150     * [widgetID, w, h] 
    134151     * where w, h is the fixed size. */ 
    135     this (IWidgetManager mgr, WidgetData data) { 
    136         super (mgr, data); 
     152    this (IWidgetManager mgr, widgetID id, WidgetData data) { 
     153        super (mgr, id, data); 
    137154        mw = cast(wdim) data.ints[1]; 
    138155        mh = cast(wdim) data.ints[2]; 
     
    146163    // Check data.length is at least 1 before calling! 
    147164    /// Constructor for a completely resizable [blank] widget. 
    148     this (IWidgetManager mgr, WidgetData data) { 
    149         super (mgr, data); 
     165    this (IWidgetManager mgr, widgetID id, WidgetData data) { 
     166        super (mgr, id, data); 
    150167    } 
    151168     
  • mde/gui/widget/createWidget.d

    r90 r91  
    4747 * --- 
    4848 *************************************************************************************************/ 
    49 IChildWidget createWidget (IWidgetManager mgr, WidgetData data, IContent content) 
     49IChildWidget createWidget (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) 
    5050in { 
    5151    assert (mgr !is null, "createWidget: mgr is null"); 
     
    6262    // Not returned a new widget... 
    6363    logger.error ("Bad widget type: {}; creating a debug widget instead.",type); 
    64     return new DebugWidget (mgr, data); 
     64    return new DebugWidget (mgr, id, data); 
    6565} 
    6666 
     
    130130                    "   debug (mdeWidgets) logger.trace (\"Creating new "~c~"Widget.\");\n" ~ 
    131131                    "   static if (WIDGET_TYPE."~c~" & WIDGET_TYPE.TAKES_CONTENT)\n" ~ 
    132                     "       return new " ~ c ~ "Widget (mgr, data, content);\n" ~ 
     132                    "       return new " ~ c ~ "Widget (mgr, id, data, content);\n" ~ 
    133133                    "   else\n" ~ 
    134                     "       return new " ~ c ~ "Widget (mgr, data);\n" ~ 
     134                    "       return new " ~ c ~ "Widget (mgr, id, data);\n" ~ 
    135135                    "} else "; 
    136136        } 
  • mde/gui/widget/layout.d

    <
    r90 r91  
    2323import mde.gui.content.options; 
    2424import mde.gui.content.Content; 
     25 
     26import tango.util.container.HashMap; 
    2527 
    2628debug { 
     
    4749     * 
    4850     * Widget uses the initialisation data: 
    49      * [widgetID, r, c, w11, w12, ..., w1c, ..., wr1, ..., wrc] 
    50      * where r and c are the number of rows and columns, and wij is the ID (from parent Window's 
     51     * --- 
     52     * ints = [widget_type, align_flags, rows, cols] 
     53     * // or with column widths and row heights: 
     54     * ints = [widget_type, align_flags, rows, cols, col1width, ..., colCwidth, row1height, ..., rowRheight] 
     55     * strings = [w11, w12, ..., w1C, ..., wR1, ..., wRC] 
     56     * --- 
     57     * where R and C are the number of rows and columns, and wij is the ID (from parent Window's 
    5158     * list) for the widget in row i and column j. The number of parameters must be r*c + 3. 
    5259     *  
    5360     * The content parameter is passed on to all children accepting an IContent. */ 
    54     this (IWidgetManager mgr, WidgetData data, IContent content) { 
     61    this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) { 
    5562        // Get grid size and check data 
    56         // Check sufficient data for rows, cols, and possibly row/col widths. 
    57         if (data.ints.length < 3) throw new WidgetDataException
    58          
    59         rows = data.ints[1]; 
    60         cols = data.ints[2]; 
     63        // Check sufficient data for type, align-flags, rows, cols, and possibly row/col widths. 
     64        if (data.ints.length < 4) throw new WidgetDataException (this)
     65         
     66        rows = data.ints[2]; 
     67        cols = data.ints[3]; 
    6168        // Check: at least one sub-widget, ints length == 3 or also contains row & col widths, 
    6269        // strings' length is correct: 
    6370        if (rows < 1 || cols < 1 || 
    64             (data.ints.length != 3 && data.ints.length != 3 + rows + cols) || 
     71            (data.ints.length != 4 && data.ints.length != 4 + rows + cols) || 
    6572            data.strings.length != rows * cols) 
    66             throw new WidgetDataException
     73            throw new WidgetDataException (this)
    6774        this.data = data; 
    6875         
     
    7380        } 
    7481         
    75         super (mgr, data); 
    76          
    77         if (data.ints.length == 3 + rows + cols) { 
    78             col.setWidths (cast(wdim[]) data.ints[3..cols+3]); 
    79             row.setWidths (cast(wdim[]) data.ints[cols+3..$]); 
    80         } else { 
    81             col.setWidths; 
    82             row.setWidths; 
    83         } 
    84         adjustCache; 
     82        if (data.ints.length == 4 + rows + cols) 
     83            initWidths = cast(wdim[]) data.ints[4..$]; 
     84         
     85        super (mgr, id, data); 
    8586    } 
    8687     
     
    9192                widget.saveChanges (strings[i]); 
    9293             
    93             ints = ints[0..3] ~ cast(int[])col.width ~ cast(int[])row.width; 
     94            ints = ints[0..4] ~ cast(int[])col.width ~ cast(int[])row.width; 
    9495        } 
    9596        mgr.setData (id, data); 
     
    106107class TrialContentLayoutWidget : GridWidget 
    107108{ 
    108     this (IWidgetManager mgr, WidgetData data) { 
     109    this (IWidgetManager mgr, widgetID id, WidgetData data) { 
    109110        debug scope (failure) 
    110111                logger.warn ("TrialContentLayoutWidget: failure"); 
    111         WDCheck (data, 1, 1); 
     112        WDCheck (data, 2, 1); 
    112113         
    113114        OptionList optsList = OptionList.trial(); 
     
    120121            subWidgets[i] = mgr.makeWidget (data.strings[0], c); 
    121122        } 
    122         super (mgr, data); 
    123          
    124         // Set col/row widths to minimals. 
    125         col.setWidths; 
    126         row.setWidths; 
    127         adjustCache; 
     123        super (mgr, id, data); 
    128124    } 
    129125     
     
    152148     * the call to genCachedConstructionData can be moved to the derived this() methods.) 
    153149     *  
    154      * Derived constructors should call setWidths on col and row, and then call 
    155      * adjustCache, after calling this. */ 
    156     protected this (IWidgetManager mgr, WidgetData data) { 
    157         super (mgr, data); 
     150     * Derived constructors may also set initWidths to the array of column widths followed by 
     151     * row heights used to initially set the row/column dimensions. */ 
     152    protected this (IWidgetManager mgr, widgetID id, WidgetData data) { 
     153        super (mgr, id, data); 
    158154         
    159155        // Create cell aligners with appropriate col/row adjustment function 
    160         col = (new AlignColumns (cols)).addSetCallback (&setColWidth); 
    161         row = (new AlignColumns (rows)).addSetCallback (&setRowHeight); 
     156        if (data.ints[1] & 1) 
     157            col = AlignColumns.getInstance (id, cols); 
     158        else 
     159            col = (new AlignColumns (cols)); 
     160        col.addSetCallback (&setColWidth); 
     161        if (data.ints[1] & 2) 
     162            row = AlignColumns.getInstance (id~"R", rows);      // id must be unique to that for cols! 
     163        else 
     164            row = (new AlignColumns (rows)); 
     165        row.addSetCallback (&setRowHeight); 
    162166         
    163167        // Calculate cached construction data 
     
    165169    } 
    166170     
    167     /** Generates cached mutable data. 
    168      * 
    169      * Should be called by adjust() after calling setWidths. */ 
    170     void adjustCache () { 
    171         // Generate cached mutable data 
    172         // Calculate column and row locations: 
    173         w = col.genPositions; 
    174         h = row.genPositions; 
    175          
    176         // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. 
    177         foreach (i,widget; subWidgets) { 
    178             // Resizing direction is arbitrarily set to negative: 
    179             widget.setWidth  (col.width[i % cols], -1); 
    180             widget.setHeight (row.width[i / cols], -1); 
    181         } 
     171    /** Responsible for calculating the minimal size and initializing some stuff. 
     172     * 
     173     * As such, this must be the first function called after this(). */ 
     174    wdim minWidth () { 
     175        if (!alignInit) {       // assumes col & row.width are initialized simultaneously 
     176            alignInit = true; 
     177            if (initWidths) { 
     178                debug assert (initWidths.length == cols + rows, "initWidths provided but has bad length"); 
     179                col.setWidths (initWidths[0..cols]); 
     180                row.setWidths (initWidths[cols..$]); 
     181                initWidths = null;  // free 
     182            } else { 
     183                col.setWidths; 
     184                row.setWidths; 
     185            } 
     186             
     187            mw = col.mw; 
     188            mh = row.mw; 
     189            w = col.w; 
     190            h = row.w; 
     191             
     192            // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. 
     193            foreach (i,widget; subWidgets) { 
     194                // Resizing direction is arbitrarily set to negative: 
     195                widget.setWidth  (col.width[i % cols], -1); 
     196                widget.setHeight (row.width[i / cols], -1); 
     197            } 
     198        } 
     199        return mw; 
    182200    } 
    183201    //END Creation & saving 
     
    192210     
    193211    void setWidth (wdim nw, int dir) { 
    194         if (nw == w) return; 
    195          
    196         w += col.adjustCellSizes (nw - w, (dir == -1 ? col.lastSizable : col.firstSizable), dir); 
    197          
     212        w = col.resizeWidth (nw, dir); 
    198213        // Note: setPosition must be called after! 
    199214    } 
    200215    void setHeight (wdim nh, int dir) { 
    201         if (nh == h) return; 
    202          
    203         h += row.adjustCellSizes (nh - h, (dir == -1 ? row.lastSizable : row.firstSizable), dir); 
    204          
     216        h = row.resizeWidth (nh, dir); 
    205217        // Note: setPosition must be called after! 
    206218    } 
     
    210222        this.y = y; 
    211223         
     224        debug assert (col.pos && row.pos, "setPosition: col/row.pos not set (code error)"); 
    212225        foreach (i,widget; subWidgets) 
    213226            widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]); 
     
    219232    IChildWidget getWidget (wdim cx, wdim cy) { 
    220233        debug scope (failure) 
    221                 logger.warn ("getWidget: failure"); 
     234            logger.warn ("getWidget: failure; values: click, pos, width - {}, {}, {} - {}, {}, {}", cx, x, w, cy, y, h); 
     235        debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)"); 
     236         
    222237        // Find row/column: 
    223238        myDiff i = col.getCell (cx - x); 
     
    240255             
    241256            // find col/row's resizeD & resizeU 
    242             if (col.findResize (cx - x) && row.findResize (cy - y)) 
     257            if (col.findResizeCols (cx - x) && row.findResizeCols (cy - y)) 
    243258                return;     // unable to resize 
    244259             
     
    264279     * Also need to be re-run if the renderer changes. 
    265280     * 
    266      * rows, cols and subWidgets must be set before calling. */ 
     281     * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns 
     282     * (col and row). */ 
    267283    void genCachedConstructionData () { 
    268284        // Will only change if renderer changes: 
     
    270286         
    271287        // Calculate the minimal column and row sizes: 
    272         // set length, making sure the arrays are initialised to zero: 
    273         col.minWidth = new wdim[cols]; 
    274         row.minWidth = new wdim[rows]; 
     288        // AlignColumns (row, col) takes care of initializing minWidth. 
    275289        foreach (i,widget; subWidgets) { 
    276290            // Increase dimensions if current minimal size is larger: 
     
    283297        } 
    284298         
    285          
    286         // Calculate the overall minimal size, starting with the spacing: 
    287         mh = mgr.renderer.layoutSpacing;    // use mh temporarily 
    288         mw = mh * cast(wdim)(cols - 1); 
    289         mh *= cast(wdim)(rows - 1); 
    290          
    291         foreach (x; col.minWidth)       // add the column/row's dimensions 
    292             mw += x; 
    293         foreach (x; row.minWidth) 
    294             mh += x; 
    295          
    296          
    297299        // Find which cols/rows are resizable: 
    298         // reset: 
    299         col.sizable = new bool[cols]; 
    300         row.sizable = new bool[rows]; 
    301         col.firstSizable = row.firstSizable = -1; 
    302          
     300        // AlignColumns initializes sizable, and sets first and last sizables. 
    303301        forCols: 
    304302        for (myIt i = 0; i < cols; ++i) {               // for each column 
     
    306304                if (!subWidgets[i+j].isWSizable)    // column not resizable 
    307305                    continue forCols;           // continue the outer for loop 
    308                  
     306             
    309307            // column is resizable if we get to here 
    310308            col.sizable[i] = true; 
    311             if (col.firstSizable < 0) 
    312                 col.firstSizable = i; 
    313             col.lastSizable = i; 
    314309        } 
    315310         
     
    320315                    continue forRows; 
    321316             
    322             row.lastSizable = i / cols; 
    323             row.sizable[row.lastSizable] = true; 
    324             if (row.firstSizable < 0) 
    325                 row.firstSizable = row.lastSizable; 
     317            row.sizable[i / cols] = true; 
    326318        } 
    327319    } 
     
    343335    //BEGIN Col/row resizing callback