Changeset 30:467c74d4804d
- Timestamp:
- 04/28/08 05:59:47
(9 months ago)
- Author:
- Diggory Hardy <diggory.hardy@gmail.com>
- branch:
- default
- convert_revision:
- 370135618612be4dee7d72022ab12c3d6f694b72
- Message:
Major changes to the scheduler, previously only used by the main loop.
Revamped Scheduler. Functions can be removed, have multiple schedules, have their scheduling changed, etc.
Scheduler has a unittest. Checked all pass.
Main loop scheduler moved to mde. Draw-on-demand currently disabled, simplifying this.
Made mtunitest.d remove the temporary file it uses afterwards.
committer: Diggory Hardy <diggory.hardy@gmail.com>
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r29 |
r30 |
|
| 4 | 4 | |
|---|
| 5 | 5 | In progress: |
|---|
| | 6 | Started buttonWidget (on hold) |
|---|
| 6 | 7 | |
|---|
| 7 | 8 | |
|---|
| 8 | | To do: |
|---|
| 9 | | Also see todo.txt. |
|---|
| 10 | | * Windows building/compatibility (currently partial) |
|---|
| 11 | | * gdc building/compatibility (wait for tango 0.99.5 release?) |
|---|
| 12 | | * OutOfMemoryException is not currently checked for â it should be at least in critical places (use high-level catching of all errors?). |
|---|
| 13 | | * Sensitivity adjustments. From es_a_out: |
|---|
| | 9 | |
|---|
| | 10 | To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): |
|---|
| | 11 | Also see todo.txt and FIXME/NOTE comment marks. |
|---|
| | 12 | 4 OutOfMemoryException is not currently checked for â it should be at least in critical places (use high-level catching of all errors?). |
|---|
| | 13 | 3 on-event draw support (mde.events and GUI need to tell mde.mde) |
|---|
| | 14 | 3 Scheduler for drawing only windows which need redrawing. |
|---|
| | 15 | 3 Update scheduler as outlined in FIXME. |
|---|
| | 16 | 3 Windows building/compatibility (currently partial) |
|---|
| | 17 | 2 Command-line options for paths to by-pass normal path finding functionality. |
|---|
| | 18 | 2 Consider replacing byte/short types with int type |
|---|
| | 19 | 2 File loading from compressed archives |
|---|
| | 20 | 2 gdc building/compatibility (wait for tango 0.99.5 release?) |
|---|
| | 21 | 2 Sensitivity adjustments. From es_a_out: |
|---|
| 14 | 22 | /+ FIXME: revise. |
|---|
| 15 | 23 | + I can't see any point using HALF_RANGE here, since it should really be used dependant on |
|---|
| … | … | |
| 38 | 46 | myThis.axis[cast(inputID) s.pop()] = y; |
|---|
| 39 | 47 | +/ |
|---|
| | 48 | 1 Mergetag binary support |
|---|
| 40 | 49 | |
|---|
| 41 | 50 | |
|---|
| 42 | 51 | Done (for git log message): |
|---|
| 43 | | GUI: Implemented a GridWidget to layout several sub-widgets. |
|---|
| 44 | | Improved log messages about init functions. |
|---|
| 45 | | Moved all dynamic-library loading into a separate init stage. |
|---|
| | 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. |
|---|
| r29 |
r30 |
|
| 18 | 18 | |
|---|
| 19 | 19 | import global = mde.global; |
|---|
| 20 | | static import mde.SDL; |
|---|
| | 20 | import sdl = mde.SDL; // resizeWindow |
|---|
| 21 | 21 | |
|---|
| 22 | 22 | import mde.input.input; |
|---|
| … | … | |
| 24 | 24 | |
|---|
| 25 | 25 | import mde.scheduler.InitFunctions; |
|---|
| 26 | | import mde.scheduler.runTime; |
|---|
| 27 | 26 | |
|---|
| 28 | 27 | import derelict.sdl.events; |
|---|
| 29 | 28 | |
|---|
| | 29 | import tango.time.Time; |
|---|
| 30 | 30 | import tango.util.log.Log : Log, Logger; |
|---|
| 31 | 31 | |
|---|
| … | … | |
| 35 | 35 | |
|---|
| 36 | 36 | init.addFunc (&initInput, "initInput"); |
|---|
| 37 | | |
|---|
| 38 | | Scheduler.perFrame (&mde.events.pollEvents); |
|---|
| 39 | 37 | } |
|---|
| 40 | 38 | |
|---|
| … | … | |
| 55 | 53 | } |
|---|
| 56 | 54 | |
|---|
| 57 | | void pollEvents (double) { |
|---|
| | 55 | void pollEvents (TimeSpan) { |
|---|
| 58 | 56 | SDL_Event event; |
|---|
| 59 | 57 | while (SDL_PollEvent (&event)) { |
|---|
| … | … | |
| 64 | 62 | break; |
|---|
| 65 | 63 | case SDL_VIDEORESIZE: |
|---|
| 66 | | mde.SDL.resizeWindow (event.resize.w, event.resize.h); |
|---|
| 67 | | Scheduler.requestUpdate(RF_KEYS.DRAW); |
|---|
| | 64 | sdl.resizeWindow (event.resize.w, event.resize.h); |
|---|
| | 65 | //global.scheduler.request(global.SCHEDULE.DRAW); |
|---|
| 68 | 66 | break; |
|---|
| 69 | | case SDL_ACTIVEEVENT: |
|---|
| | 67 | /+case SDL_ACTIVEEVENT: |
|---|
| 70 | 68 | case SDL_VIDEOEXPOSE: |
|---|
| 71 | | Scheduler.requestUpdate(RF_KEYS.DRAW); |
|---|
| 72 | | break; |
|---|
| | 69 | //global.scheduler.request(global.SCHEDULE.DRAW); |
|---|
| | 70 | break;+/ |
|---|
| 73 | 71 | default: |
|---|
| 74 | 72 | try { |
|---|
| r27 |
r30 |
|
| 19 | 19 | module mde.gl; |
|---|
| 20 | 20 | |
|---|
| 21 | | import mde.scheduler.runTime; |
|---|
| | 21 | import global = mde.global; |
|---|
| | 22 | import mde.gui.gui; |
|---|
| 22 | 23 | |
|---|
| 23 | 24 | import derelict.sdl.sdl; |
|---|
| 24 | 25 | import derelict.opengl.gl; |
|---|
| 25 | 26 | |
|---|
| 26 | | static this () { |
|---|
| 27 | | Scheduler.perRequest (RF_KEYS.DRAW, &mde.gl.draw); |
|---|
| 28 | | } |
|---|
| | 27 | import tango.time.Time; // TimeSpan (type only; unused) |
|---|
| 29 | 28 | |
|---|
| 30 | 29 | //BEGIN GL & window setup |
|---|
| … | … | |
| 66 | 65 | //BEGIN Drawing loop |
|---|
| 67 | 66 | // Temporary draw function |
|---|
| 68 | | void draw () { |
|---|
| | 67 | void draw (TimeSpan) { |
|---|
| 69 | 68 | glClear(GL_COLOR_BUFFER_BIT); |
|---|
| 70 | 69 | |
|---|
| 71 | | foreach (func; drawCallbacks) |
|---|
| 72 | | func(); |
|---|
| | 70 | gui.draw (); |
|---|
| 73 | 71 | |
|---|
| 74 | 72 | glFlush(); |
|---|
| 75 | 73 | SDL_GL_SwapBuffers(); |
|---|
| 76 | 74 | } |
|---|
| 77 | | |
|---|
| 78 | | alias void delegate() DrawingFunc; |
|---|
| 79 | | void addDrawCallback (DrawingFunc f) { |
|---|
| 80 | | drawCallbacks ~= f; |
|---|
| 81 | | } |
|---|
| 82 | | private DrawingFunc[] drawCallbacks; |
|---|
| 83 | 75 | //END Drawing loop |
|---|
| r26 |
r30 |
|
| 25 | 25 | |
|---|
| 26 | 26 | import mde.input.input; |
|---|
| | 27 | import mde.scheduler.Scheduler; |
|---|
| | 28 | |
|---|
| | 29 | /** Some enums used by per request functions. */ |
|---|
| | 30 | enum SCHEDULE : Scheduler.ID { |
|---|
| | 31 | DRAW |
|---|
| | 32 | }; |
|---|
| 27 | 33 | |
|---|
| 28 | 34 | bool run = true; // main loop continues if this is true |
|---|
| r28 |
r30 |
|
| 31 | 31 | * been created, creates it using the Window's widget creation data (throws on error; don't |
|---|
| 32 | 32 | * catch the exception). */ |
|---|
| 33 | | Widget getWidget (widgetID i); |
|---|
| | 33 | IWidget getWidget (widgetID i); |
|---|
| | 34 | |
|---|
| | 35 | /** Called by a sub-widget when a redraw is necessary (since drawing may sometimes be done on |
|---|
| | 36 | * event. */ |
|---|
| | 37 | void requestRedraw (); |
|---|
| 34 | 38 | } |
|---|
| r29 |
r30 |
|
| 23 | 23 | import mde.scheduler.InitFunctions; |
|---|
| 24 | 24 | |
|---|
| 25 | | /** Interface for widgets (may become a class). |
|---|
| | 25 | //BEGIN Iface and createWidget |
|---|
| | 26 | /** Interface for widgets (may become a base class). |
|---|
| 26 | 27 | * |
|---|
| 27 | | * Variable loading/saving efficiency and code-reuse need to be revised later! |
|---|
| 28 | | * Give each Widget an int[] of data which it should check in this() and throw if bad? |
|---|
| | 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. |
|---|
| 29 | 38 | */ |
|---|
| 30 | | interface Widget |
|---|
| 31 | | { |
|---|
| 32 | | /* this() should look like this: |
|---|
| 33 | | this (IWindow window, int[] data) |
|---|
| 34 | | * where: |
|---|
| 35 | | * window is the parent window (only needed for getting sub-widgets, hence no need to store) |
|---|
| 36 | | * data is the widget creation data, stripped of the widget type (see createWidget). |
|---|
| 37 | | */ |
|---|
| 38 | | |
|---|
| | 39 | //FIXME: check code reuse later! |
|---|
| | 40 | interface IWidget |
|---|
| | 41 | { |
|---|
| 39 | 42 | /** Draw, starting from given x and y. |
|---|
| 40 | 43 | * |
|---|
| 41 | | * Maybe replace later with drawClipped, especially for cases where only part of the widget is |
|---|
| 42 | | * visible behind a scrolling window or hidden window. */ |
|---|
| | 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. */ |
|---|
| 43 | 46 | void draw (int x, int y); |
|---|
| 44 | 47 | |
|---|
| 45 | | /** Calculate the size of the widget, taking into account child-widgets. |
|---|
| | 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. |
|---|
| 46 | 53 | * |
|---|
| 47 | | * Later will work out how to make this more flexible. */ |
|---|
| 48 | | void getSize (out int w, out int h); |
|---|
| 49 | | } |
|---|
| 50 | | |
|---|
| | 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 | |
|---|
| | 59 | /// Widget types. Start high so they can be reordered easily later. |
|---|
| | 60 | enum WIDGET_TYPES : int { |
|---|
| | 61 | BOX = 1001, GRID |
|---|
| | 62 | } |
|---|
| | 63 | |
|---|
| | 64 | /** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation |
|---|
| | 65 | * data [1..$]. */ |
|---|
| | 66 | IWidget createWidget (IWindow window, int[] data) { |
|---|
| | 67 | if (data.length < 1) throw new WidgetDataException ("No widget data"); |
|---|
| | 68 | int type = data[0]; // type is first element of data |
|---|
| | 69 | data = data[1..$]; // the rest is passed to the Widget |
|---|
| | 70 | |
|---|
| | 71 | if (type == WIDGET_TYPES.BOX) return new BoxWidget (window, data); |
|---|
| | 72 | else if (type == WIDGET_TYPES.GRID) return new GridWidget (window, data); |
|---|
| | 73 | else throw new WidgetDataException ("Bad widget type"); |
|---|
| | 74 | } |
|---|
| | 75 | //END Iface and createWidget |
|---|
| | 76 | |
|---|
| | 77 | //BEGIN Widgets |
|---|
| 51 | 78 | /// Draws a box. That's it. |
|---|
| 52 | | class BoxWidget : Widget |
|---|
| | 79 | class BoxWidget : IWidget |
|---|
| 53 | 80 | { |
|---|
| 54 | 81 | int w, h; // size |
|---|
| … | … | |
| 66 | 93 | } |
|---|
| 67 | 94 | |
|---|
| 68 | | void getSize (out int w, out int h) { |
|---|
| | 95 | void getMinimumSize (out int w, out int h) { |
|---|
| | 96 | w = h = 0; // box has no content |
|---|
| | 97 | } |
|---|
| | 98 | void getCurrentSize (out int w, out int h) { |
|---|
| 69 | 99 | w = this.w; |
|---|
| 70 | 100 | h = this.h; |
|---|
| … | … | |
| 73 | 103 | |
|---|
| 74 | 104 | /// Encapsulates a grid of Widgets |
|---|
| 75 | | class GridWidget : Widget |
|---|
| 76 | | { |
|---|
| 77 | | const PADDING = 1; // padding between rows/cols |
|---|
| 78 | | const BORDER = 1; // border width |
|---|
| | 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 |
|---|
| 79 | 110 | int w, h; // size |
|---|
| 80 | 111 | int rows, cols; // number of cells in grid |
|---|
| … | … | |
| 83 | 114 | int[] rowY; // cumulative rowH[i-1] + BORDER/PADDING |
|---|
| 84 | 115 | int[] colX; // cumulative colW[i-1] + BORDER/PADDING |
|---|
| 85 | | Widget[] subWidgets;// all widgets in the grid (by row): |
|---|
| | 116 | IWidget[] subWidgets; // all widgets in the grid (by row): |
|---|
| 86 | 117 | /* SubWidget order: [ 2 3 ] |
|---|
| 87 | 118 | * [ 0 1 ] */ |
|---|
| … | … | |
| 95 | 126 | // Get all sub-widgets |
|---|
| 96 | 127 | if (data.length != 2 + rows * cols) throw new WidgetDataException; |
|---|
| | 128 | subWidgets.length = rows*cols; |
|---|
| | 129 | foreach (i, inout subWidget; subWidgets) { |
|---|
| | 130 | subWidget = window.getWidget (data[i+2]); |
|---|
| | 131 | } |
|---|
| | 132 | |
|---|
| | 133 | getMinimumSize (w,h); // Calculate the size (current size is not saved) |
|---|
| | 134 | } |
|---|
| | 135 | |
|---|
| | 136 | void draw (int x, int y) { |
|---|
| | 137 | gl.setColor (1.0f, 0.6f, 0.0f); |
|---|
| | 138 | gl.drawBox (x,x+w, y,y+h); |
|---|
| | 139 | |
|---|
| | 140 | foreach (i,widget; subWidgets) { |
|---|
| | 141 | widget.draw (x + colX[i % cols], y + rowY[i / cols]); |
|---|
| | 142 | } |
|---|
| | 143 | } |
|---|
| | 144 | |
|---|
| | 145 | /** Also recalculates row/column widths. */ |
|---|
| | 146 | void getMinimumSize (out int w, out int h) { |
|---|
| 97 | 147 | if (rows*cols == 0) { // special case |
|---|
| 98 | 148 | w = h = 2*BORDER; |
|---|
| 99 | 149 | return; |
|---|
| 100 | 150 | } |
|---|
| 101 | | subWidgets.length = rows*cols; |
|---|
| 102 | | for (uint i = 2; i < data.length; ++i) { |
|---|
| 103 | | subWidgets[i-2] = window.getWidget (data[i]); |
|---|
| 104 | | } |
|---|
| 105 | 151 | |
|---|
| 106 | 152 | // Find the sizes of all subWidgets |
|---|
| 107 | 153 | int[] widgetW = new int[subWidgets.length]; // dimensions |
|---|
| 108 | 154 | int[] widgetH = new int[subWidgets.length]; |
|---|
| 109 | | foreach (i,widget; subWidgets) widget.getSize (widgetW[i],widgetH[i]); |
|---|
| | 155 | foreach (i,widget; subWidgets) widget.getCurrentSize (widgetW[i],widgetH[i]); |
|---|
| 110 | 156 | |
|---|
| 111 | 157 | // Find row heights and column widths (non cumulative) |
|---|
| … | … | |
| 135 | 181 | w = cum + BORDER - PADDING; // total width |
|---|
| 136 | 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 |
|---|
| | 189 | /// First interactible widget |
|---|
| | 190 | class ButtonWidget : IWidget |
|---|
| | 191 | { |
|---|
| | 192 | const BORDER = 5; // border width |
|---|
| | 193 | int w, h; // size |
|---|
| | 194 | bool pushed = false;// true if button is pushed in |
|---|
| | 195 | |
|---|
| | 196 | this (IWindow, int[] data) { |
|---|
| | 197 | if (data.length != 2) throw new WidgetDataException; |
|---|
| | 198 | |
|---|
| | 199 | w = data[0] + 2*BORDER; |
|---|
| | 200 | h = data[1] + 2*BORDER; |
|---|
| | 201 | } |
|---|
| 137 | 202 | |
|---|
| 138 | 203 | void draw (int x, int y) { |
|---|
| 139 | | gl.setColor (1.0f, 0.6f, 0.0f); |
|---|
| | 204 | if (pushed) |
|---|
| | 205 | gl.setColor (1f, 0f, 1f); |
|---|
| | 206 | else |
|---|
| | 207 | gl.setColor (.6f, 0f, .6f); |
|---|
| 140 | 208 | gl.drawBox (x,x+w, y,y+h); |
|---|
| 141 | | |
|---|
| 142 | | foreach (i,widget; subWidgets) { |
|---|
| 143 | | widget.draw (x + colX[i % cols], y + rowY[i / cols]); |
|---|
| 144 | | } |
|---|
| 145 | | } |
|---|
| 146 | | |
|---|
| 147 | | void getSize (out int w, out int h) { |
|---|
| | 209 | } |
|---|
| | 210 | |
|---|
| | 211 | void getMinimumSize (out int w, out int h) { |
|---|
| | 212 | w = this.w; // button is not resizable |
|---|
| | 213 | h = this.h; |
|---|
| | 214 | } |
|---|
| | 215 | void getCurrentSize (out int w, out int h) { |
|---|
| 148 | 216 | w = this.w; |
|---|
| 149 | 217 | h = this.h; |
|---|
| 150 | 218 | } |
|---|
| 151 | | } |
|---|
| 152 | | |
|---|
| 153 | | // Widget types. Start high so they can be reordered easily later. |
|---|
| 154 | | enum WIDGET_TYPES : int { |
|---|
| 155 | | BOX = 1001, GRID |
|---|
| 156 | | } |
|---|
| 157 | | |
|---|
| 158 | | Widget createWidget (IWindow window, int[] data) { |
|---|
| 159 | | if (data.length < 1) throw new WidgetDataException ("No widget data"); |
|---|
| 160 | | int type = data[0]; // type is first element of data |
|---|
| 161 | | data = data[1..$]; // the rest is passed to the Widget |
|---|
| 162 | | |
|---|
| 163 | | if (type == WIDGET_TYPES.BOX) return new BoxWidget (window, data); |
|---|
| 164 | | else if (type == WIDGET_TYPES.GRID) return new GridWidget (window, data); |
|---|
| 165 | | else throw new WidgetDataException ("Bad widget type"); |
|---|
| 166 | | } |
|---|
| | 219 | |
|---|
| | 220 | } |
|---|
| | 221 | +/ |
|---|
| | 222 | //END Widgets |
|---|
| r29 |
r30 |
|
| 31 | 31 | import tango.scrapple.text.convert.parseFrom : parseFrom; |
|---|
| 32 | 32 | |
|---|
| | 33 | import tango.util.log.Log : Log, Logger; |
|---|
| | 34 | |
|---|
| 33 | 35 | private Logger logger; |
|---|
| 34 | 36 | static this () { |
|---|
| 35 | 37 | logger = Log.getLogger ("mde.gui.gui"); |
|---|
| 36 | 38 | |
|---|
| 37 | | init.addFunc (&GUI.load, "GUI.load"); |
|---|
| 38 | | } |
|---|
| 39 | | |
|---|
| | 39 | init.addFunc (&loadGUI, "loadGUI"); |
|---|
| | 40 | } |
|---|
| | 41 | |
|---|
| | 42 | 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 | } |
|---|
| | 48 | |
|---|
| | 49 | /** A GUI handles a bunch of windows, all to be drawn to the same device. */ |
|---|
| 40 | 50 | struct GUI { |
|---|
| 41 | | static: |
|---|
| 42 | | private const fileName = "gui"; |
|---|
| | 51 | /** Load all windows from the file gui. */ |
|---|
| 43 | 52 | void load() { |
|---|
| | 53 | static const fileName = "gui"; |
|---|
| 44 | 54 | if (!confDir.exists (fileName)) { |
|---|
| 45 | 55 | logger.error ("Unable to load GUI: no config file!"); |
|---|
| … | … | |
| 66 | 76 | foreach (sec; reader.dataset.sec) { |
|---|
| 67 | 77 | Window w = cast(Window) sec; |
|---|
| 68 | | if (w !is null) { // extra safety |
|---|
| 69 | | windows ~= w; |
|---|
| 70 | | try { |
|---|
| 71 | | w.finalise(); |
|---|
| 72 | | |
|---|
| 73 | | gl.addDrawCallback (&w.draw); |
|---|
| 74 | | } catch (WindowLoadException e) { |
|---|
| 75 | | logger.error ("Window failed to load: " ~ e.msg); |
|---|
| 76 | | } |
|---|
| 77 | | } |
|---|
| 78 | | } |
|---|
| 79 | | } |
|---|
| 80 | | |
|---|
| 81 | | private: |
|---|
| | 78 | debug if (w is null) { |
|---|
| | 79 | logger.error (__FILE__ ~ "(GUI.load): code error (w is null)"); |
|---|
| | 80 | continue; |
|---|
| | 81 | } |
|---|
| | 82 | try { |
|---|
| | 83 | //logger.trace ("1"); |
|---|
| | 84 | int x; |
|---|
| | 85 | w.finalise(); |
|---|
| | 86 | x = 6; |
|---|
| | 87 | windows ~= w; // only add if load successful |
|---|
| | 88 | } catch (WindowLoadException e) { |
|---|
| | 89 | logger.error ("Window failed to load: " ~ e.msg); |
|---|
| | 90 | } |
|---|
| | 91 | } |
|---|
| | 92 | } |
|---|
| | 93 | |
|---|
| | 94 | /** Draw each window. |
|---|
| | 95 | * |
|---|
| | 96 | * Currently no concept of how to draw overlapping windows, or how to not bother drawing windows |
|---|
| | 97 | * which don't need redrawing. */ |
|---|
| | 98 | void draw() { |
|---|
| | 99 | foreach (w; windows) |
|---|
| | 100 | w.draw(); |
|---|
| | 101 | } |
|---|
| | 102 | |
|---|
| | 103 | private: |
|---|
| 82 | 104 | Window[] windows; |
|---|
| 83 | 105 | } |
|---|
| … | … | |
| 96 | 118 | { |
|---|
| 97 | 119 | alias int widgetID; // Widget ID type. Each ID is unique under this window. Type is int since this is the widget data type. |
|---|
| 98 | | private int[][widgetID] widgetData; // Data for all widgets under this window. |
|---|
| 99 | | private Widget[widgetID] widgets; // List of all widgets under this window (created on demand). |
|---|
| 100 | | |
|---|
| 101 | | Widget widget; // The primary widget in this window. |
|---|
| 102 | | int x,y; // Window position |
|---|
| 103 | | int w,h; // Window size (calculated from Widgets) |
|---|
| 104 | | |
|---|
| 105 | | const BORDER_WIDTH = 8; // Temporary way to handle window decorations |
|---|
| 106 | | |
|---|
| 107 | | |
|---|
| 108 | | // Call after loading is finished to setup the window and confirm that it's valid. |
|---|
| | 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! */ |
|---|
| 109 | 124 | void finalise () { |
|---|
| 110 | | // Create the widget, throwing on error: |
|---|
| | 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: |
|---|
| 111 | 129 | widget = getWidget (0); // primary widget always has ID 0. |
|---|
| 112 | | widget.getSize (w,h); // Find the initial size |
|---|
| | 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 size |
|---|
| 113 | 134 | w += BORDER_WIDTH * 2; // Adjust for border |
|---|
| 114 | 135 | h += BORDER_WIDTH * 2; |
|---|
| 115 | 136 | } |
|---|
| 116 | 137 | |
|---|
| 117 | | Widget getWidget (widgetID i) { |
|---|
| | 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 { |
|---|
| 118 | 146 | // See if it's already been created: |
|---|
| 119 | | Widget* p = i in widgets; |
|---|
| | 147 | IWidget* p = i in widgets; |
|---|
| 120 | 148 | if (p !is null) return *p; // yes |
|---|
| 121 | 149 | else { // no |
|---|
| … | … | |
| 124 | 152 | |
|---|
| 125 | 153 | // Throws WidgetDataException (a WindowLoadException) if bad data: |
|---|
| 126 | | Widget w = createWidget (this, *d); |
|---|
| | 154 | IWidget w = createWidget (this, *d); |
|---|
| 127 | 155 | widgets[i] = w; |
|---|
| 128 | 156 | return w; |
|---|
| 129 | 157 | } |
|---|
| | 158 | } |
|---|
| | 159 | |
|---|
| | 160 | void requestRedraw () { |
|---|
| | 161 | //FIXME |
|---|
| 130 | 162 | } |
|---|
| 131 | 163 | |
|---|
| … | … | |
| 157 | 189 | } |
|---|
| 158 | 190 | //END Mergetag code |
|---|
| 159 | | } |
|---|
| | 191 | |
|---|
| | 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 position |
|---|
| | 198 | int w,h; // Window size (calculated from Widgets) |
|---|
| | 199 | |
|---|
| | 200 | const BORDER_WIDTH = 8; // Temporary way to handle window decorations |
|---|
| | 201 | |
|---|
| | 202 | } |
|---|
| r27 |
r30 |
|
| 23 | 23 | |
|---|
| 24 | 24 | import global = mde.global; // global.run |
|---|
| 25 | | import mde.SDL; // unused (but must be linked in) |
|---|
| 26 | | import mde.events; // unused (but must be linked in) |
|---|
| 27 | | import mde.gui.gui; // unused (but must be linked in) |
|---|
| | 25 | import gl = mde.gl; // gl.draw |
|---|
| | 26 | import mde.events; // pollEvents |
|---|
| 28 | 27 | |
|---|
| 29 | 28 | import mde.scheduler.Init; |
|---|
| 30 | | import mde.scheduler.runTime; // Scheduler.run() |
|---|
| | 29 | import mde.scheduler.Scheduler; // Scheduler.run() |
|---|
| 31 | 30 | import mde.scheduler.exception; // InitException |
|---|
| 32 | 31 | |
|---|
| … | … | |
| 50 | 49 | //END Initialisation |
|---|
| 51 | 50 | |
|---|
| | 51 | //BEGIN Main loop setup |
|---|
| | 52 | Scheduler scheduler = new Scheduler; // main loop's scheduler |
|---|
| | 53 | |
|---|
| | 54 | scheduler.add (scheduler.getNewID, &mde.events.pollEvents).frame = true; |
|---|
| | 55 | scheduler.add (global.SCHEDULE.DRAW, &gl.draw).frame = true; // draw. for now, every frame. |
|---|
| | 56 | //END Main loop setup |
|---|
| | 57 | |
|---|
| 52 | 58 | while (global.run) { |
|---|
| 53 | | Scheduler.run (Clock.now()); |
|---|
| | 59 | scheduler.execute (Clock.now()); |
|---|
| 54 | 60 | |
|---|
| 55 | 61 | Thread.sleep (0.050); // sleep this many seconds |
|---|
| r26 |
r30 |
|
| 17 | 17 | module mde.mergetag.mtunittest; |
|---|
| 18 | 18 | |
|---|
| 19 | | import mde.mergetag.Reader; |
|---|
| 20 | | import mde.mergetag.Writer; |
|---|
| 21 | | import mde.mergetag.DataSet; |
|---|
| 22 | | import mde.mergetag.DefaultData; |
|---|
| 23 | | |
|---|
| 24 | | import tango.scrapple.text.convert.parseTo : parseTo; |
|---|
| 25 | | import tango.scrapple.text.convert.parseFrom : parseFrom; |
|---|
| 26 | | |
|---|
| 27 | | import tango.util.log.Log : Log, Logger; |
|---|
| 28 | | |
|---|
| 29 | 19 | debug (mdeUnitTest) { |
|---|
| | 20 | import mde.mergetag.Reader; |
|---|
| | 21 | import mde.mergetag.Writer; |
|---|
| | 22 | import mde.mergetag.DataSet; |
|---|
| | 23 | import mde.mergetag.DefaultData; |
|---|
| | 24 | |
|---|
| | 25 | import tango.scrapple.text.convert.parseTo : parseTo; |
|---|
| | 26 | import tango.scrapple.text.convert.parseFrom : parseFrom; |
|---|
| | 27 | |
|---|
| | 28 | import tango.io.FilePath; |
|---|
| | 29 | import tango.util.log.Log : Log, Logger; |
|---|
| | 30 | |
|---|
| 30 | 31 | private Logger logger; |
|---|
| 31 | 32 | static this() { |
|---|
| 32 | 33 | logger = Log.getLogger ("mde.mergetag.mtunittest"); |
|---|
| 33 | 34 | } |
|---|
| 34 | | |
|---|
| | 35 | |
|---|
| 35 | 36 | unittest { |
|---|
| 36 | 37 | /* This does a basic write-out and read-in test for each type with its default value. |
|---|
| … | … | |
| 92 | 93 | } |
|---|
| 93 | 94 | mixin (genCheckCode (`secW`,`secR`)); |
|---|
| 94 | | |
|---|
| | 95 | |
|---|
| | 96 | // Delete the unittest file now |
|---|
| | 97 | FilePath (file~".mtt").remove; |
|---|
| | 98 | |
|---|
| 95 | 99 | logger.info ("Unittest complete (for DefaultData)."); |
|---|
| 96 | 100 | } |
|---|
| r29 |
r30 |
|
| 47 | 47 | import derelict.opengl.gl; |
|---|
| 48 | 48 | import derelict.sdl.sdl; |
|---|
| 49 | | //import derelict.freetype.ft; |
|---|
| | 49 | import derelict.freetype.ft; |
|---|
| 50 | 50 | import derelict.util.exception; |
|---|
| 51 | 51 | |
|---|
| … | … | |
| 151 | 151 | DerelictSDL.load(); |
|---|
| 152 | 152 | DerelictGL.load(); |
|---|
| 153 | | //DerelictFT.load(); |
|---|
| | 153 | DerelictFT.load(); |
|---|
| 154 | 154 | } catch (DerelictException de) { |
|---|
| 155 | 155 | logger.fatal ("Loading dynamic library failed:"); |
|---|
| … | … | |
| 157 | 157 | |
|---|
| 158 | 158 | throw new InitException ("Loading dynamic libraries failed (see above)."); |
|---|
| 159 | | return; |
|---|
| 160 | 159 | } |
|---|
| 161 | 160 | //END Load dynamic libraries |
|---|
| … | … | |
| 309 | 308 | static void initFunc () { |
|---|
| 310 | 309 | initialised = true; |
|---|
| 311 | | cleanupUT.addFunc (&cleanupFunc1); |
|---|
| 312 | | cleanupUT.addFunc (&cleanupFunc2); |
|---|
| 313 | | } |
|---|
| 314 | | |
|---|
| 315 | | initUT.addFunc (&initFunc); |
|---|
| | 310 | cleanupUT.addFunc (&cleanupFunc1, "UT cleanup 1"); |
|---|
| | 311 | cleanupUT.addFunc (&cleanupFunc2, "UT cleanup 2"); |
|---|
| | 312 | } |
|---|
| | 313 | |
|---|
| | 314 | initUT.addFunc (&initFunc, "UT init"); |
|---|
| 316 | 315 | |
|---|
| 317 | 316 | runStageForward (initUT); |
|---|
| r29 |
r30 |
|
| 23 | 23 | * "out cleanupFunc[]". */ |
|---|
| 24 | 24 | module mde.scheduler.InitFunctions; |
|---|
| 25 | | |
|---|
| 26 | | static import mde.gl; |
|---|
| 27 | 25 | |
|---|
| 28 | 26 | import tango.util.log.Log : Log, Logger; |
|---|
| r26 |
r30 |
|
| 29 | 29 | import mde.exception; |
|---|
| 30 | 30 | import mde.scheduler.Init; |
|---|
| | 31 | import mde.scheduler.Scheduler; |
|---|
| 31 | 32 | import mde.i18n.I18nTranslation; |
|---|
| 32 | 33 | |
|---|