Mercurial > projects > mde
view mde/gui/gui.d @ 30:467c74d4804d
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>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Mon, 28 Apr 2008 10:59:47 +0100 |
parents | f985c28c0ec9 |
children | baa87e68d7dc |
line wrap: on
line source
/* LICENSE BLOCK Part of mde: a Modular D game-oriented Engine Copyright © 2007-2008 Diggory Hardy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /// Base GUI module. module mde.gui.gui; import mde.gui.IWindow; import mde.gui.Widget; import mde.gui.exception; import mt = mde.mergetag.DataSet; import mt = mde.mergetag.exception; import mde.mergetag.Reader; import mde.resource.paths; import mde.scheduler.InitFunctions; import tango.scrapple.text.convert.parseTo : parseTo; import tango.scrapple.text.convert.parseFrom : parseFrom; import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.gui"); init.addFunc (&loadGUI, "loadGUI"); } GUI gui; // Currently just one instance; handle differently later. // Wrap gui.load, since init doesn't handle delegates // (do it this way since GUI handling will eventually be changed) void loadGUI () { gui.load(); } /** A GUI handles a bunch of windows, all to be drawn to the same device. */ struct GUI { /** Load all windows from the file gui. */ void load() { static const fileName = "gui"; if (!confDir.exists (fileName)) { logger.error ("Unable to load GUI: no config file!"); return; // not a fatal error (so long as the game can run without a GUI!) } IReader reader; try { reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY); reader.dataSecCreator = delegate mt.IDataSection(mt.ID) { return new Window; }; reader.read; } catch (mt.MTException e) { logger.error ("Loading GUI aborted:"); logger.error (e.msg); return; } // get list windows.length = reader.dataset.sec.length; // pre-allocate windows.length = 0; foreach (sec; reader.dataset.sec) { Window w = cast(Window) sec; debug if (w is null) { logger.error (__FILE__ ~ "(GUI.load): code error (w is null)"); continue; } try { //logger.trace ("1"); int x; w.finalise(); x = 6; windows ~= w; // only add if load successful } catch (WindowLoadException e) { logger.error ("Window failed to load: " ~ e.msg); } } } /** Draw each window. * * Currently no concept of how to draw overlapping windows, or how to not bother drawing windows * which don't need redrawing. */ void draw() { foreach (w; windows) w.draw(); } private: Window[] windows; } package: // Nothing else here is for external use. /** GUI Window class * * A window class instance does two things: (1) specify a region of the screen upon which the window * and its associated widgets are drawn, and (2) load, save, and generally manage all its widgets. * * Let the window load a table of widget data, of type int[][widgetID]. Each widget will, when * created, be given its int[] of data, which this() must confirm is valid (or throw). */ class Window : mt.IDataSection, IWindow { alias int widgetID; // Widget ID type. Each ID is unique under this window. Type is int since this is the widget data type. /** Call after loading is finished to setup the window and confirm that it's valid. * * Throws: WindowLoadException. Do not use the instance in this case! */ void finalise () { // Check data was loaded: if (widgetData is null) throw new WindowLoadException ("No widget data"); // Create the primary widget (and indirectly all sub-widgets), throwing on error: widget = getWidget (0); // primary widget always has ID 0. widgetData = null; // data is no longer needed: allow GC to collect (cannot safely delete) widget.getCurrentSize (w,h);// Find the initial size w += BORDER_WIDTH * 2; // Adjust for border h += BORDER_WIDTH * 2; } /** Get/create a widget by ID. * * Should $(I only) be called internally and by sub-widgets! */ IWidget getWidget (widgetID i) in { // widgetData is normally left to be garbage collected after widgets have been created: assert (widgetData !is null, "getWidget: widgetData is null"); } body { // See if it's already been created: IWidget* p = i in widgets; if (p !is null) return *p; // yes else { // no int[]* d = i in widgetData; if (d is null) throw new WindowLoadException ("Widget not found"); // Throws WidgetDataException (a WindowLoadException) if bad data: IWidget w = createWidget (this, *d); widgets[i] = w; return w; } } void requestRedraw () { //FIXME } void draw () { //BEGIN Window border/back gl.setColor (0.0f, 0.0f, 0.5f); gl.drawBox (x,x+w, y,y+h); //END Window border/back // Tell the widget to draw itself: widget.draw(x + BORDER_WIDTH, y + BORDER_WIDTH); } //BEGIN Mergetag code void addTag (char[] tp, mt.ID id, char[] dt) { if (tp == "int[][int]") { if (id == "widgetData") { widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt); } } else if (tp == "int") { if (id == "x") { x = parseTo!(int) (dt); } else if (id == "y") { y = parseTo!(int) (dt); } } } void writeAll (ItemDelg dlg) { } //END Mergetag code private: int[][widgetID] widgetData; // Data for all widgets under this window (deleted after loading) IWidget[widgetID] widgets; // List of all widgets under this window (created on demand). IWidget widget; // The primary widget in this window. int x,y; // Window position int w,h; // Window size (calculated from Widgets) const BORDER_WIDTH = 8; // Temporary way to handle window decorations }