# HG changeset patch # User Diggory Hardy # Date 1207325258 -3600 # Node ID 0aa621b3e0701e93ab5681c6e239b865b016fbbe # Parent 611f7b9063c6d2e4c2217f34406035f506a3d368 Some GUI work, plus a small fix in the paths module. Implemented GUI code to load windows from file with a basic widget and draw. Fixed a bug in mde.resource.paths.mdeDirectory.makeMTReader when called with readOrder == PRIORITY.HIGH_ONLY. committer: Diggory Hardy diff -r 611f7b9063c6 -r 0aa621b3e070 codeDoc/jobs.txt --- a/codeDoc/jobs.txt Thu Apr 03 18:15:02 2008 +0100 +++ b/codeDoc/jobs.txt Fri Apr 04 17:07:38 2008 +0100 @@ -3,6 +3,7 @@ In progress: +GUI work... To do: @@ -40,3 +41,4 @@ Done (for git log message): +Fixed a bug in paths.mdeDirectory.makeMTReader when called with readOrder == PRIORITY.HIGH_ONLY. diff -r 611f7b9063c6 -r 0aa621b3e070 data/conf/gui.mtt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/conf/gui.mtt Fri Apr 04 17:07:38 2008 +0100 @@ -0,0 +1,5 @@ +{MT01} +{W1} + + + diff -r 611f7b9063c6 -r 0aa621b3e070 mde/SDL.d --- a/mde/SDL.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/SDL.d Fri Apr 04 17:07:38 2008 +0100 @@ -30,7 +30,7 @@ import derelict.opengl.gl; import derelict.util.exception; -Logger logger; +private Logger logger; static this() { logger = Log.getLogger ("mde.SDL"); diff -r 611f7b9063c6 -r 0aa621b3e070 mde/gl.d --- a/mde/gl.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/gl.d Fri Apr 04 17:07:38 2008 +0100 @@ -13,7 +13,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/** Simple OpenGL functions. */ +/** Some basic OpenGL code. +* +* Everything here is really intended as makeshift code to enable GUI development. */ module mde.gl; import mde.scheduler.runTime; @@ -25,6 +27,7 @@ Scheduler.perRequest (RF_KEYS.DRAW, &mde.gl.draw); } +//BEGIN GL & window setup void glSetup () { glClearColor (0.0f, 0.0f, 0.0f, 0.0f); } @@ -34,31 +37,47 @@ glLoadIdentity (); glViewport (0,0,w,h); - //glOrtho (0.0,w, 0.0,h, -1.0, 1.0); - glOrtho (0.0,1.0,0.0,1.0,-1.0,1.0); + glOrtho (0.0,w, 0.0,h, -1.0, 1.0); + //glOrtho (0.0,1.0,0.0,1.0,-1.0,1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } +//END GL & window setup +//BEGIN Drawing utils +// Simple drawing commands for use by GUI +// (temporary) +void setColor (float r, float g, float b) { + glColor3f (r, g, b); +} +void drawBox (int l, int r, int b, int t) { + glBegin (GL_QUADS); + { + glVertex2i (l, b); + glVertex2i (r, b); + glVertex2i (r, t); + glVertex2i (l, t); + } + glEnd(); +} +//END Drawing utils + +//BEGIN Drawing loop // Temporary draw function void draw () { glClear(GL_COLOR_BUFFER_BIT); - glBegin (GL_QUADS); - { - glColor3f (0.2f, 0.6f, 0.8f); - /+glVertex2i (40, 40); - glVertex2i (200, 40); - glVertex2i (200, 200); - glVertex2i (40, 200);+/ - glVertex2f (0.1f, 0.1f); - glVertex2f (0.9f, 0.1f); - glVertex2f (0.9f, 0.9f); - glVertex2f (0.1f, 0.9f); - } - glEnd(); + foreach (func; drawCallbacks) + func(); glFlush(); SDL_GL_SwapBuffers(); } + +alias void delegate() DrawingFunc; +void addDrawCallback (DrawingFunc f) { + drawCallbacks ~= f; +} +private DrawingFunc[] drawCallbacks; +//END Drawing loop diff -r 611f7b9063c6 -r 0aa621b3e070 mde/gui/Widget.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/Widget.d Fri Apr 04 17:07:38 2008 +0100 @@ -0,0 +1,76 @@ +/* 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 . */ + +/// GUI Widget module. +module mde.gui.Widget; + +import mde.gui.exception; + +import gl = mde.gl; + +/** Interface for widgets (may become a class). +* +* Variable loading/saving efficiency and code-reuse need to be revised later! +* Give each Widget an int[] of data which it should check in this() and throw if bad? +*/ +interface Widget +{ + /** Draw, starting from given x and y. + * + * Maybe replace later with drawClipped, especially for cases where only part of the widget is + * visible behind a scrolling window or hidden window. */ + void draw (int x, int y); + + /** Calculate the size of the widget, taking into account child-widgets. + * + * Later will work out how to make this more flexible. */ + void getSize (out int w, out int h); +} + +/// Draws a box. That's it. +class BoxWidget : Widget +{ + int w, h; // size + + this (int[] data) { + if (data.length != 2) throw new WidgetDataException; + + w = data[0]; + h = data[1]; + } + + void draw (int x, int y) { + gl.setColor (1.0f, 1.0f, 0.0f); + gl.drawBox (x,x+w, y,y+h); + } + + void getSize (out int w, out int h) { + w = this.w; + h = this.h; + } +} + +enum WIDGET_TYPES : int { + BOX = 1 +} + +Widget createWidget (int[] data) { + if (data.length < 1) throw new WidgetDataException ("No widget data"); + int type = data[0]; // type is first element of data + data = data[1..$]; // the rest is passed to the Widget + + if (type == WIDGET_TYPES.BOX) return new BoxWidget (data); + else throw new WidgetDataException ("Bad widget type"); +} diff -r 611f7b9063c6 -r 0aa621b3e070 mde/gui/exception.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/exception.d Fri Apr 04 17:07:38 2008 +0100 @@ -0,0 +1,49 @@ +/* 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 . */ + +/// All GUI Exception classes +module mde.gui.exception; + +import mde.exception; + +class GuiException : mdeException +{ + char[] getSymbol () { + return super.getSymbol ~ ".gui"; + } + + this (char[] msg) { + super(msg); + } +} + +/// Thrown when something goes wrong while loading a window (usually a data error). +class WindowLoadException : GuiException +{ + this (char[] msg) { + super(msg); + } +} + +/// Thrown when or createWidget a Widget class's this() is called with invalid data. +class WidgetDataException : WindowLoadException +{ + this () { // Default, by Widget class's this + super ("Bad widget data"); + } + this (char[] msg) { // From createWidget + super (msg); + } +} diff -r 611f7b9063c6 -r 0aa621b3e070 mde/gui/gui.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/gui.d Fri Apr 04 17:07:38 2008 +0100 @@ -0,0 +1,158 @@ +/* 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 . */ + +/// Base GUI module. +module mde.gui.gui; + +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; + +private Logger logger; +static this () { + logger = Log.getLogger ("mde.gui.gui"); + + init.addFunc (&GUI.load); +} + +struct GUI { +static: + private const fileName = "gui"; + void load() { + 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; + if (w !is null) { // extra safety + windows ~= w; + try { + w.finalise(); + } catch (WindowLoadException e) { + logger.error ("Window failed to load: " ~ e.msg); + } + + gl.addDrawCallback (&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 +{ + alias int widgetID; // Widget ID type. Each ID is unique under this window. Type is int since this is the widget data type. + private int[][widgetID] widgetData; // Data for all widgets under this window. + private Widget[widgetID] widgets; // List of all widgets under this window (created on demand). + + Widget widget; // The primary widget in this window. + int x,y; // Window position + int w,h; // Window size (calculated from Widgets) + + const BORDER_WIDTH = 5; // Temporary way to handle window decorations + + + // Call after loading is finished to setup the window and confirm that it's valid. + void finalise () { + // Create the widget, throwing on error: + widget = getWidget (0); // primary widget always has ID 0. + widget.getSize (w,h); // Find the initial size + w += BORDER_WIDTH * 2; // Adjust for border + h += BORDER_WIDTH * 2; + } + + Widget getWidget (widgetID i) { + // See if it's already been created: + Widget* 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: + Widget w = createWidget (*d); + widgets[i] = w; + return w; + } + } + + 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 +} diff -r 611f7b9063c6 -r 0aa621b3e070 mde/mde.d --- a/mde/mde.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/mde.d Fri Apr 04 17:07:38 2008 +0100 @@ -24,6 +24,7 @@ import global = mde.global; // global.run import mde.SDL; // unused (but must be linked in) import mde.events; // unused (but must be linked in) +import mde.gui.gui; // unused (but must be linked in) import mde.scheduler.Init; import mde.scheduler.runTime; // Scheduler.run() diff -r 611f7b9063c6 -r 0aa621b3e070 mde/options.d --- a/mde/options.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/options.d Fri Apr 04 17:07:38 2008 +0100 @@ -134,7 +134,7 @@ logger.error (ERR_MSG); } } - + // Track all sections for saving/loading/other generic handling. static Options[ID] subClasses; static OptionsGeneric[ID] subClassChanges; diff -r 611f7b9063c6 -r 0aa621b3e070 mde/resource/paths.d --- a/mde/resource/paths.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/resource/paths.d Fri Apr 04 17:07:38 2008 +0100 @@ -71,7 +71,15 @@ */ IReader makeMTReader (char[] file, PRIORITY readOrder, DataSet ds = null, bool rdHeader = false) { - if (readOrder == PRIORITY.HIGH_ONLY) return makeReader (paths[pathsLen-1] ~ file, ds, rdHeader); + if (readOrder == PRIORITY.HIGH_ONLY) { + foreach_reverse (path; paths) { // starting with highest-priority path... + try { + return makeReader (path~file, ds, rdHeader); + } + catch (MTFileIOException) {} // Ignore errors regarding no file for now. + } + throw new MTFileIOException ("Unable to find the file: "~file~"[.mtt|mtb]"); + } else return new mdeReader (file, readOrder, ds, rdHeader, paths); } @@ -246,7 +254,7 @@ } if (readersLen == 0) { // totally failed to find any valid files - throw new MTFileIOException ("Unable to find the file: "~file[1..$]~"[.mtt|mtb]"); + throw new MTFileIOException ("Unable to find the file: "~file~"[.mtt|mtb]"); } // This is simply the easiest way of adjusting the reading order: diff -r 611f7b9063c6 -r 0aa621b3e070 mde/scheduler/InitFunctions.d --- a/mde/scheduler/InitFunctions.d Thu Apr 03 18:15:02 2008 +0100 +++ b/mde/scheduler/InitFunctions.d Fri Apr 04 17:07:38 2008 +0100 @@ -15,7 +15,12 @@ /** This module is responsible for calling all init functions. * -* It is also responsible for setting up all scheduled functions for now. */ +* It is also responsible for setting up all scheduled functions for now. + +* Idea: change import direction so this module adds all init functions. All init functions are +* wrapped in another function before being run in a thread (i.e. run indirectly). Functions fail +* either by throwing an exception or by returning a boolean. Functions may take parameters, e.g. +* "out cleanupFunc[]". */ module mde.scheduler.InitFunctions; static import mde.gl;