Changeset 31:baa87e68d7dc
- Timestamp:
- 04/29/08 13:10:58 (9 months ago)
- Files:
-
- codeDoc/gui/GUI notes.txt (added)
- codeDoc/jobs.txt (modified) (2 diffs)
- codeDoc/todo.txt (modified) (1 diff)
- data/conf/gui.mtt (modified) (1 diff)
- mde/SDL.d (modified) (2 diffs)
- mde/events.d (modified) (3 diffs)
- mde/gl.d (deleted)
- mde/gl/basic.d (added)
- mde/gl/draw.d (added)
- mde/gui/IWindow.d (deleted)
- mde/gui/Ifaces.d (added)
- mde/gui/Widget.d (modified) (8 diffs)
- mde/gui/Window.d (added)
- mde/gui/decoration.d (added)
- mde/gui/gui.d (modified) (5 diffs)
- mde/input/input.d (modified) (3 diffs)
- mde/input/joystick.d (modified) (1 diff)
- mde/mde.d (modified) (1 diff)
- mde/scheduler/Init.d (modified) (1 diff)
- mde/scheduler/Init2.d (added)
- mde/scheduler/InitFunctions.d (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r30 r31 4 4 5 5 In progress: 6 Started buttonWidget (on hold)7 6 8 7 … … 50 49 51 50 Done (for git log message): 52 Revamped Scheduler. Functions can be removed, have multiple schedules, have their scheduling changed, etc. 53 Scheduler has a unittest. Checked all pass. 54 Main loop scheduler moved to mde. Draw-on-demand currently disabled, simplifying this. 55 Made mtunitest.d remove the temporary file it uses afterwards. 51 Removed some circular dependencies which slipped in. As a result, the OpenGL code got separated into different files. 52 Enabled widgets to recieve events. 53 New IParentWidget interface allowing widgets to interact with their parents. 54 New Widget base class. 55 New WidgetDecoration class. 56 New ButtonWidget class responding to events (in a basic way). codeDoc/todo.txt
r26 r31 3 3 4 4 5 * means done 6 5 7 GUI: 6 8 -> Basic OpenGL code to: 7 -> create orthographic projection8 -> draw boxes9 -> maybe more (text, textures, ...)10 -> Windows with size & position9 ->* create orthographic projection 10 ->* draw boxes 11 -> maybe more (text, textures, ...) 12 ->* Windows with size & position 11 13 -> Widgets: 12 -> minimum size but expandable, auto-set 13 -> grid "layout" widgets 14 ->* minimum size but expandable, auto-set 15 -> no ability to resize yet except from config files 16 ->* grid "layout" widgets 17 -> scripted widgets 18 -> decent rendering/theme system 14 19 -> Text rendering 15 20 -> text library? data/conf/gui.mtt
r29 r31 1 1 {MT01} 2 2 {W1} 3 <int|x= 10>4 <int|y= 50>5 <int[][int]|widgetData=[0:[100 1,200,200]]>3 <int|x=0> 4 <int|y=0> 5 <int[][int]|widgetData=[0:[1003,200,200]]> 6 6 {W2} 7 7 <int|x=150> 8 8 <int|y=200> 9 <int[][int]|widgetData=[0:[1002,3,2,2,2,3,3, 3,3],2:[1001,150,150],3:[1001,100,100]]>9 <int[][int]|widgetData=[0:[1002,3,2,2,2,3,3,2,5],2:[1001,150,150],3:[1003,150,150],5:[1002,2,2,6,6,6,6],6:[1003,73,73]]> mde/SDL.d
r29 r31 21 21 import mde.input.joystick; 22 22 import mde.options; 23 import mde.gl ;23 import mde.gl.basic; 24 24 import global = mde.global; 25 25 … … 108 108 // OpenGL stuff: 109 109 glSetup(); 110 111 // Projection (mde.gl)112 110 setProjection (w, h); 113 111 mde/events.d
r30 r31 21 21 22 22 import mde.input.input; 23 import mde.input.exception;24 25 import mde.scheduler.InitFunctions;26 23 27 24 import derelict.sdl.events; … … 33 30 static this() { 34 31 logger = Log.getLogger ("mde.events"); 35 36 init.addFunc (&initInput, "initInput");37 }38 39 void initInput () { // init func40 try {41 global.input = new Input();42 global.input.loadConfig (); // (may also create instance)43 44 global.input.addButtonCallback (cast(Input.inputID) 0x0u, delegate void(Input.inputID i, bool b) {45 if (b) {46 logger.info ("Quiting...");47 global.run = false;48 }49 } );50 } catch (Exception) {51 setInitFailure;52 }53 32 } 54 33 … … 72 51 try { 73 52 global.input (event); 74 } catch ( InputClassException e) {53 } catch (Exception e) { 75 54 logger.error ("Caught input exception; event will be ignored. Exception was:"); 76 55 logger.error (e.msg); mde/gui/Widget.d
r30 r31 17 17 module mde.gui.Widget; 18 18 19 import mde.gui.I Window;19 import mde.gui.Ifaces; 20 20 import mde.gui.exception; 21 21 22 import gl = mde.gl; 23 import mde.scheduler.InitFunctions; 24 25 //BEGIN Iface and createWidget 26 /** Interface for widgets (may become a base class). 27 * 28 * A widget is a region of a GUI window which handles rendering and user-interaction for itself 29 * and is able to communicate with it's window and parent/child widgets as necessary. 30 * 31 * A widget's constructor should have this prototype: 32 * ---------------------------------- 33 * this (IWindow window, int[] data); 34 * ---------------------------------- 35 * Where window is the parent window and data is an array of initialisation data. The method should 36 * throw a WidgetDataException (created without parameters) if the data has wrong length or is 37 * otherwise invalid. 38 */ 39 //FIXME: check code reuse later! 40 interface IWidget 41 { 42 /** Draw, starting from given x and y. 43 * 44 * Maybe later enforce clipping of all sub-widget drawing, particularly for cases where only 45 * part of the widget is visible: scroll bars or a hidden window. */ 46 void draw (int x, int y); 47 48 /** Calculate the minimum size the widget could be shrunk to, taking into account 49 * child-widgets. */ 50 void getMinimumSize (out int w, out int h); 51 52 /** Get the current size of the widget. 53 * 54 * On the first call (during loading), this may be a value saved as part of the config or 55 * something else (e.g. revert to getMinimumSize). */ 56 void getCurrentSize (out int w, out int h); 57 } 58 22 import gl = mde.gl.basic; 23 24 import tango.io.Stdout; 25 26 //BEGIN createWidget 59 27 /// Widget types. Start high so they can be reordered easily later. 60 enum WIDGET_TYPE S: int {61 BOX = 1001, GRID 28 enum WIDGET_TYPE : int { 29 BOX = 1001, GRID, BUTTON 62 30 } 63 31 64 32 /** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation 65 33 * data [1..$]. */ 66 IWidget createWidget (IWindow window, int[] data) { 34 IWidget createWidget (IWindow window, IParentWidget parent, int[] data) 35 in { 36 assert (window !is null, "createWidget: window is null"); 37 assert (parent !is null, "createWidget: parent is null"); 38 } body { 67 39 if (data.length < 1) throw new WidgetDataException ("No widget data"); 68 40 int type = data[0]; // type is first element of data 69 41 data = data[1..$]; // the rest is passed to the Widget 70 42 71 if (type == WIDGET_TYPES.BOX) return new BoxWidget (window, data); 72 else if (type == WIDGET_TYPES.GRID) return new GridWidget (window, data); 43 if (type == WIDGET_TYPE.BOX) return new BoxWidget (window, parent, data); 44 else if (type == WIDGET_TYPE.GRID) return new GridWidget (window, parent, data); 45 else if (type == WIDGET_TYPE.BUTTON) return new ButtonWidget (window, parent, data); 73 46 else throw new WidgetDataException ("Bad widget type"); 74 47 } 75 //END Iface and createWidget 76 77 //BEGIN Widgets 78 /// Draws a box. That's it. 79 class BoxWidget : IWidget 80 { 81 int w, h; // size 82 83 this (IWindow, int[] data) { 84 if (data.length != 2) throw new WidgetDataException; 85 86 w = data[0]; 87 h = data[1]; 88 } 89 48 //END createWidget 49 50 /** A base widget class. Widgets need not inherit this (they only need implement IWidget), but this 51 * class provides a useful basic implementation for widgets. 52 * 53 * Technically this class could be instantiated, but it wouldn't do anything, not even draw itself. 54 */ 55 class Widget : IWidget 56 { 57 /** Basic draw method: draw the background */ 90 58 void draw (int x, int y) { 91 gl.setColor (1.0f, 1.0f, 0.0f); 92 gl.drawBox (x,x+w, y,y+h); 93 } 94 95 void getMinimumSize (out int w, out int h) { 96 w = h = 0; // box has no content 97 } 59 deco.setColor(); 60 gl.drawBox (x,y, w,h); 61 } 62 63 /** Dummy event method (ignore) */ 64 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {} 65 66 /** Minimum size is zero. */ 67 void getMinimumSize (out int w, out int h) {} // w,h initialised to 0 68 /** Current size. */ 98 69 void getCurrentSize (out int w, out int h) { 99 70 w = this.w; 100 71 h = this.h; 101 72 } 73 74 WidgetDecoration decoration () { 75 return deco; 76 } 77 78 protected: 79 WidgetDecoration deco; // the widget's decoration 80 int w, h; // size 81 } 82 83 //BEGIN Widgets 84 /// Draws a box. That's it. 85 class BoxWidget : Widget 86 { 87 this (IWindow, IParentWidget parent, int[] data) { 88 if (data.length != 2) throw new WidgetDataException; 89 90 deco = new WidgetDecoration (parent.decoration); 91 92 w = data[0] + 2*deco.border; 93 h = data[1] + 2*deco.border; 94 } 102 95 } 103 96 104 97 /// Encapsulates a grid of Widgets 105 class GridWidget : IWidget 106 { 107 //NOTE: maybe remove the padding and have each widget include a border? Or vice-versa (no borders on widgets)? 108 const PADDING = 3; // padding between rows/cols 109 const BORDER = 8; // border width 110 int w, h; // size 111 int rows, cols; // number of cells in grid 112 int[] rowH; // row height (highest widget in the row) 113 int[] colW; // column width (widest widget) 114 int[] rowY; // cumulative rowH[i-1] + BORDER/PADDING 115 int[] colX; // cumulative colW[i-1] + BORDER/PADDING 116 IWidget[] subWidgets; // all widgets in the grid (by row): 117 /* SubWidget order: [ 2 3 ] 118 * [ 0 1 ] */ 119 120 this (IWindow window, int[] data) { 98 class GridWidget : Widget 99 { 100 this (IWindow window, IParentWidget parent, int[] data) { 121 101 // Get grid size 122 102 if (data.length < 2) throw new WidgetDataException; … … 124 104 cols = data[1]; 125 105 106 deco = new WidgetDecoration (parent.decoration, TypeFlags.LAYOUT); 107 126 108 // Get all sub-widgets 109 // Check: correct data length and rows*cols >= 0 (know data.length - 2 >= 0). 127 110 if (data.length != 2 + rows * cols) throw new WidgetDataException; 128 111 subWidgets.length = rows*cols; 129 112 foreach (i, inout subWidget; subWidgets) { 130 subWidget = window.getWidget (data[i+2] );113 subWidget = window.getWidget (data[i+2], this); 131 114 } 132 115 … … 135 118 136 119 void draw (int x, int y) { 137 gl.setColor (1.0f, 0.6f, 0.0f);138 gl.drawBox (x, x+w, y,y+h);120 deco.setColor; 121 gl.drawBox (x,y, w,h); 139 122 140 123 foreach (i,widget; subWidgets) { … … 143 126 } 144 127 145 / ** Also recalculates row/column widths. */128 // Calculates from all rows and columns of widgets. 146 129 void getMinimumSize (out int w, out int h) { 147 130 if (rows*cols == 0) { // special case 148 w = h = 2* BORDER;131 w = h = 2*deco.border; 149 132 return; 150 133 } … … 157 140 // Find row heights and column widths (non cumulative) 158 141 rowH.length = rows; 159 colW.length = cols; 142 colW.length = cols; //WARNING: code reliant on these being initialised to zero 160 143 for (uint i = 0; i < subWidgets.length; ++i) { 161 144 uint x = i / cols; // row … … 168 151 rowY.length = rows; 169 152 colX.length = cols; 170 int cum = BORDER;153 int cum = deco.border; 171 154 foreach (i, x; rowH) { 172 155 rowY[i] = cum; 173 156 cum += x + PADDING; 174 157 } 175 h = cum + BORDER- PADDING; // total height176 cum = BORDER;158 h = cum + deco.border - PADDING; // total height 159 cum = deco.border; 177 160 foreach (i, x; colW) { 178 161 colX[i] = cum; 179 162 cum += x + PADDING; 180 163 } 181 w = cum + BORDER - PADDING; // total width 182 } 183 void getCurrentSize (out int wC, out int hC) { 184 wC = w; 185 hC = h; 186 } 187 } 188 /+ On hold until after next commit 164 w = cum + deco.border - PADDING; // total width 165 } 166 167 // Pass event on to relevant widget. Simply return if not on a widget. 168 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 169 if (rows*cols == 0) return; // special case 170 171 // Find the column 172 int i = cols - 1; // starting from right... 173 while (cx < colX[i]) { // decrement while left of this column 174 if (i == 0) return; // left of first column 175 --i; 176 } // now (cx >= colX[i]) 177 if (cx >= colX[i] + colW[i]) return; // between columns 178 179 // Find the row; 180 int j = rows - 1; 181 while (cy < rowY[j]) { 182 if (j == 0) return; 183 --j; 184 } 185 if (cy >= rowY[j] + rowH[j]) return; 186 187 // Now we know it's in widget (i,j)'s cell (but the widget may not take up the whole cell) 188 cx -= colX[i]; 189 cy -= rowY[j]; 190 IWidget widg = subWidgets[i + j*cols]; 191 widg.getCurrentSize (i,j); 192 if (cx < i && cy < j) 193 widg.clickEvent (cx, cy, b, state); 194 } 195 196 protected: 197 const PADDING = 4; // padding between rows/cols 198 int rows, cols; // number of cells in grid 199 int[] rowH; // row height (highest widget in the row) 200 int[] colW; // column width (widest widget) 201 int[] rowY; // cumulative rowH[i-1] + border and padding 202 int[] colX; // cumulative colW[i-1] + border and padding 203 IWidget[] subWidgets; // all widgets in the grid (by row): 204 /* SubWidget order: [ 2 3 ] 205 * [ 0 1 ] */ 206 } 207 189 208 /// First interactible widget 190 class ButtonWidget : IWidget 191 { 192 const BORDER = 5; // border width 193 int w, h; // size 209 class ButtonWidget : Widget 210 { 194 211 bool pushed = false;// true if button is pushed in 195 212 196 this (IWindow, int[] data) {213 this (IWindow, IParentWidget parent, int[] data) { 197 214 if (data.length != 2) throw new WidgetDataException; 198 215 199 w = data[0] + 2*BORDER; 200 h = data[1] + 2*BORDER; 216 deco = new WidgetDecoration (parent.decoration); 217 218 w = data[0] + 2*deco.border; 219 h = data[1] + 2*deco.border; 201 220 } 202 221 … … 206 225 else 207 226 gl.setColor (.6f, 0f, .6f); 208 gl.drawBox (x, x+w, y,y+h);227 gl.drawBox (x,y, w,h); 209 228 } 210 229 … … 213 232 h = this.h; 214 233 } 215 void getCurrentSize (out int w, out int h) { 216 w = this.w; 217 h = this.h; 218 } 219 220 } 221 +/ 234 235 void clickEvent (ushort, ushort, ubyte b, bool state) { 236 if (b == 1) pushed = state; // very basic 237 } 238 } 222 239 //END Widgets mde/gui/gui.d
r30 r31 14 14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 15 16 /// Base GUI module. 16 /** The GUI struct and Window class. 17 * 18 * Possibly add a GUIManager to update all active GUIs and pass coordinates (remapping if necessary). */ 17 19 module mde.gui.gui; 18 20 19 import mde.gui.IWindow; 20 import mde.gui.Widget; 21 import mde.gui.Window; 21 22 import mde.gui.exception; 22 23 … … 26 27 27 28 import mde.resource.paths; 28 import mde.scheduler.InitFunctions;29 30 import tango.scrapple.text.convert.parseTo : parseTo;31 import tango.scrapple.text.convert.parseFrom : parseFrom;32 29 33 30 import tango.util.log.Log : Log, Logger; … … 36 33 static this () { 37 34 logger = Log.getLogger ("mde.gui.gui"); 38 39 init.addFunc (&loadGUI, "loadGUI");40 35 } 41 36 42 37 GUI gui; // Currently just one instance; handle differently later. 43 // Wrap gui.load, since init doesn't handle delegates 44 // (do it this way since GUI handling will eventually be changed) 45 void loadGUI () { 46 gui.load(); 47 } 38 // Handle externally or with a GUI Manager? 48 39 49 40 /** A GUI handles a bunch of windows, all to be drawn to the same device. */ 41 /* NOTE: currently GUI just keeps a list of windows and draw and clickEvent simply calls them all. 42 * Coords should be stored (and functionality like z-order?). */ 50 43 struct GUI { 51 44 /** Load all windows from the file gui. */ 52 void load() { 53 static const fileName = "gui"; 45 void load(char[] fileName) { 54 46 if (!confDir.exists (fileName)) { 55 47 logger.error ("Unable to load GUI: no config file!"); … … 81 73 } 82 74 try { 83 //logger.trace ("1");84 int x;85 75 w.finalise(); 86 x = 6;87 76 windows ~= w; // only add if load successful 88 } catch ( WindowLoadException e) {77 } catch (Exception e) { 89 78 logger.error ("Window failed to load: " ~ e.msg); 90 79 } … … 101 90 } 102 91 92 /** Send an input event. 93 * 94 * I.e. send all mouse click events to all active GUIs, which check the coordinates and forward 95 * to any relevent windows. */ 96 void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 97 foreach (w; windows) 98 w.clickEvent (cx,cy,b,state); 99 } 100 103 101 private: 104 102 Window[] windows; 105 103 } 106 107 package: // Nothing else here is for external use.108 109 /** GUI Window class110 *111 * A window class instance does two things: (1) specify a region of the screen upon which the window112 * and its associated widgets are drawn, and (2) load, save, and generally manage all its widgets.113 *114 * Let the window load a table of widget data, of type int[][widgetID]. Each widget will, when115 * created, be given its int[] of data, which this() must confirm is valid (or throw).116 */117 class Window : mt.IDataSection, IWindow118 {119 alias int widgetID; // Widget ID type. Each ID is unique under this window. Type is int since this is the widget data type.120 121 /** Call after loading is finished to setup the window and confirm that it's valid.122 *123 * Throws: WindowLoadException. Do not use the instance in this case! */124 void finalise () {125 // Check data was loaded:126 if (widgetData is null) throw new WindowLoadException ("No widget data");127 128 // Create the primary widget (and indirectly all sub-widgets), throwing on error:129 widget = getWidget (0); // primary widget always has ID 0.130 131 widgetData = null; // data is no longer needed: allow GC to collect (cannot safely delete)132 133 widget.getCurrentSize (w,h);// Find the initial size134 w += BORDER_WIDTH * 2; // Adjust for border135 h += BORDER_WIDTH * 2;136 }137 138 /** Get/create a widget by ID.139 *140 * Should $(I only) be called internally and by sub-widgets! */141 IWidget getWidget (widgetID i)142 in {143 // widgetData is normally left to be garbage collected after widgets have been created:144 assert (widgetData !is null, "getWidget: widgetData is null");145 } body {146 // See if it's already been created:147 IWidget* p = i in widgets;148 if (p !is null) return *p; // yes149 else { // no150 int[]* d = i in widgetData;151 if (d is null) throw new WindowLoadException ("Widget not found");152 153 // Throws WidgetDataException (a WindowLoadException) if bad data:154 IWidget w = createWidget (this, *d);155 widgets[i] = w;156 return w;157 }158 }159 160 void requestRedraw () {161 //FIXME162 }163 164 void draw () {165 //BEGIN Window border/back166 gl.setColor (0.0f, 0.0f, 0.5f);167 gl.drawBox (x,x+w, y,y+h);168 //END Window border/back169 170 // Tell the widget to draw itself:171 widget.draw(x + BORDER_WIDTH, y + BORDER_WIDTH);172 }173 174 //BEGIN Mergetag code175 void addTag (char[] tp, mt.ID id, char[] dt) {176 if (tp == "int[][int]") {177 if (id == "widgetData") {178 widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt);179 }180 } else if (tp == "int") {181 if (id == "x") {182 x = parseTo!(int) (dt);183 } else if (id == "y") {184 y = parseTo!(int) (dt);185 }186 }187 }188 void writeAll (ItemDelg dlg) {189 }190 //END Mergetag code191 192 private:193 int[][widgetID] widgetData; // Data for all widgets under this window (deleted after loading)194 IWidget[widgetID] widgets; // List of all widgets under this window (created on demand).195 196 IWidget widget; // The primary widget in this window.197 int x,y; // Window position198 int w,h; // Window size (calculated from Widgets)199 200 const BORDER_WIDTH = 8; // Temporary way to handle window decorations201 202 }mde/input/input.d
r26 r31 149 149 * Mouse events don't need config for the GUI. Handle them first so that if no config exists 150 150 * some functionality at least is retained. 151 * 152 * Note that the mouse coordinates as reported by SDL put the top-left most pixel at 1,1. 153 * Internal coordinates put that pixel at 0,0 (see gui/GUI notes.txt). 151 154 */ 152 155 switch (event.type) { … … 154 157 case SDL_MOUSEBUTTONUP: 155 158 foreach (dg; mouseClickCallbacks) 156 dg (event.button.x, event.button.y, event.button.button, event.button.state == SDL_PRESSED); 159 dg (event.button.x - 1, event.button.y - 1, 160 event.button.button, event.button.state == SDL_PRESSED); 157 161 break; 158 162 159 163 case SDL_MOUSEMOTION: 160 mouse_x = event.motion.x ;161 mouse_y = event.motion.y ;164 mouse_x = event.motion.x - 1; 165 mouse_y = event.motion.y - 1; 162 166 break; 163 167 … … 293 297 /** Loads all configs, activating the requested id. 294 298 * 295 * Returns: true if the requested config id wasn't found. 296 */ 297 bool loadConfig (char[] profile = "Default") { 299 * Throws: ConfigLoadException if unable to load any configs or the requested config id wasn't 300 * found. 301 */ 302 void loadConfig (char[] profile = "Default") { 298 303 Config.load("input"); // FIXME: filename 299 304 Config* c_p = profile in Config.configs; 300 if (c_p) { 301 config = *c_p; 302 return false; 303 } 304 logger.error ("Config profile \""~profile~"\" not found: input won't work unless a valid profile is loaded!"); 305 return true; 305 if (c_p) config = *c_p; 306 else { 307 throw new ConfigLoadException; 308 logger.error ("Config profile \""~profile~"\" not found: input won't work unless a valid profile is loaded!"); 309 } 306 310 } 307 311 mde/input/joystick.d
r29 r31 54 54 // FIXME: this is sometimes causing a SIGSEGV (Address boundary error) 55 55 // FIXME: when init fails 56 debug logger.trace ("Closing joysticks (this sometimes fails when mde exits prematurely)"); 56 57 if(js !is null) SDL_JoystickClose(js); // only close if successfully opened 58 debug logger.trace ("Done closing joysticks"); 57 59 } 58 60 } mde/mde.d
r30 r31 23 23 24 24 import global = mde.global; // global.run 25 import gl = mde.gl ; // gl.draw25 import gl = mde.gl.draw; // gl.draw() 26 26 import mde.events; // pollEvents 27 27 mde/scheduler/Init.d
r30 r31 23 23 module mde.scheduler.Init; 24 24 25 import mde.scheduler.Init2; // This module is responsible for setting up some init functions. 25 26 import mde.scheduler.InitFunctions; 26 27 import mde.scheduler.exception; mde/scheduler/InitFunctions.d
r30 r31 24 24 module mde.scheduler.InitFunctions; 25 25 26 /+ unused 26 27 import tango.util.log.Log : Log, Logger; 27 28 static this() { 28 29 logger = Log.getLogger ("mde.scheduler.InitFunctions"); 29 } 30 }+/ 30 31 31 32 void setInitFailure () { /// Call to indicate failure in an init function … … 37 38 { 38 39 struct InitFunction { 39 void function() func; // the actual function40 void delegate() func; // the actual function 40 41 char[] name; // it's name; 41 42 } … … 48 49 * thread exceptions is not guaranteed to work. Log a message, call setFailure() and return 49 50 * instead. */ 50 void addFunc (void function() f, char[] name) {51 void addFunc (void delegate() f, char[] name) { 51 52 InitFunction s; 52 53 s.func = f; 54 s.name = name; 55 funcs ~= s; 56 } 57 void addFunc (void function() f, char[] name) { /// ditto 58 InitFunction s; 59 s.func.funcptr = f; 53 60 s.name = name; 54 61 funcs ~= s; … … 65 72 66 73 private: 67 Logger logger; 68 /+ I keep changing my mind about wrapping all init functions: 69 const LOG_MSG = "Init function "; 70 const TRACE_START = " - running"; 71 const TRACE_END = " - completed"; 72 const FAIL_MSG = " - failed: "; 73 // Template to call function, catching exceptions: 74 void initInput(alias Func, char[] name) () { 75 try { 76 debug logger.trace (LOG_MSG ~ name ~ TRACE_START); 77 Func(); 78 debug logger.trace (LOG_MSG ~ name ~ TRACE_END); 79 } catch (Exception e) { 80 logger.fatal (LOG_MSG ~ name ~ FAIL_MSG ~ e.msg); 81 initFailure = true; 82 } 83 } 84 +/ 74 //Logger logger;
