Changeset 91:4d5d53e4f881
- Timestamp:
- 10/16/08 12:43:48 (3 months ago)
- Files:
-
- codeDoc/jobs.txt (modified) (1 diff)
- codeDoc/todo.txt (modified) (1 diff)
- data/conf/gui.mtt (modified) (1 diff)
- data/conf/options.mtt (modified) (1 diff)
- mde/gui/WidgetDataSet.d (modified) (1 diff)
- mde/gui/WidgetManager.d (modified) (2 diffs)
- mde/gui/content/Content.d (modified) (4 diffs)
- mde/gui/content/options.d (modified) (3 diffs)
- mde/gui/exception.d (modified) (1 diff)
- mde/gui/renderer/SimpleRenderer.d (modified) (1 diff)
- mde/gui/widget/Floating.d (modified) (3 diffs)
- mde/gui/widget/Ifaces.d (modified) (5 diffs)
- mde/gui/widget/TextWidget.d (modified) (3 diffs)
- mde/gui/widget/Widget.d (modified) (8 diffs)
- mde/gui/widget/createWidget.d (modified) (3 diffs)
- mde/gui/widget/layout.d (modified) (27 diffs)
- mde/gui/widget/miscWidgets.d (modified) (4 diffs)
- mde/setup/Init.d (modified) (8 diffs)
- mde/setup/InitStage.d (modified) (3 diffs)
- mde/setup/Screen.d (modified) (2 diffs)
- mde/setup/paths.d (modified) (1 diff)
- mde/types/Colour.d (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r90 r91 4 4 5 5 In progress: 6 Layout alignment sharing for instances of same widgetID.7 6 8 7 9 8 10 9 Bugs: 11 Sometimes nothing is drawn until a resize, and fonts are blocks? External bug? 10 Sometimes nothing is drawn until a resize, and fonts are blocks? External bug? Info: doesn't happen when limited to one thread. 12 11 13 12 codeDoc/todo.txt
r90 r91 3 3 4 4 5 Content: 6 Need 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? 13 Generic 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 5 20 GUI: 6 21 -> Widgets: 7 -> rethink how widgets are created and receive creation data, so that they don't have to be created by the Window8 22 -> scripted widgets 9 23 -> decent rendering/theme system 10 -> lists from content lists11 24 12 25 13 26 Scratchpad area for ideas: 14 15 16 Redesign:17 -> possibilities18 -> 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 children21 -> now extended to support instancing & passing content; seems to fulfill requirements22 -> (alternate) Pass widget a list of pointers to all sub-widgets23 -> manager creates all static (from data files) widets24 -> other widgets can create dynamic widgets as or to pass to sub-widgets25 -> widget data needs a portion which is explicitly IDs for subwidgets, or data needs reforming into a treedata/conf/gui.mtt
r90 r91 3 3 <char[]|Design="Working"> 4 4 {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"]}> 6 6 <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]}> 9 9 <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:["="]}> 14 15 <WidgetData|floating={0:[0x8200,20,20],1:["text"]}> 15 16 <WidgetData|text={0:[0x21,0xFF0000],1:["Floating text"]}> data/conf/options.mtt
r89 r91 1 1 {MT01} 2 2 {misc} 3 <int|maxThreads= 4>3 <int|maxThreads=1> 4 4 <bool|exitImmediately=false> 5 5 <char[]|L10n="en-GB"> 6 6 <int|logOptions=0x3000> 7 7 <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"> 8 13 9 14 {font} mde/gui/WidgetDataSet.d
r81 r91 67 67 } 68 68 69 // Per-widget data:70 protected WidgetData[widgetID] widgetData;69 protected: 70 WidgetData[widgetID] widgetData; // Per-widget data: 71 71 } 72 72 mde/gui/WidgetManager.d
r90 r91 96 96 if (dg (cast(wdabs)cx, cast(wdabs)cy, b, state)) return; 97 97 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 } 100 104 IChildWidget widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); 101 105 if (widg !is null) … … 388 392 IChildWidget makeWidget (widgetID id, IContent content = null) { 389 393 debug (mdeWidgets) logger.trace ("Creating widget \""~id~'"'); 390 return createWidget (this, curData[id], content);394 return createWidget (this, id, curData[id], content); 391 395 } 392 396 mde/gui/content/Content.d
r90 r91 35 35 * state, and a tabbed box could show a tab based on this. Or could represent an option. 36 36 */ 37 //TODO - write a generic IContent displaying widget. Also a generic editable? 37 38 // Don't include dimension/drawing stuff because it's renderer specific and content should not be! 38 39 // NOTE: an interface or a class? … … 51 52 +/ 52 53 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); 56 62 } 57 63 /+ 64 /** Extension to interface providing text-specific tools. */ 65 interface 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? 58 72 /** Get a content from the list (what list?). */ 59 73 ContentText getContentText (char[] id) { … … 65 79 return new ContentInt (42); // forget the list for now 66 80 } 81 +/ 67 82 83 /+FIXME - currently unused 68 84 /** Text content. */ 69 85 /* May end up extending a universal content type. … … 136 152 int int_; 137 153 } 154 +/ mde/gui/content/options.d
r79 r91 54 54 } 55 55 56 //FIXME - todo.txt 56 57 class ContentOptionText : ContentOption 57 58 { … … 63 64 } 64 65 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 () { 66 75 return opts.get!(char[])(symb); 67 76 } 68 77 void value (char[] v) { 69 78 opts.set!(char[])(symb, v); 70 } 79 }+/ 71 80 } 72 81 73 82 abstract class ContentOption : IContent 74 83 { 75 abstract char[] toString ();76 77 //char[] value (); /// Get/set the value.78 void value (char[] v); /// ditto79 80 84 // Get the symbol name (useful?) 81 85 /+ 82 86 /// Get the translated name 83 87 char[] name () { … … 89 93 return desc_; 90 94 } 91 95 +/ 92 96 protected: 93 97 Options opts; // the set of options within which our option lies mde/gui/exception.d
r75 r91 33 33 class WidgetDataException : GuiException 34 34 { 35 this ( ) { // Default, by Widget class's this36 super ("Bad widget data ");35 this (Object o) { // Default, by Widget class's this / WDCheck 36 super ("Bad widget data for "~o.classinfo.name); 37 37 } 38 this (char[] msg) { // From createWidget 38 } 39 40 class ContentException : GuiException 41 { 42 char[] getSymbol () { 43 return super.getSymbol ~ ".content"; 44 } 45 this () { 46 super ("Unexpected content type"); 47 } 48 this (char[] msg) { 39 49 super (msg); 40 50 } mde/gui/renderer/SimpleRenderer.d
r80 r91 95 95 } 96 96 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 } 98 103 99 104 void drawBlank (wdim x, wdim y, wdim w, wdim h) { mde/gui/widget/Floating.d
r90 r91 56 56 class FloatingAreaWidget : SizableWidget 57 57 { 58 this (IWidgetManager mgr, WidgetData data) {58 this (IWidgetManager mgr, widgetID id, WidgetData data) { 59 59 subWidgets.length = data.strings.length; 60 60 foreach (i,s; data.strings) … … 64 64 if (data.ints.length != 1) { 65 65 if (data.ints.length != 2*subWidgets.length + 1) { 66 throw new WidgetDataException ;66 throw new WidgetDataException (this); 67 67 } 68 68 foreach (i, ref c; sWCoords) { … … 72 72 } 73 73 74 super (mgr, data);74 super (mgr, id, data); 75 75 76 76 foreach (w; subWidgets) { mde/gui/widget/Ifaces.d
r90 r91 62 62 IChildWidget makeWidget (widgetID id, IContent content = null); 63 63 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. */ 65 66 void setData (widgetID id, WidgetData); 66 67 … … 146 147 * If the widget has subwidgets, it should also be recursively called on these (passing their 147 148 * ids). */ 149 // FIXME - no longer necessary to pass id! 148 150 bool saveChanges (widgetID id); 149 151 … … 165 167 166 168 //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. */ 170 175 bool isWSizable (); 171 176 bool isHSizable (); /// ditto … … 173 178 /** The minimal size the widget could be shrunk to (or its fixed size). 174 179 * 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! */ 176 184 wdim minWidth (); 177 185 wdim minHeight (); /// ditto 178 186 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 184 190 185 191 /** Used to adjust the size. … … 192 198 * Most widgets can simply ignore it. 193 199 * 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. 198 203 * 199 204 * setPosition must be called after calling either setWidth or setHeight. */ mde/gui/widget/TextWidget.d
r90 r91 27 27 import mde.font.font; 28 28 29 /// Adapter to ease use of ContentLabelWidget 30 struct 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 29 55 /// Basic text widget 30 56 class TextLabelWidget : Widget … … 36 62 * where contentID is an ID for the string ID of the contained content 37 63 * 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) { 39 65 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]); 94 67 adapter.getDimensions (mw, mh); 95 super (mgr, data);68 super (mgr, id, data); 96 69 } 97 70 … … 102 75 103 76 protected: 104 ContentLabelAdapter adapter;77 TextAdapter adapter; 105 78 } 79 80 /// Basic widget displaying a label from a content. 81 class 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 97 protected: 98 TextAdapter adapter; 99 IContent content; 100 int index; 101 } mde/gui/widget/Widget.d
r80 r91 26 26 import mde.gui.exception; 27 27 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; 28 debug { 29 import tango.util.log.Log : Log, Logger; 30 private Logger logger; 31 static this () { 32 logger = Log.getLogger ("mde.gui.widget.Widget"); 33 } 42 34 } 43 35 … … 54 46 //BEGIN Load and save 55 47 // Base this() for child Widgets. 56 this (IWidgetManager mgr, WidgetData data) {48 this (IWidgetManager mgr, widgetID, WidgetData) { 57 49 this.mgr = mgr; 58 50 } … … 81 73 } 82 74 75 wdim width () { 76 return w; 77 } 78 wdim height() { 79 return h; 80 } 81 83 82 deprecated void getCurrentSize (out wdim cw, out wdim ch) { 84 83 cw = w; … … 89 88 * enlarging, so in both cases this is a correct implementation. */ 90 89 void setWidth (wdim nw, int) { 90 debug if (nw < mw) logger.warn ("Widget width set below minimal size"); 91 91 w = (nw >= mw ? nw : mw); 92 92 } 93 93 void setHeight (wdim nh, int) { 94 debug if (nh < mh) logger.warn ("Widget height set below minimal size"); 94 95 h = (nh >= mh ? nh : mh); 95 96 } … … 104 105 /* This method is only called when the location is over this widget; hence for all widgets 105 106 * 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)"); 107 109 return this; 108 110 } … … 118 120 119 121 protected: 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 120 137 IWidgetManager mgr; // the enclosing window 121 138 wdim x, y; // position … … 133 150 * [widgetID, w, h] 134 151 * 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); 137 154 mw = cast(wdim) data.ints[1]; 138 155 mh = cast(wdim) data.ints[2]; … … 146 163 // Check data.length is at least 1 before calling! 147 164 /// 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); 150 167 } 151 168 mde/gui/widget/createWidget.d
r90 r91 47 47 * --- 48 48 *************************************************************************************************/ 49 IChildWidget createWidget (IWidgetManager mgr, WidgetData data, IContent content)49 IChildWidget createWidget (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) 50 50 in { 51 51 assert (mgr !is null, "createWidget: mgr is null"); … … 62 62 // Not returned a new widget... 63 63 logger.error ("Bad widget type: {}; creating a debug widget instead.",type); 64 return new DebugWidget (mgr, data);64 return new DebugWidget (mgr, id, data); 65 65 } 66 66 … … 130 130 " debug (mdeWidgets) logger.trace (\"Creating new "~c~"Widget.\");\n" ~ 131 131 " 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" ~ 133 133 " else\n" ~ 134 " return new " ~ c ~ "Widget (mgr, data);\n" ~134 " return new " ~ c ~ "Widget (mgr, id, data);\n" ~ 135 135 "} else "; 136 136 } mde/gui/widget/layout.d
r90 r91 23 23 import mde.gui.content.options; 24 24 import mde.gui.content.Content; 25 26 import tango.util.container.HashMap; 25 27 26 28 debug { … … 47 49 * 48 50 * 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 51 58 * list) for the widget in row i and column j. The number of parameters must be r*c + 3. 52 59 * 53 60 * 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) { 55 62 // 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]; 61 68 // Check: at least one sub-widget, ints length == 3 or also contains row & col widths, 62 69 // strings' length is correct: 63 70 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) || 65 72 data.strings.length != rows * cols) 66 throw new WidgetDataException ;73 throw new WidgetDataException (this); 67 74 this.data = data; 68 75 … … 73 80 } 74 81 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); 85 86 } 86 87 … … 91 92 widget.saveChanges (strings[i]); 92 93 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; 94 95 } 95 96 mgr.setData (id, data); … … 106 107 class TrialContentLayoutWidget : GridWidget 107 108 { 108 this (IWidgetManager mgr, WidgetData data) {109 this (IWidgetManager mgr, widgetID id, WidgetData data) { 109 110 debug scope (failure) 110 111 logger.warn ("TrialContentLayoutWidget: failure"); 111 WDCheck (data, 1, 1);112 WDCheck (data, 2, 1); 112 113 113 114 OptionList optsList = OptionList.trial(); … … 120 121 subWidgets[i] = mgr.makeWidget (data.strings[0], c); 121 122 } 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); 128 124 } 129 125 … … 152 148 * the call to genCachedConstructionData can be moved to the derived this() methods.) 153 149 * 154 * Derived constructors should call setWidths on col and row, and then call155 * 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); 158 154 159 155 // 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); 162 166 163 167 // Calculate cached construction data … … 165 169 } 166 170 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; 182 200 } 183 201 //END Creation & saving … … 192 210 193 211 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); 198 213 // Note: setPosition must be called after! 199 214 } 200 215 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); 205 217 // Note: setPosition must be called after! 206 218 } … … 210 222 this.y = y; 211 223 224 debug assert (col.pos && row.pos, "setPosition: col/row.pos not set (code error)"); 212 225 foreach (i,widget; subWidgets) 213 226 widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]); … … 219 232 IChildWidget getWidget (wdim cx, wdim cy) { 220 233 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 222 237 // Find row/column: 223 238 myDiff i = col.getCell (cx - x); … … 240 255 241 256 // 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)) 243 258 return; // unable to resize 244 259 … … 264 279 * Also need to be re-run if the renderer changes. 265 280 * 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). */ 267 283 void genCachedConstructionData () { 268 284 // Will only change if renderer changes: … … 270 286 271 287 // 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. 275 289 foreach (i,widget; subWidgets) { 276 290 // Increase dimensions if current minimal size is larger: … … 283 297 } 284 298 285 286 // Calculate the overall minimal size, starting with the spacing:287 mh = mgr.renderer.layoutSpacing; // use mh temporarily288 mw = mh * cast(wdim)(cols - 1);289 mh *= cast(wdim)(rows - 1);290 291 foreach (x; col.minWidth) // add the column/row's dimensions292 mw += x;293 foreach (x; row.minWidth)294 mh += x;295 296 297 299 // 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. 303 301 forCols: 304 302 for (myIt i = 0; i < cols; ++i) { // for each column … … 306 304 if (!subWidgets[i+j].isWSizable) // column not resizable 307 305 continue forCols; // continue the outer for loop 308 306 309 307 // column is resizable if we get to here 310 308 col.sizable[i] = true; 311 if (col.firstSizable < 0)312 col.firstSizable = i;313 col.lastSizable = i;314 309 } 315 310 … … 320 315 continue forRows; 321 316 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; 326 318 } 327 319 } … … 343 335 //BEGIN Col/row resizing callback <
