# HG changeset patch # User Diggory Hardy # Date 1227636104 0 # Node ID 42e241e7be3ed3d435ac7c2183bde5fe3d948fd2 # Parent ba035eba07b468638b60e64d1b32e02a3dcc3271 ContentList content type; getting content items/lists from Options generically via content.Items, and a new addContent widget function. Several improvements to generic handling of content. New button-with-text widget. Some tidy-up. Some name changes, to increase uniformity. Bug-fix: floating widgets of fixed size could previously be made larger than intended from config dimdata. diff -r ba035eba07b4 -r 42e241e7be3e codeDoc/jobs.txt --- a/codeDoc/jobs.txt Sat Nov 22 20:59:36 2008 +0000 +++ b/codeDoc/jobs.txt Tue Nov 25 18:01:44 2008 +0000 @@ -6,14 +6,9 @@ -Bugs: -Sometimes nothing is drawn until a resize, and fonts are blocks. Cause: init-stages always appear to get divided between two threads. If Inpt, Font and SWnd are called by the main thread and not a sub-thread, the bug doesn't occur. A temporary fix is just to set maxThreads=1. A redesign of threading init stages could solve this, but doesn't seem worth the effort right now. -Drawing of floating widgets is not restricted to the floating area; see SimpleRenderer.restrict(). - - - To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): Also see todo.txt and FIXME/NOTE comment marks. +3 Widget saving: how to deal with modifier functions, esp. when they discard parameters? Remove feature except for dimdata and handle gui editing separately? 3 Use of dtors - don't rely on them? Or what happens when init throws during creation - relying on undefined behaviour. 3 glBindTexture not working with non-0 index - perhaps use a higher level graphics library at some point. 3 Windows building/compatibility (currently partial) - tango/sys/win32/SpecialPath.d @@ -53,4 +48,4 @@ 1 Mergetag binary support / other file formats. -Done (for mercurial log message): +Done (since last commit): diff -r ba035eba07b4 -r 42e241e7be3e data/conf/gui.mtt --- a/data/conf/gui.mtt Sat Nov 22 20:59:36 2008 +0000 +++ b/data/conf/gui.mtt Tue Nov 25 18:01:44 2008 +0000 @@ -2,18 +2,19 @@ {Working} - - - - + + - + + + +!{cloned to avoid bug #4} + {Basic} diff -r ba035eba07b4 -r 42e241e7be3e data/conf/options.mtt --- a/data/conf/options.mtt Sat Nov 22 20:59:36 2008 +0000 +++ b/data/conf/options.mtt Tue Nov 25 18:01:44 2008 +0000 @@ -21,6 +21,5 @@ - - - + + diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/WidgetManager.d Tue Nov 25 18:01:44 2008 +0000 @@ -399,19 +399,14 @@ } } - - /** Create a widget by ID. - * - * A widget instance is created from data found under ID. Multiple instances may be created. - * NOTE - data conflicts when saving? - * - * An IContent may be passed. This could contain many things, e.g. some basic data, a widget, - * multiple sub-IContents. It is only passed to the widget by createWidget if it's enumeration - * given in that module has the flag TAKES_CONTENT. */ IChildWidget makeWidget (widgetID id, IContent content = null) { debug (mdeWidgets) logger.trace ("Creating widget \""~id~'"'); return createWidget (this, id, curData[id], content); } + IChildWidget makeWidget (widgetID id, WidgetData data, IContent content = null) { + debug (mdeWidgets) logger.trace ("Creating widget \""~id~'"'); + return createWidget (this, id, data, content); + } /** Runs finalize for all descendants, in a deepest first order. */ /* NOTE: The way this function works may seem a little odd, but it's designed to allow @@ -444,17 +439,12 @@ } } - /** Get dimension data for a widget. */ wdims dimData (widgetID id) { return curData.dims (id); } - - /** For making changes to widget structure. */ void setData (widgetID id, WidgetData d) { changes[id] = d; // also updates WidgetDataSet in data. } - - /** For making changes to widget dimensions. */ void setDimData (widgetID id, wdims d) { changes.setDims(id, d); // also updates WidgetDataSet in data. } diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/content/Content.d --- a/mde/gui/content/Content.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/content/Content.d Tue Nov 25 18:01:44 2008 +0000 @@ -45,25 +45,8 @@ * Use as a state switch (one option from an enumeration). E.g. a button/selection box could set a * state, and a tabbed box could show a tab based on this. Or could represent an option. */ -//TODO - write a generic IContent displaying widget. Also a generic editable? -// Don't include dimension/drawing stuff because it's renderer specific and content should not be! -// NOTE: an interface or a class? interface IContent { - /+ NOTE: None of this is really used yet, but was (mostly) intended for clipboard copying. - /** Return a copy of self. */ - IContent dup (); - - /** Attempt to convert the content to a specific type (may simply return this if appropriate). - * - * Annoyingly we can't use cast because overloading by return type isn't supported. */ - // FIXME: throw or return null on error or unsupported conversion? - ContentText toText (); - ContentInt toInt (); /// ditto - +/ - - - /** Generically return strings. * * This serves two purposes: generically returning a string of/related to the content (i == 0), @@ -79,10 +62,68 @@ char[] toString (uint i); } +/** A generic way to handle a list of type IContent. */ +class ContentList : IContent +{ + this (IContent[] list = null) { + list_ = list; + } + this (ValueContent[char[]] l) { + list_.length = l.length; + size_t i; + foreach (c; l) + list_[i++] = c; + } + + char[] toString (uint i) { + return i == 0 ? Int.toString (list_.length) ~ " elements" + : i == 1 ? "ContentList" + : null; + } + + IContent[] list () { + return list_; + } + ContentList list (IContent[] list) { + list_ = list; + return this; + } + +protected: + IContent[] list_; +} + +/** Created on errors to display and log a message. */ +class ErrorContent : IContent +{ + this (char[] msg) { + msg_ = msg; + } + + char[] toString (uint i) { + return i == 0 ? msg_ + : i == 1 ? "Error" + : null; + } + +protected: + char[] msg_; +} + /** Base class for content containing a simple value. * - * All derived classes should support functions to set/get any ValueContent type, but return the - * default value of any type other than it's own. */ + * All derived classes should have the following functions: + * --- + * this (char[] symbol, T val = /+ default value +/); + * BoolContent addChangeCb (void delegate (char[] symbol,T value) cb); // add a callback called on any change + * void assignNoCB (T val); // assign val, but without calling callbacks (for Options) + * void opAssign (T val); // assign val, calling callbacks + * T opCall (); // return value + * alias opCall opCast; + * void endEdit (); // Converts sv and assigns to self, calling callbacks + * --- + * On any assignation (by this, assignNoCB, opAssign) the value should be converted to a string and + * assigned to sv, and pos should be clamped to [0,sv.length] (i.e. enforce pos <= sv.length). */ abstract class ValueContent : IContent { protected this () {} @@ -95,9 +136,9 @@ /// Get the text. char[] toString (uint i) { return (i == 0) ? sv - : (i == 1) ? name_ - : (i == 2) ? desc_ - : null; + : (i == 1) ? name_ + : (i == 2) ? desc_ + : null; } /** Acts on a keystroke and returns the new value. @@ -146,7 +187,7 @@ return sv; } - size_t getEditIndex () { + size_t editIndex () { size_t i = 0; for (size_t p = 0; p < pos; ++p) if (!(sv[p] & 0x80) || sv[p] & 0x40) @@ -193,6 +234,7 @@ void assignNoCB (bool val) { v = val; sv = v ? "true" : "false"; + if (pos > sv.length) pos = sv.length; } void opAssign (bool val) { assignNoCB (val); @@ -204,7 +246,7 @@ } alias opCall opCast; - void endEdit () { + override void endEdit () { v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1'); foreach (cb; cngCb) cb(symb, v); @@ -220,7 +262,7 @@ { this (char[] symbol, char[] val = null) { symb = symbol; - v = val; + v = val; } /** Adds cb to the list of callback functions called when the value is changed. Returns this. */ @@ -231,9 +273,10 @@ void assignNoCB (char[] val) { v = val; + if (pos > sv.length) pos = sv.length; } void opAssign (char[] val) { - v = val; + assignNoCB (val); foreach (cb; cngCb) cb(symb, val); } @@ -242,7 +285,7 @@ } alias opCall opCast; - void endEdit () { + override void endEdit () { foreach (cb; cngCb) cb(symb, v); } @@ -270,6 +313,7 @@ void assignNoCB (int val) { v = val; sv = Int.toString (v); + if (pos > sv.length) pos = sv.length; } void opAssign (int val) { assignNoCB (val); @@ -281,7 +325,7 @@ } alias opCall opCast; - void endEdit () { + override void endEdit () { v = Int.toInt (sv); foreach (cb; cngCb) cb(symb, v); @@ -310,6 +354,7 @@ void assignNoCB (double val) { v = val; sv = Float.toString (v); + if (pos > sv.length) pos = sv.length; } void opAssign (double val) { assignNoCB (val); @@ -321,7 +366,7 @@ } alias opCall opCast; - void endEdit () { + override void endEdit () { v = Float.toFloat (sv); foreach (cb; cngCb) cb(symb, v); diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/content/Items.d --- a/mde/gui/content/Items.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/content/Items.d Tue Nov 25 18:01:44 2008 +0000 @@ -13,9 +13,63 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/** List of content items, somewhat like options. - * - * Both named and non-named items like options, but each item is a content. - * Separation by types of content or not? - */ -module mde.gui.content.Items; +/************************************************************************************************** + * A generic way to access content items. + *************************************************************************************************/ +module mde.gui.content.ContentItems; + +import mde.gui.content.Content; +import mde.gui.exception; + +import mde.lookup.Options; +import mde.lookup.Translation; + + /** Get a specific content item. + * + * E.g. get ("Options.MiscOptions.L10n") returns miscOpts.L10n, + * Items.get ("Options.MiscOptions") returns a ContentList of all misc options. */ + IContent get (char[] item) { + char[] h = head (item); + if (h == "Options") + return getOptions (item); + throw new ContentItemException (h); + } + + /** Same as calling get("Options."~item). */ + IContent getOptions (char[] item) { + char[] h = head (item); + auto p = h in Options.optionsClasses; + if (p) { + if (!p.transLoaded) { + Translation trans = Translation.load ("L10n/"~h); + foreach (s, v; p.content) { + Translation.Entry transled = trans.getStruct (s); + v.name (transled.name, transled.desc); + } + p.transLoaded = true; + } + + if (item == null) + return new ContentList (p.content); + + auto q = (h = head (item)) in p.content; + if (q && item is null) // enforce item is an exact match + return *q; + } + throw new ContentItemException (h); + } + +private: + /** Takes the string "head.tail" where tail may contain '.' but head does not, returns "head", + * with str set to "tail". */ + char[] head (ref char[] str) { + size_t i = 0; + while (i < str.length && str[i] != '.') + ++i; + char[] ret = str[0..i]; + if (i == str.length) + str = null; + else + str = str[i+1..$]; + return ret; + } diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/content/options.d --- a/mde/gui/content/options.d Sat Nov 22 20:59:36 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* 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 . */ - -/** Content classes for holding options. - * - * Currently they use a rather poor implementation: each is responsible for grabbing its value and - * updating the option, and cannot be notified if that option is changed externally. - */ -module mde.gui.content.options; - -import mde.gui.content.Content; - -import mde.lookup.Options; -import mde.lookup.Translation; - -import tango.util.log.Log : Log, Logger; -private Logger logger; -static this () { - logger = Log.getLogger ("mde.gui.content.options"); -} - -class OptionList -{ - this (char[] optsName) - { - auto p = optsName in Options.subClasses; - if (p is null) { - logger.error ("OptionList created with invalid options class name."); - return; // list is empty, nothing displayed - } - Options opts = *p; - - Translation trans = Translation.load ("L10n/"~optsName); - char[][] list = opts.list; - - textOpts.length = list.length + opts.content.length; - size_t i; - foreach (s; list) { - Translation.Entry transled = trans.getStruct (s); - textOpts[i] = new OptionContent(opts, s, transled.name, transled.desc); - ++i; - } - foreach (s, v; opts.content) { - Translation.Entry transled = trans.getStruct (s); - v.name (transled.name, transled.desc); // set Content name & desc. Only needs doing once - textOpts[i++] = v; - } - - } - - IContent[] list () { - return textOpts; - } - -protected: - IContent[] textOpts; -} - -//FIXME - todo.txt -class OptionContent : IContent -{ - this (Options o, char[] s, char[] name, char[] desc) { - opts = o; - symb = s; - name_ = name; - desc_ = desc; - } - - char[] toString (uint i) { - if (i == 0) - return "dummy"; //opts.get!(char[])(symb); - else if (i == 1) - return name_; - else if (i == 2) - return desc_; - } - /+char[] value () { - return opts.get!(char[])(symb); - } - void value (char[] v) { - opts.set!(char[])(symb, v); - }+/ -protected: - Options opts; // the set of options within which our option lies - char[] symb; // the symbol name of our option - char[] name_, desc_;// name and description, loaded by lookup.Translation -} diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/exception.d --- a/mde/gui/exception.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/exception.d Tue Nov 25 18:01:44 2008 +0000 @@ -32,8 +32,11 @@ /// Thrown when createWidget or a Widget class's this() is called with invalid data. class WidgetDataException : GuiException { - this (Object o) { // Default, by Widget class's this / WDCheck - super ("Bad widget data for "~o.classinfo.name); + this () { // Other CTOR should be used by classes + super ("Bad widget data"); + } + this (Object o) { // Default, by Widget class's this / WDCheck + super ("Bad widget data for "~o.classinfo.name); } } @@ -49,3 +52,10 @@ super (msg); } } + +/// Thrown when getting a content item fails +class ContentItemException : GuiException { + this (char[] msg) { + super ("Bad content item specifier: "~msg); + } +} diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/renderer/IRenderer.d --- a/mde/gui/renderer/IRenderer.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/renderer/IRenderer.d Tue Nov 25 18:01:44 2008 +0000 @@ -87,17 +87,21 @@ * NOTE: currently inflexible. Could use (function) pointers, class interfaces or struct * interfaces when available to allow flexibility. */ struct TextAdapter { - void setText (char[] c) { - content = c; + char[] text () { + return content; + } + void text (char[] str) { + content = str; textCache.cacheVer = -1; // force update } - void setColour (int col = 0xFFFFFF) { - colour = Colour (col); + void colour (int colour = 0xFFFFFF) { + colour_ = Colour (colour); } - void setIndex (size_t index = size_t.max) { - this.index = index; + /** If not size_t.max, an edit marker is drawn before character index. */ + void index (size_t index = size_t.max) { + index_ = index; } void getDimensions (out wdsize w, out wdsize h) { @@ -107,13 +111,13 @@ } void draw (wdabs x, wdabs y) { - font.textBlock (x,y, content, textCache, colour, index); + font.textBlock (x,y, content, textCache, colour_, index_); } char[] content; TextBlock textCache; - size_t index; - Colour colour; + size_t index_; + Colour colour_; FontStyle font; } //END Types diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/renderer/SimpleRenderer.d --- a/mde/gui/renderer/SimpleRenderer.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/renderer/SimpleRenderer.d Tue Nov 25 18:01:44 2008 +0000 @@ -59,6 +59,8 @@ //FIXME - make these do something + // They should restrict the drawing of floating widgets to the floating area, for instance, + // although this isn't strictly necessary. void restrict (wdim x, wdim y, wdim w, wdim h) {} void relax () {} @@ -67,7 +69,7 @@ glRecti(x, y+h, x+w, y); if (border.capability != 0) { - glColor3f (0f, 0f, .7f); + glColor3f (0f, 0f, .6f); glBegin (GL_TRIANGLES); wdim t = border.x1 + border.y1; glVertex2i (x, y); @@ -134,8 +136,8 @@ TextAdapter a; a.font = defaultFont; a.content = text; - a.colour = Colour (col); - a.index = size_t.max; + a.colour_ = Colour (col); + a.index_ = size_t.max; return a; } diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/types.d --- a/mde/gui/types.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/types.d Tue Nov 25 18:01:44 2008 +0000 @@ -19,9 +19,7 @@ module mde.gui.types; -/** Widget ID type. Each ID is unique under this window. -* -* Type is int since this is the widget data type. */ +/** Widget ID type. Each ID is unique under this window. */ alias char[] widgetID; /** Window coordinate and dimension/size type (int). diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/Floating.d --- a/mde/gui/widget/Floating.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/Floating.d Tue Nov 25 18:01:44 2008 +0000 @@ -66,8 +66,8 @@ d.border = mgr.renderer.getBorder (borderType, widg.isWSizable, widg.isHSizable); mw = widg.minWidth + border.x1 + border.x2; mh = widg.minHeight + border.y1 + border.y2; - if (w < mw) w = mw; - if (h < mh) h = mh; + if (w < mw || !widg.isWSizable) w = mw; + if (h < mh || !widg.isHSizable) h = mh; widg.setWidth (w - border.x1 - border.x2, -1); widg.setHeight (h - border.y1 - border.y2, -1); } diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/Ifaces.d Tue Nov 25 18:01:44 2008 +0000 @@ -54,14 +54,18 @@ * * Params: * id = Identifier, within data files, of the data for the widget. + * data = Pass this data to the widget, not data looked up via id. * content = An IContent may be passed to some widgets on creation. * * Creates a widget, using the widget data with index id. Widget data is loaded from files, * and per design (multiple gui layouts, called designs, may exist; data is per design). - */ + * + * The function taking a WidgetData is intended for modifier functions and only exists to + * avoid circular dependencies between the modifier function's module and createWidget. */ IChildWidget makeWidget (widgetID id, IContent content = null); + IChildWidget makeWidget (widgetID id, WidgetData data, IContent content = null); - /// Get dimensional data. + /** Get dimension data for a widget. */ wdims dimData (widgetID id); /** Record some changes, for saving. Should only be called from IWidget.saveChanges() to avoid diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/TextWidget.d --- a/mde/gui/widget/TextWidget.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/TextWidget.d Tue Nov 25 18:01:44 2008 +0000 @@ -24,12 +24,10 @@ import mde.gui.renderer.IRenderer; import mde.gui.content.Content; -debug { - import tango.util.log.Log : Log, Logger; - private Logger logger; - static this () { - logger = Log.getLogger ("mde.gui.widget.TextWidget"); - } +import tango.util.log.Log : Log, Logger; +private Logger logger; +static this () { + logger = Log.getLogger ("mde.gui.widget.TextWidget"); } /** Base text widget. @@ -88,3 +86,29 @@ IContent content; int index; } + +class TextButtonWidget : AButtonWidget +{ + this (IWidgetManager mgr, widgetID id, WidgetData data) { + WDCheck (data, 1, 1); + adapter = mgr.renderer.getAdapter (data.strings[0]); + adapter.getDimensions (mw, mh); + w = mw; + h = mh; + super (mgr, id, data); + } + + void draw () { + super.draw(); + adapter.draw (x,y); + } + + void activated () { + logger.info ("Button \""~adapter.text~"\" pressed"); + } + +protected: + IRenderer.TextAdapter adapter; + IContent content; + int index; +} \ No newline at end of file diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/Widget.d --- a/mde/gui/widget/Widget.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/Widget.d Tue Nov 25 18:01:44 2008 +0000 @@ -140,7 +140,8 @@ protected: /********************************************************************************************** * Widgets may use WDCheck as a utility to check what data holds. Its use is encouraged, so - * that the checks can easily be updated should WidgetData be changed. + * that the checks can easily be updated should WidgetData be changed. WDMinCheck is similar, + * but allows more data than required; it is used by some generic content widgets. * * Params: * data = the WidgetData to check lengths of @@ -148,9 +149,15 @@ * n_strings= number of strings (default 0 since not all widgets use strings) *********************************************************************************************/ void WDCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) { - if (data.ints.length != n_ints || - data.strings.length != n_strings) - throw new WidgetDataException (this); + if (data.ints.length != n_ints || + data.strings.length != n_strings) + throw new WidgetDataException (this); + } + /** ditto */ + void WDMinCheck (WidgetData data, size_t n_ints, size_t n_strings = 0) { + if (data.ints.length < n_ints || + data.strings.length < n_strings) + throw new WidgetDataException (this); } widgetID id; // The widget's ID, used for saving data diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/createWidget.d --- a/mde/gui/widget/createWidget.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/createWidget.d Tue Nov 25 18:01:44 2008 +0000 @@ -97,19 +97,23 @@ // buttons: 0x10 Button = 0x10, + TextButton = 0x11, // labels: 0x20 ContentLabel = TAKES_CONTENT | 0x20, TextLabel = 0x21, - // content editables: 0x30 + // content functions: 0x30 editContent = FUNCTION | TAKES_CONTENT | 0x30, - DisplayContent = TAKES_CONTENT | 0x30, - BoolContent = TAKES_CONTENT | 0x31, - ValueContent = TAKES_CONTENT | 0x32, + addContent = FUNCTION | 0x31, + + // content widgets: 0x40 + DisplayContent = TAKES_CONTENT | 0x40, + BoolContent = TAKES_CONTENT | 0x41, + ValueContent = TAKES_CONTENT | 0x42, GridLayout = TAKES_CONTENT | PARENT | 0x100, - TrialContentLayout = PARENT | 0x110, + ContentList = TAKES_CONTENT | PARENT | 0x110, FloatingArea = PARENT | 0x200, } @@ -122,15 +126,17 @@ "SizableBlank", "Debug", "Button", - "TextLabel", - "ContentLabel", + "TextButton", + "TextLabel", + "addContent", + "ContentLabel", "DisplayContent", "BoolContent", "ValueContent", "editContent", - "TrialContentLayout", "FloatingArea", - "GridLayout"]; + "GridLayout", + "ContentList"]; /* Generates a binary search algorithm. */ char[] binarySearch (char[] var, char[][] consts) { diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/layout.d Tue Nov 25 18:01:44 2008 +0000 @@ -19,8 +19,6 @@ import mde.gui.widget.Widget; import mde.gui.exception; -import mde.gui.widget.TextWidget; -import mde.gui.content.options; import mde.gui.content.Content; import tango.util.container.HashMap; @@ -93,26 +91,31 @@ /************************************************************************************************* - * Trial layout of sub-widgets of one type only. + * Iterates on an ContentList to produce a list of widgets, each of which is created with widgetID + * data.strings[0]. If an IContent is passed, this is cast to a ContentList, otherwise + * content.Items is used to get an IContent. It is an error if the content fails to cast to + * ContentList. *************************************************************************************************/ -class TrialContentLayoutWidget : GridWidget +class ContentListWidget : GridWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { + this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) { debug scope (failure) logger.warn ("TrialContentLayoutWidget: failure"); - WDCheck (data, 2, 2); - - OptionList optsList = new OptionList(data.strings[1]); + WDCheck (data, 2, 1); + + cList = cast(ContentList) content; + if (cList is null) + throw new ContentException; + cols = 1; - if ((rows = optsList.list.length) > 0) { - // Get all sub-widgets + if ((rows = cList.list.length) > 0) { subWidgets.length = rows; - foreach (i, c; optsList.list) { + foreach (i, c; cList.list) { subWidgets[i] = mgr.makeWidget (data.strings[0], c); } } else { rows = 1; - subWidgets = [mgr.makeWidget (data.strings[0], new TextContent (data.strings[1], "Invalid Options section"))]; + subWidgets = [mgr.makeWidget (data.strings[0], new ErrorContent (""))]; } super (mgr, id, data); } @@ -125,7 +128,7 @@ } private: - OptionList optsList; + ContentList cList; } diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/miscContent.d --- a/mde/gui/widget/miscContent.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/miscContent.d Tue Nov 25 18:01:44 2008 +0000 @@ -13,42 +13,53 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/** Some content widgets. */ +/************************************************************************************************* + * A function to return content widgets and some miscellaneous content widgets. + *************************************************************************************************/ module mde.gui.widget.miscContent; +import mde.gui.widget.Widget; +import mde.gui.exception; import mde.gui.widget.textContent; -import mde.gui.widget.Widget; -import mde.gui.widget.TextWidget; -import mde.gui.exception; +import mde.gui.widget.layout; + import mde.gui.renderer.IRenderer; - import mde.gui.content.Content; +import Items = mde.gui.content.Items; -/// Chooses the most appropriate content editing widget +/************************************************************************************************* + * A function which uses Items.get (data.strings[0]) to get a content and creates a widget from + * data.ints[1]. The first item in each ints and strings is removed before passing data to the new + * widget. + * + * The function only takes an IContent parameter to satisfy createWidget; it's value is ignored. + *************************************************************************************************/ +IChildWidget addContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent) { + if (data.ints.length < 2 || data.strings.length < 1) throw new WidgetDataException; + char[] cItem = data.strings[0]; + data.strings = data.strings[1..$]; + data.ints = data.ints [1..$]; + return mgr.makeWidget (id, data, Items.get (cItem)); +} + +/************************************************************************************************* + * A function which returns the most appropriate content editing widget. + * + * Widgets which can be returned: BoolContentWidget (toggle button), ValueContentWidget (generic + * text-box editor), DisplayContentWidget (generic text label). + *************************************************************************************************/ IChildWidget editContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + if (c is null) throw new ContentException; if (cast(BoolContent) c) return new BoolContentWidget(mgr,id,data,c); else if (cast(ValueContent) c) return new ValueContentWidget(mgr,id,data,c); + else if (cast(ContentList) c) + return new ContentListWidget(mgr,id,data,c); else // generic uneditable option return new DisplayContentWidget(mgr,id,data,c); } -/// Just displays the content -class DisplayContentWidget : ATextWidget -{ - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { - WDCheck(data, 1); - content = c; - if (!content) throw new ContentException (); - adapter = mgr.renderer.getAdapter (content.toString(0)); - super (mgr, id, data); - } - -protected: - IContent content; -} - /// Editable boolean widget class BoolContentWidget : AButtonWidget { diff -r ba035eba07b4 -r 42e241e7be3e mde/gui/widget/textContent.d --- a/mde/gui/widget/textContent.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/gui/widget/textContent.d Tue Nov 25 18:01:44 2008 +0000 @@ -31,11 +31,26 @@ } } +/// Just displays the content +class DisplayContentWidget : ATextWidget +{ + this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + WDMinCheck(data, 1); + content = c; + if (!content) throw new ContentException (); + adapter = mgr.renderer.getAdapter (content.toString(0)); + super (mgr, id, data); + } + + protected: + IContent content; +} + /// Capable of editing any ValueContent class class ValueContentWidget : ATextWidget { this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { - WDCheck(data, 1); + WDMinCheck(data, 1); content = cast(ValueContent) c; if (!content) //content = new TextContent (null, null); throw new ContentException (); @@ -45,19 +60,19 @@ /** On click, request keyboard input. */ int clickEvent (wdabs, wdabs, ubyte, bool state) { - adapter.setIndex = content.getEditIndex; + adapter.index = content.editIndex; mgr.requestRedraw; return 1; // get keyboard input via keyEvent } void keyEvent (ushort s, char[] i) { - adapter.setText = content.keyStroke (s, i); - adapter.setIndex = content.getEditIndex; + adapter.text = content.keyStroke (s, i); + adapter.index = content.editIndex; adapter.getDimensions (mw, mh); // NOTE: only passively change size: next resize will see new minimal size mgr.requestRedraw; } void keyFocusLost () { - adapter.setIndex; + adapter.index; content.endEdit; // update other users of content relying on callbacks mgr.requestRedraw; } diff -r ba035eba07b4 -r 42e241e7be3e mde/lookup/Options.d --- a/mde/lookup/Options.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/lookup/Options.d Tue Nov 25 18:01:44 2008 +0000 @@ -13,6 +13,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +//FIXME: Ddoc is outdated /** This module handles stored options, currently all except input maps. * * The purpose of having all options centrally controlled is to allow generic handling by the GUI @@ -44,6 +45,7 @@ logger = Log.getLogger ("mde.lookup.Options"); } +//FIXME: Ddoc is outdated /** Base class for handling options. * * This class itself handles no options and should not be instantiated, but provides a sub-classable @@ -165,11 +167,16 @@ c.secName = i; subClasses[cast(ID) i] = c; } - - // Track all sections for saving/loading/other generic handling. - Options[ID] subClasses; - bool changed = false; // any changes at all, i.e. do we need to save? - + + /** Get the hash map of Options classes. */ + Options[ID] optionsClasses () { + return subClasses; + } + + // Track all sections for saving/loading/other generic handling. + private Options[ID] subClasses; + private bool changed = false; // any changes at all, i.e. do we need to save? + /* Load/save options from file. * * If the file doesn't exist, no reading is attempted (options are left at default values). @@ -246,6 +253,7 @@ } else const char[] setMixin = ``; }+/ + /+ /** Set option symbol of an Options sub-class to val. * * Due to the way options are handled generically, string IDs must be used to access the options @@ -264,7 +272,6 @@ logger.error ("Options.set: invalid symbol"); } }+/ - /+ /** Get option symbol of an Options sub-class. * * Using this method to read an option is not necessary, but allows for generic use. */ @@ -293,9 +300,13 @@ return opts; } - /// Variable validate function. This implementation does nothing. + /** Variable validate function, called when options are loaded from file. This implementation + * does nothing. */ void validate() {} + /** Boolean, telling whether translation strings have been loaded for the instance. */ + bool transLoaded; + protected { char[] secName; // name of this option setting; set null after translation is loaded OptionChanges optionChanges; // all changes to options (for saving) diff -r ba035eba07b4 -r 42e241e7be3e mde/lookup/Translation.d --- a/mde/lookup/Translation.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/lookup/Translation.d Tue Nov 25 18:01:44 2008 +0000 @@ -225,8 +225,8 @@ // Hack a specific locale... // Also to allow unittest to run without init. - char[] currentL10n = miscOpts.L10n; - miscOpts.L10n = "test-1"; + TextContent realL10n = miscOpts.L10n; + miscOpts.L10n = new TextContent ("L10n", "test-1"); Translation transl = load ("unittest/Translation"); @@ -245,7 +245,8 @@ // Only check extra entries are allowed but ignored. // Restore - miscOpts.L10n = currentL10n; + delete miscOpts.L10n; + miscOpts.L10n = realL10n; logger.info ("Unittest complete."); } diff -r ba035eba07b4 -r 42e241e7be3e mde/mde.d --- a/mde/mde.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/mde.d Tue Nov 25 18:01:44 2008 +0000 @@ -1,4 +1,4 @@ -/* LICENSE BLOCK +/* LICENSE BLOCK Part of mde: a Modular D game-oriented Engine Copyright © 2007-2008 Diggory Hardy @@ -81,7 +81,7 @@ mainSchedule.add (mainSchedule.getNewID, &mde.events.pollEvents).frame = true; //END Main loop setup - double pollInterval = miscOpts.pollInterval(); + double pollInterval = miscOpts.pollInterval(); while (run) { mainSchedule.execute (Clock.now()); diff -r ba035eba07b4 -r 42e241e7be3e mde/setup/Init.d --- a/mde/setup/Init.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/setup/Init.d Tue Nov 25 18:01:44 2008 +0000 @@ -325,7 +325,6 @@ // Do a job: try { - // FIXME - old stage start&finish trace messages - we don't have a name! static if (startup) { debug logger.trace ("({}) InitStage {}: starting init", threadNum, stage.name); stage.state = (*stage).init(); // init is a property of a pointer (oh no!) @@ -449,8 +448,8 @@ foreach (key,stage_p; stages) foreach (name; stage_p.depends) stages[name].rdepends ~= key; - int realMaxThreads = miscOpts.maxThreads(); - miscOpts.maxThreads = 4; // force up to 4 threads for unittest + IntContent realMaxThreads = miscOpts.maxThreads; + miscOpts.maxThreads = new IntContent ("maxThreads", 4); // force up to 4 threads for unittest logger.level(Logger.Info); // hide a lot of trace messages logger.info ("You should see some warning messages starting \"InitStage\":"); @@ -490,6 +489,7 @@ assert (stages[toStageName("stg3")].state == cast(StageState)7); // set by the exception stages = realInit; // restore the real init stages + delete miscOpts.maxThreads; miscOpts.maxThreads = realMaxThreads; logger.info ("Unittest complete."); } diff -r ba035eba07b4 -r 42e241e7be3e mde/setup/paths.d --- a/mde/setup/paths.d Sat Nov 22 20:59:36 2008 +0000 +++ b/mde/setup/paths.d Tue Nov 25 18:01:44 2008 +0000 @@ -220,7 +220,6 @@ /** Find at least one path for each required directory. * * Note: the logger cannot be used yet, so only output is exception messages. */ -// FIXME: use tango/sys/Environment.d version (linux) { SortedMap!(char[],char[]) fontFiles; // key is file name, value is CString path /** Get the actual path of a font file, or throw NoFileException if not found.