Mercurial > projects > mde
changeset 121:5b37d0400732
Widgets now receive and store their parent (IParentWidget). Infinite widget recursion checks. WidgetManager code redistributed.
WidgetManager code redistributed between classes; WMScreen class moved to WMScreen.d.
addContent function now calls makeWidget with another id.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Fri, 02 Jan 2009 18:07:10 +0000 |
parents | 46c63cb1c74f |
children | f96e8d18c00a |
files | data/conf/guiDemo.mtt examples/guiDemo.d mde/gui/WidgetManager.d mde/gui/widget/Floating.d mde/gui/widget/Ifaces.d mde/gui/widget/PopupMenu.d mde/gui/widget/TextWidget.d mde/gui/widget/Widget.d mde/gui/widget/createWidget.d mde/gui/widget/layout.d mde/gui/widget/miscContent.d mde/gui/widget/miscWidgets.d |
diffstat | 12 files changed, 421 insertions(+), 633 deletions(-) [+] |
line wrap: on
line diff
--- a/data/conf/guiDemo.mtt Thu Jan 01 15:16:00 2009 +0000 +++ b/data/conf/guiDemo.mtt Fri Jan 02 18:07:10 2009 +0000 @@ -2,17 +2,19 @@ <char[]|Renderer="Simple"> <char[]|Design="Working"> {Working} -<WidgetData|root={0:[0x4100,0,3,1],1:["bar","opts","bar"]}> +<WidgetData|root={0:[0x4100,0,3,1],1:["bar","optaC","bar"]}> <WidgetData|bar={0:[0x4100,0,1,3],1:["menu","blank","menu"]}> -<WidgetData|menu={0:[0x2031,0x4011,0],1:["imde.menu","menu0"]}> -<WidgetData|menu0={0:[0xE032,0],1:["menu1"]}> -<WidgetData|menu1={0:[0x6033],1:["menu2"]}> -<WidgetData|menu2={0:[0xE032,0],1:["menu2"]}> +<WidgetData|menu={0:[0x2031],1:["imde.menu","menu0"]}> +<WidgetData|menu0={0:[0x4011,0],1:["menu1"]}> +<WidgetData|menu1={0:[0xE032,0],1:["menu2"]}> +<WidgetData|menu2={0:[0x6033],1:["menu3"]}> +<WidgetData|menu3={0:[0xE032,0],1:["menu3"]}> <WidgetData|blank={0:[0x2]}> -<WidgetData|opts={0:[0x2031,0x4100,4,2,1],1:["Options","optName","optSecs"]}> -<WidgetData|optSecs={0:[0xE030,4],1:["optSec"]}> +<WidgetData|optaC={0:[0x2031],1:["Options","opts"]}> +<WidgetData|opts={0:[0x4100,4,2,1],1:["optName","optSecs"]}> +<WidgetData|optSecs={0:[0xC110,4],1:["optSec"]}> <WidgetData|optSec={0:[0x4100,0,2,1],1:["optName","optVars"]}> -<WidgetData|optVars={0:[0xE030,0],1:["optDBox"]}> +<WidgetData|optVars={0:[0xC110,0],1:["optDBox"]}> <WidgetData|optDBox={0:[0x4100,1,2,1],1:["optBox","optDesc"]}> <WidgetData|optBox={0:[0x4100,1,1,3],1:["optName","optSep","optVal"]}> <WidgetData|optName={0:[0x4020, 1, 0xffffff]}>
--- a/examples/guiDemo.d Thu Jan 01 15:16:00 2009 +0000 +++ b/examples/guiDemo.d Fri Jan 02 18:07:10 2009 +0000 @@ -23,7 +23,7 @@ import mde.scheduler.Scheduler; // mainSchedule import mde.setup.Screen; // Screen.draw() import mde.setup.InitStage; // StageState -import mde.gui.WidgetManager; +import mde.gui.WMScreen; import tango.core.Thread : Thread; // Thread.sleep() import tango.time.Clock; // Clock.now() @@ -43,7 +43,7 @@ } // Set up the gui - scope WidgetManager gui = new WidgetManager ("guiDemo"); + scope WMScreen gui = new WMScreen ("guiDemo"); StageState guiLoad () { // init func gui.init; gui.loadDesign();
--- a/mde/gui/WidgetManager.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/WidgetManager.d Fri Jan 02 18:07:10 2009 +0000 @@ -14,27 +14,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ /************************************************************************************************* - * The gui manager class. + * The gui manager class base. * - * This is the module to use externally to create a graphical user interface (likely also with - * content modules). + * This contains most of the code required by a window manager, but does not interact with a screen + * or get user input. Rendering is handled separately by the renderer anyway. *************************************************************************************************/ module mde.gui.WidgetManager; import mde.gui.WidgetDataSet; import mde.gui.widget.Ifaces; -import mde.gui.renderer.createRenderer; +import mde.gui.exception; +import mde.content.Content; import imde = mde.imde; -import mde.input.Input; -import mde.scheduler.Scheduler; -import mde.setup.Screen; -import Items = mde.content.Items; // loadTranslation -import mde.lookup.Options; // miscOpts.L10n callback +import mde.file.mergetag.Reader; +import mde.file.mergetag.Writer; +import mde.setup.paths; + +// Widgets to create: +import mde.gui.widget.layout; +import mde.gui.widget.miscWidgets; +import mde.gui.widget.TextWidget; +import mde.gui.widget.miscContent; +import mde.gui.widget.Floating; +import mde.gui.widget.PopupMenu; import tango.core.sync.Mutex; import tango.util.log.Log : Log, Logger; -import tango.util.container.CircularList; // pop-up draw callbacks import tango.util.container.SortedMap; private Logger logger; @@ -43,283 +49,12 @@ } /************************************************************************************************* - * The widget manager. - * - * This provides a layer on top of WidgetLoader, handling input and rendering. Other functionality - * is contained in the super class, to simplify supporting new input/graphics libraries. - * - * Currently mouse coordinates are passed to widgets untranslated. It may make sense to translate - * them and possibly drop events for some uses, such as if the gui is drawn to a texture. - * - * Aside from the IWidgetManager methods, this class should be thread-safe. - *************************************************************************************************/ -class WidgetManager : WidgetLoader, Screen.IDrawable { - /** Construct a new widget manager. - * - * params: - * fileName = Name of file specifying the gui, excluding path and extension. - */ - this (char[] file) { - super(file); - - Screen.addDrawable (this); - clickCallbacks = new typeof(clickCallbacks); - motionCallbacks = new typeof(motionCallbacks); - } - - // this() runs during static this(), when imde.input doesn't exist. init() runs later. - void init () { - // Doesn't need a lock - cannot conflict with other class functions. - // Events we want to know about: - imde.input.addMouseClickCallback(&clickEvent); - imde.input.addMouseMotionCallback(&motionEvent); - - Items.loadTranslation (); - miscOpts.L10n.addCallback (&reloadStrings); - } - - - /** Draw the gui. */ - void draw() { - synchronized(mutex) { - if (child) - child.draw; - foreach (popup; popups) - popup.widget.draw(); - } - } - - - /** For mouse click events. - * - * Sends the event on to the relevant windows and all click callbacks. */ - void clickEvent (ushort usx, ushort usy, ubyte b, bool state) { - debug scope (failure) - logger.warn ("clickEvent: failed!"); - mutex.lock; - scope(exit) mutex.unlock; - if (child is null) return; - - wdabs cx = cast(wdabs) usx, cy = cast(wdabs) usy; - - // 1. Callbacks have the highest priority recieving events (e.g. a button release) - foreach (dg; clickCallbacks) - if (dg (cx, cy, b, state)) return; - - // 2. Then pop-ups: close from top, depending on click pos - // Note: assumes each evaluated popup's parent is not under another still open popup. - // Also assumes popup's parent doesn't have other children in its box. - size_t removeTo = popups.length; - bool eventDone; // don't pass clickEvent - IChildWidget widg; // widget clicked on - foreach_reverse (i,popup; popups) with (popup) { - if (cx < x || cx >= x + w || - cy < y || cy >= y + h) { // on popup - if (parent.onSelf (cx, cy)) { - if (parent.popupParentClick()) removeTo = i; - eventDone = true; - break; - } else { - removeTo = i; - parent.popupClose; - } - } else { - widg = widget.getWidget (cast(wdabs)cx,cast(wdabs)cy); - break; - } - } - if (removeTo < popups.length) { - requestRedraw; - popups = popups[0..removeTo]; - } - if (eventDone) - return; - - // 3. Then the main widget tree - debug assert (cx < child.width && cy < child.height, "WidgetManager: child doesn't cover whole area (code error)"); - if (widg is null) - widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); - if (keyFocus && keyFocus !is widg) { - keyFocus.keyFocusLost; - keyFocus = null; - imde.input.setLetterCallback (null); - } - if (widg !is null) { - if (widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { - keyFocus = widg; - imde.input.setLetterCallback (&widg.keyEvent); - } - } - } - - /** For mouse motion events. - * - * Sends the event on to all motion callbacks. */ - void motionEvent (ushort scx, ushort scy) { - debug scope (failure) - logger.warn ("motionEvent: failed!"); - mutex.lock; - scope(exit) mutex.unlock; - wdabs cx = cast(wdabs) scx, cy = cast(wdabs) scy; - foreach (dg; motionCallbacks) - dg (cx, cy); - - IChildWidget ohighlighted = highlighted; - foreach_reverse (popup; popups) with (popup) { - if (cx >= x && cx < x+w && cy >= y && cy < y+h) { - highlighted = widget.getWidget (cx,cy); - goto foundPopup; - } - } - highlighted = null; // not over a popup - foundPopup: - if (ohighlighted != highlighted) { - if (ohighlighted) - ohighlighted.highlight (false); - if (highlighted) - highlighted.highlight (true); - requestRedraw; - } - } - - - void sizeEvent (int nw, int nh) { // IDrawable function - mutex.lock; - scope(exit) mutex.unlock; - - w = cast(wdim) nw; - h = cast(wdim) nh; - - if (w < mw || h < mh) - logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h); - - if (!child) return; // if not created yet. - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); - } - - //BEGIN IWidgetManager methods - // These methods are only intended for use within the gui package. They are not necessarily - // thread-safe. - IRenderer renderer () { - assert (rend !is null, "WidgetManager.renderer: rend is null"); - return rend; - } - - void addPopup (IChildWidget parnt, IChildWidget widg, int flags = 0) { - debug assert (parnt && widg, "addPopup: null widget"); - if (popups.length >= popupsMem.length) - popupsMem.length = popupsMem.length * 2 + 2; - with (popupsMem[popups.length]) { - parent = parnt; - widget = widg; - w = widg.width; - h = widg.height; - if (flags & 1) { - y = parent.yPos; - if (y+h > this.h) y += parent.height - h; - x = parent.xPos + parent.width; - if (x+w > this.w) x = parent.xPos - w; - } else { - x = parent.xPos; // align on left edge - if (x+w > this.w) x += parent.width - w; // align on right edge - y = parent.yPos + parent.height; // place below - if (y+h > this.h) y = parent.yPos - h; // place above - } - widget.setPosition (x, y); - } - popups = popupsMem[0..popups.length+1]; - requestRedraw; - } - void removePopup (IChildWidget parnt) { - foreach_reverse (i,popup; popups) { - if (popup.parent is parnt) - popups = popups[0..i]; - } - requestRedraw; - } - - void requestRedraw () { - imde.mainSchedule.request(imde.SCHEDULE.DRAW); - } - - void addClickCallback (bool delegate(wdabs, wdabs, ubyte, bool) dg) { - clickCallbacks[dg.ptr] = dg; - } - void addMotionCallback (void delegate(wdabs, wdabs) dg) { - motionCallbacks[dg.ptr] = dg; - } - void removeCallbacks (void* frame) { - clickCallbacks.removeKey(frame); - motionCallbacks.removeKey(frame); - } - //END IWidgetManager methods - -protected: - /* Second stage of widget loading. - * Note: sizeEvent should be called with window size before this. */ - final override void createRootWidget () { - // The renderer needs to be created on the first load, but not after this. - if (rend is null) - rend = createRenderer (rendName); - popups = null; - - child = makeWidget ("root"); - child.setup (0, 3); - - mw = child.minWidth; - mh = child.minHeight; - - if (w < mw || h < mh) - logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h); - - child.setWidth (w, -1); - child.setHeight (h, -1); - child.setPosition (0,0); - } - - final override void preSave () { - if (keyFocus) { - keyFocus.keyFocusLost; - keyFocus = null; - imde.input.setLetterCallback (null); - } - } - -private: - struct ActivePopup { - IChildWidget widget; - IChildWidget parent; - wdabs x,y; - wdsize w,h; - } - IRenderer rend; - ActivePopup[] popups; // Pop-up [menus] to draw. Last element is top popup. - ActivePopup[] popupsMem; // allocated memory for popups - // callbacks indexed by their frame pointers. Must support removal of elements in foreach: - SortedMap!(void*,bool delegate(wdabs cx, wdabs cy, ubyte b, bool state)) clickCallbacks; - SortedMap!(void*,void delegate(wdabs cx, wdabs cy)) motionCallbacks; - IChildWidget keyFocus; // widget receiving keyboard input when non-null - IChildWidget highlighted; // NOTE: in some ways should be same as keyFocus -} - - -import mde.gui.exception; -import mde.content.Content; // Content passed to a callback -import mde.gui.widget.createWidget; - -import mde.file.mergetag.Reader; -import mde.file.mergetag.Writer; -import mde.setup.paths; - -/************************************************************************************************* * Contains the code for loading and saving an entire gui (more than one may exist), but not the * code for drawing it or handling user input. * * This abstract class exists solely for separating out some of the functionality. *************************************************************************************************/ -abstract scope class WidgetLoader : IWidgetManager +abstract scope class AWidgetManager : IWidgetManager { /** Construct a new widget loader. * @@ -503,6 +238,110 @@ requestRedraw; } + //BEGIN IParentWidget methods + // If call reaches the widget manager there isn't any recursion. + //NOTE: should be override + final void recursionCheck (widgetID) {} + //END IParentWidget methods + + //BEGIN IWidgetManager methods + override IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null) + { + debug assert (parent, "makeWidget: parent is null (code error)"); + debug scope (failure) + logger.warn ("Creating widget \""~id~"\" failed."); + + WidgetData data = curData[id]; + if (data.ints.length < 1) { + logger.error ("No int data; creating a debug widget"); + data.ints = [WIDGET_TYPE.Debug]; + } + int type = data.ints[0]; // type is first element of data + + try { + // Statically programmed binary search on type, returning a new widget or calling a + // function: + //pragma (msg, binarySearch ("type", WIDGETS)); + mixin (binarySearch ("type", WIDGETS)); + // Not returned a new widget: + logger.error ("Bad widget type: {}; creating a debug widget instead",type); + } catch (Exception e) { + logger.error ("Error creating widget: {}; creating a debug widget instead.", e.msg); + } + + return new DebugWidget (this, this, id, data); + } + + override WidgetData widgetData (widgetID id) { + return curData[id]; + } + override void widgetData (widgetID id, WidgetData d) { + changes[id] = d; // also updates WidgetDataSet in data. + } + + override wdims dimData (widgetID id) { + return curData.dims (id); + } + override void dimData (widgetID id, wdims d) { + changes.setDims(id, d); // also updates WidgetDataSet in data. + } + + // These methods are only intended for use within the gui package. They are not necessarily + // thread-safe. + IRenderer renderer () { + assert (rend !is null, "WidgetManager.renderer: rend is null"); + return rend; + } + + void addPopup (IChildWidget parnt, IChildWidget widg, int flags = 0) { + debug assert (parnt && widg, "addPopup: null widget"); + if (popups.length >= popupsMem.length) + popupsMem.length = popupsMem.length * 2 + 2; + with (popupsMem[popups.length]) { + parent = parnt; + widget = widg; + w = widg.width; + h = widg.height; + if (flags & 1) { + y = parent.yPos; + if (y+h > this.h) y += parent.height - h; + x = parent.xPos + parent.width; + if (x+w > this.w) x = parent.xPos - w; + } else { + x = parent.xPos; // align on left edge + if (x+w > this.w) x += parent.width - w; // align on right edge + y = parent.yPos + parent.height; // place below + if (y+h > this.h) y = parent.yPos - h; // place above + } + widget.setPosition (x, y); + } + popups = popupsMem[0..popups.length+1]; + requestRedraw; + } + void removePopup (IChildWidget parnt) { + foreach_reverse (i,popup; popups) { + if (popup.parent is parnt) + popups = popups[0..i]; + } + requestRedraw; + } + + void requestRedraw () { + imde.mainSchedule.request(imde.SCHEDULE.DRAW); + } + + void addClickCallback (bool delegate(wdabs, wdabs, ubyte, bool) dg) { + clickCallbacks[dg.ptr] = dg; + } + void addMotionCallback (void delegate(wdabs, wdabs) dg) { + motionCallbacks[dg.ptr] = dg; + } + void removeCallbacks (void* frame) { + clickCallbacks.removeKey(frame); + motionCallbacks.removeKey(frame); + } + //END IWidgetManager methods + protected: /** Second stage of loading the widgets. * @@ -525,24 +364,116 @@ void preSave (); public: - //BEGIN IWidgetManager methods - override IChildWidget makeWidget (widgetID id, IContent content = null) { - debug (mdeWidgets) logger.trace ("Creating widget \""~id~'"'); - return createWidget (this, id, curData[id], content); - } + //BEGIN makeWidget metacode +private static { +/// Widget types. Items match widget names without the "Widget" suffix. +enum WIDGET_TYPE : int { + FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) + TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. + SAFE_RECURSION = 0x8000, // Safe to instantiate recursively without infinite looping. + + // Use widget names rather than usual capitals convention + Unnamed = 0x0, // Only for use by widgets not created with createWidget + + // blank: 0x1 + FixedBlank = 0x1, + SizableBlank = 0x2, + Debug = 0xF, + + // popup widgets: 0x10 + PopupMenu = TAKES_CONTENT | 0x11, + SubMenu = TAKES_CONTENT | 0x12, + + // labels: 0x20 + ContentLabel = TAKES_CONTENT | 0x20, + TextLabel = 0x21, + + // content functions: 0x30 + editContent = FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x30, + addContent = FUNCTION | 0x31, + flatMenuContent = FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x32, + subMenuContent = FUNCTION | TAKES_CONTENT | 0x33, + + // content widgets: 0x40 + DisplayContent = TAKES_CONTENT | 0x40, + BoolContent = TAKES_CONTENT | 0x41, + AStringContent = TAKES_CONTENT | 0x42, + ButtonContent = TAKES_CONTENT | 0x43, + MenuButtonContent = TAKES_CONTENT | 0x44, + + GridLayout = TAKES_CONTENT | 0x100, + ContentList = TAKES_CONTENT | SAFE_RECURSION | 0x110, - override wdims dimData (widgetID id) { - return curData.dims (id); + FloatingArea = 0x200, +} + +// Only used for binarySearch algorithm generation; must be ordered by numerical values. +const char[][] WIDGETS = [ + "FixedBlank", + "SizableBlank", + "Debug", + "TextLabel", + "FloatingArea", + "addContent", + "PopupMenu", + "SubMenu", + "ContentLabel", + "DisplayContent", + "BoolContent", + "AStringContent", + "ButtonContent", + "MenuButtonContent", + "GridLayout", + "subMenuContent", + "ContentList", + "editContent", + "flatMenuContent"]; + +/* Generates a binary search algorithm for makeWidget. */ +char[] binarySearch (char[] var, char[][] consts) { + if (consts.length > 3) { + return `if (`~var~` <= WIDGET_TYPE.`~consts[$/2 - 1]~`) {` ~ + binarySearch (var, consts[0 .. $/2]) ~ + `} else {` ~ + binarySearch (var, consts[$/2 .. $]) ~ + `}`; + } else { + char[] ret; + foreach (c; consts) { + ret ~= `if (` ~ var ~ ` == WIDGET_TYPE.` ~ c ~ `) { + debug (mdeWidgets) logger.trace ("Creating new `~c~`."); + if (!(WIDGET_TYPE.`~c~` & WIDGET_TYPE.SAFE_RECURSION)) + parent.recursionCheck (id); + static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.FUNCTION) + return `~c~` (this, parent, id, data, content); + else static if (WIDGET_TYPE.`~c~` & WIDGET_TYPE.TAKES_CONTENT) + return new `~c~`Widget (this, parent, id, data, content); + else + return new `~c~`Widget (this, parent, id, data); + } else `; + } + ret = ret[0..$-6]; // remove last else + return ret; } - override void setData (widgetID id, WidgetData d) { - changes[id] = d; // also updates WidgetDataSet in data. +} + +debug { // check items in WIDGETS are listed in order + char[] WIDGETS_check () { + char[] ret; + for (int i = WIDGETS.length-2; i > 0; --i) { + ret ~= "WIDGET_TYPE."~WIDGETS[i] ~" >= WIDGET_TYPE."~ WIDGETS[i+1]; + if (i>1) ret ~= " || "; + } + return ret; } - override void setDimData (widgetID id, wdims d) { - changes.setDims(id, d); // also updates WidgetDataSet in data. - } - //END IWidgetManager methods + mixin ("static if ("~WIDGETS_check~") + static assert (false, \"WIDGETS is not in order!\");"); +} +} + //END makeWidget metacode protected: + // Dataset/design data: final char[] fileName; char[] defaultDesign; // The design specified in the file header. char[] rendName; // Name of renderer; for saving and creating renderers @@ -555,10 +486,27 @@ scope mt.DataSet changesDS; // changes and sections from user file (used for saving) bool loadUserFile = true; // still need to load user file for saving? + IRenderer rend; + + // Widgets: wdim w,h; // area available to the widgets wdim mw,mh; // minimal area required by widgets (ideally for limiting w,h) scope IChildWidget child; // The primary widget. uint setupN; // n to pass to IChildWidget.setup + struct ActivePopup { + IChildWidget widget; + IChildWidget parent; + wdabs x,y; + wdsize w,h; + } + ActivePopup[] popups; // Pop-up [menus] to draw. Last element is top popup. + ActivePopup[] popupsMem; // allocated memory for popups + // callbacks indexed by their frame pointers. Must support removal of elements in foreach: + SortedMap!(void*,bool delegate(wdabs cx, wdabs cy, ubyte b, bool state)) clickCallbacks; + SortedMap!(void*,void delegate(wdabs cx, wdabs cy)) motionCallbacks; + IChildWidget keyFocus; // widget receiving keyboard input when non-null + IChildWidget highlighted; // NOTE: in some ways should be same as keyFocus + Mutex mutex; // lock on methods for use outside the package. }
--- a/mde/gui/widget/Floating.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/Floating.d Fri Jan 02 18:07:10 2009 +0000 @@ -38,15 +38,16 @@ */ class FloatingAreaWidget : AParentWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { if (data.ints.length != 1 + data.strings.length) throw new WidgetDataException (this); + super (mgr, parent, id); subWidgets.length = data.strings.length; // widgets created from string data sWOrder.length = subWidgets.length; sWData.length = subWidgets.length; foreach (i,s; data.strings) { - subWidgets[i] = mgr.makeWidget (s); + subWidgets[i] = mgr.makeWidget (this, s); sWOrder[i] = i; sWData[i].borderType = cast(BTYPE) data.ints[i+1]; } @@ -56,11 +57,10 @@ foreach (i, ref d; sWData) (&d.x)[0..4] = dd[i*4..i*4+4]; } - - super (mgr, id, data); } override bool setup (uint n, uint flags) { + debug (mdeWidgets) logger.trace ("FloatingAreaWidget.setup"); foreach (i, ref d; sWData) with (d) { auto widg = subWidgets[i]; if (!widg.setup (n, flags) && n != 0 && !(flags & 1)) @@ -84,7 +84,7 @@ dd[4*i..4*i+4] = (&d.x)[0..4]; } - mgr.setDimData (id, dd); // save positions + mgr.dimData (id, dd); // save positions return true; }
--- a/mde/gui/widget/Ifaces.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/Ifaces.d Fri Jan 02 18:07:10 2009 +0000 @@ -29,16 +29,17 @@ /************************************************************************************************* - * Common interface for all widgets. + * Interface for parent widgets, including IWidgetManager. * * Notation: * Positive/negative direction: along the x/y axis in this direction. * Layout widget: a widget containing multiple sub-widges (which hence controls how they are * laid out). *************************************************************************************************/ -//NOTE: keep this? -interface IWidget +interface IParentWidget { + /** Checks for recursion of unsafe widgets to prevent infinite recursion. */ + void recursionCheck (widgetID); } @@ -47,29 +48,33 @@ * * This class handles widget rendering, input, loading and saving. *************************************************************************************************/ -interface IWidgetManager : IWidget +interface IWidgetManager : IParentWidget { // Loading/saving: - /** Create a widget by ID. + /** Create a widget by looking up the data for id then looking up data.ints[0] in WIDGET_TYPES. * * 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. * + * When used in a this(), super() should be called before any calls to makeWidget (or at least + * parent and id set) due to recursionCheck being called on the widget. + * * 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). */ - IChildWidget makeWidget (widgetID id, IContent content = null); - - /** Get dimension data for a widget. */ - wdims dimData (widgetID id); + IChildWidget makeWidget (IParentWidget parent, widgetID id, IContent content = null); - /** Record some changes, for saving. Should only be called from IWidget.saveChanges() to avoid - * multiple calls for instanced widgets of same id. - * - * WidgetData is for most data, dimensional data (wdims) is for dimensions. */ - void setData (widgetID id, WidgetData); - void setDimData (widgetID id, wdims d); /// ditto + /** Get or set widget id's WidgetData or dimension data. + * + * WidgetData is for most data, dimensional data (wdims) is for dimensions. + * + * Data should only be set from IChildWidget.saveChanges() to + * avoid setting multiple times when a widget id has several instances. */ + WidgetData widgetData (widgetID id); + void widgetData (widgetID id, WidgetData data); /// ditto + wdims dimData (widgetID id); /// ditto + void dimData (widgetID id, wdims d); /// ditto // Rendering: /** For when a widget needs redrawing. @@ -151,7 +156,7 @@ * although some parents may set child-widgets' size during their creation. *************************************************************************************************/ //NOTE: add another this() without the data for default initialization, for the GUI editor? -interface IChildWidget : IWidget +interface IChildWidget : IParentWidget { //BEGIN Load and save /** 2nd stage of initialization for widgets; also called on some changes.
--- a/mde/gui/widget/PopupMenu.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/PopupMenu.d Fri Jan 02 18:07:10 2009 +0000 @@ -38,17 +38,18 @@ *************************************************************************************************/ class PopupMenuWidget : AParentSingleWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { content = c; WDCMinCheck (data, 1,1, content); - subWidget = mgr.makeWidget (data.strings[0], content); + super (mgr, parent, id); + + subWidget = mgr.makeWidget (this, data.strings[0], content); adapter = mgr.renderer.getAdapter; adapter.text = content.toString (1); adapter.getDimensions (mw, mh); w = mw; h = mh; - super (mgr, id, data); } override int clickEvent (wdabs, wdabs, ubyte b, bool state) { @@ -95,8 +96,8 @@ *************************************************************************************************/ class SubMenuWidget : PopupMenuWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { - super (mgr, id, data, c); + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { + super (mgr, parent, id, data, c); } override int clickEvent (wdabs, wdabs, ubyte b, bool state) { @@ -114,27 +115,27 @@ /************************************************************************************************* * A function which returns a ContentListWidget or MenuButtonContentWidget. *************************************************************************************************/ -IChildWidget flatMenuContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { +IChildWidget flatMenuContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { if (c is null) throw new ContentException; if (cast(IContentList) c) - return new ContentListWidget(mgr,id,data,c); - else if (cast(EventContent) c) - return new MenuButtonContentWidget(mgr,id,data,c); - else // generic uneditable option - return new DisplayContentWidget(mgr,id,data,c); + return new ContentListWidget(mgr,parent,id,data,c); + if (cast(EventContent) c) + return new MenuButtonContentWidget(mgr,parent,id,data,c); + // generic uneditable option + return new DisplayContentWidget(mgr,parent,id,data,c); } /************************************************************************************************* * A function which returns a SubMenuWidget or MenuButtonContentWidget. *************************************************************************************************/ -IChildWidget subMenuContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { +IChildWidget subMenuContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { if (c is null) throw new ContentException; if (cast(IContentList) c) - return new SubMenuWidget(mgr,id,data,c); - else if (cast(EventContent) c) - return new MenuButtonContentWidget(mgr,id,data,c); - else // generic uneditable option - return new DisplayContentWidget(mgr,id,data,c); + return new SubMenuWidget(mgr,parent,id,data,c); + if (cast(EventContent) c) + return new MenuButtonContentWidget(mgr,parent,id,data,c); + // generic uneditable option + return new DisplayContentWidget(mgr,parent,id,data,c); } /************************************************************************************************* @@ -142,11 +143,11 @@ *************************************************************************************************/ class MenuButtonContentWidget : ATextWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { - content = cast(EventContent) c; - WDCMinCheck (data, 1,0, content); - adapter = mgr.renderer.getAdapter (); - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { + content = cast(EventContent) c; + if (content is null) throw new ContentException (this); + adapter = mgr.renderer.getAdapter (); + super (mgr, parent, id); } override bool setup (uint n, uint flags) {
--- a/mde/gui/widget/TextWidget.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/TextWidget.d Fri Jan 02 18:07:10 2009 +0000 @@ -32,8 +32,8 @@ { /** Set the adapter first: * adapter = mgr.renderer.getAdapter (...); */ - protected this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + super (mgr, parent, id); } /** Recalculates dims if the renderer changed. */ @@ -70,11 +70,11 @@ * [widgetID, contentID, colour] * where contentID is an ID for the string ID of the contained content * and colour is an 8-bit-per-channel RGB colour of the form 0xRRGGBB. */ - this (IWidgetManager mgr, widgetID id, WidgetData data) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { WDCheck (data, 2, 1); + super (mgr, parent, id); adapter = mgr.renderer.getAdapter (data.ints[1]); adapter.text = data.strings[0]; - super (mgr, id, data); } } @@ -84,12 +84,12 @@ * data.ints[1]. */ class ContentLabelWidget : ATextWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { content = c; WDCCheck (data, 3, 0, content); + super (mgr, parent, id); index = data.ints[1]; adapter = mgr.renderer.getAdapter (data.ints[2]); - super (mgr, id,data); } override bool setup (uint n, uint flags) { @@ -106,12 +106,13 @@ /// Just displays the value of a content. Generic − any IContent. class DisplayContentWidget : ATextWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { content = c; - WDCMinCheck(data, 1,0, content); - adapter = mgr.renderer.getAdapter (); + if (content is null) throw new ContentException (this); + super (mgr, parent, id); + + adapter = mgr.renderer.getAdapter (); adapter.text = content.toString(0); - super (mgr, id, data); } protected: @@ -121,12 +122,13 @@ /// Text-box for editing a content's value. Generic − any AStringContent. class AStringContentWidget : ATextWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { content = cast(AStringContent) c; - WDCMinCheck(data, 1,0, content); + if (content is null) throw new ContentException (this); + super (mgr, parent, id); + adapter = mgr.renderer.getAdapter (); adapter.text = content.toString(0); - super (mgr, id, data); } override bool isWSizable () { return true; }
--- a/mde/gui/widget/Widget.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/Widget.d Fri Jan 02 18:07:10 2009 +0000 @@ -47,11 +47,23 @@ *************************************************************************************************/ abstract class AWidget : IChildWidget { +//BEGIN IParentWidget methods + // Don't override; use the WIDGET_TYPE.SAFE_RECURSION flag for safe widgets. + //NOTE: should be override (compiler bug) + final void recursionCheck (widgetID a) { + debug assert (id !is null && parent !is null, "recursionCheck called before parent and id set"); + if (a is id) + throw new GuiException ("Infite recursion of "~a); + parent.recursionCheck (a); + } +//END IParentWidget methods + //BEGIN Load and save // Base this() for child Widgets. - protected this (IWidgetManager mgr, widgetID id, WidgetData) { - this.mgr = mgr; - this.id = id; + protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + this.mgr = mgr; + this.parent = parent; + this.id = id; } // Widgets need to do their initialization either in this() or setup(). @@ -183,9 +195,10 @@ throw new ContentException (this); } + IWidgetManager mgr; // the enclosing window + IParentWidget parent; // the parent widget + wdim x, y; // position widgetID id; // The widget's ID, used for saving data - IWidgetManager mgr; // the enclosing window - wdim x, y; // position wdim w, h; // size wdim mw = 0, mh = 0; // minimal or fixed size, depending on whether the widget is // resizible; both types of widgets should actually be expandable. @@ -199,12 +212,13 @@ *************************************************************************************************/ abstract class AParentWidget : AWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + super (mgr, parent, id); } override bool setup (uint n, uint flags) { - bool c = false; + debug (mdeWidgets) logger.trace ("AParentWidget.setup"); + bool c = false; foreach (w; subWidgets) { debug assert (w); c |= w.setup (n,flags); @@ -225,12 +239,13 @@ /** ditto */ abstract class AParentSingleWidget : AWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + super (mgr, parent, id); } override bool setup (uint n, uint flags) { - debug assert (subWidget); + debug (mdeWidgets) logger.trace ("AParentSingleWidget.setup"); + debug assert (subWidget); return subWidget.setup (n,flags); } @@ -250,8 +265,8 @@ * Widget uses the initialisation data: * [widgetID, w, h] * where w, h is the fixed size. */ - this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { + super (mgr, parent, id); w = mw = cast(wdim) data.ints[1]; h = mh = cast(wdim) data.ints[2]; } @@ -261,8 +276,8 @@ class SizableWidget : AWidget { // Check data.length is at least 1 before calling! /// Constructor for a completely resizable [blank] widget. - this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + super (mgr, parent, id); } override bool isWSizable () { return true; } @@ -274,8 +289,8 @@ * Overriding classes should implement this() (setting the size), draw() and activated(). */ abstract class AButtonWidget : AWidget { - protected this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + protected this (IWidgetManager mgr, IParentWidget parent, widgetID id) { + super (mgr, parent, id); } /// May be over-ridden. Pushed is true if the button has been pushed and not released.
--- a/mde/gui/widget/createWidget.d Thu Jan 01 15:16:00 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,200 +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 <http://www.gnu.org/licenses/>. */ - -/** This module contains code to create a widget based on an enumeration value passed at runtime. - * - * It could be a part of the WidgetLoader.makeWidget function, but having it here makes things - * tidier. */ -module mde.gui.widget.createWidget; - -import mde.gui.widget.Ifaces; -import mde.gui.exception; -import mde.content.Content; -import Items = mde.content.Items; - -// Widgets to create: -import mde.gui.widget.layout; -import mde.gui.widget.miscWidgets; -import mde.gui.widget.TextWidget; -import mde.gui.widget.miscContent; -import mde.gui.widget.Floating; -import mde.gui.widget.PopupMenu; - -import tango.util.log.Log : Log, Logger; - -private Logger logger; -static this () { - logger = Log.getLogger ("mde.gui.widget.createWidget"); -} - -/** Create a widget. - * - * Usually called by the widget manager's makeWidget function. - * - * Widget created of type data.ints[0] (see enum WIDGET_TYPES), with one of the following CTORs: - * --- - * this (IWidgetManager mgr, WidgetData data); - * // Called if (data.ints[0] & WIDGET_TYPES.TAKES_CONTENT): - * this (IWidgetManager mgr, WidgetData data, IContent content); - * --- - *************************************************************************************************/ -IChildWidget createWidget (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) -in { - assert (mgr !is null, "createWidget: mgr is null"); -} body { - if (data.ints.length < 1) { - logger.error ("No int data; creating a debug widget"); - data.ints = [WIDGET_TYPE.Debug]; - } - int type = data.ints[0]; // type is first element of data - - try { - //pragma (msg, binarySearch ("type", WIDGETS)); - mixin (binarySearch ("type", WIDGETS)); // creates widget by type: new XWidget (mgr, data [, parent]); - // Not returned a new widget or thrown: - logger.error ("Bad widget type: {}; creating a debug widget instead",type); - } catch (Exception e) { - logger.error ("Error creating widget: {}; creating a debug widget instead.", e.msg); - } - - return new DebugWidget (mgr, id, data); -} - -/************************************************************************************************* - * 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. - * - * Circularly depends on createWidget, so should be in this module. - *************************************************************************************************/ -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 createWidget (mgr, id, data, Items.get (cItem)); -} - -private: -/// Widget types. -enum WIDGET_TYPE : int { - FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) - TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. - SAFE_RECURSION = 0x8000, // Safe to instantiate recursively without infinite looping. - - // Use widget names rather than usual capitals convention - Unnamed = 0x0, // Only for use by widgets not created with createWidget - - // blank: 0x1 - FixedBlank = 0x1, - SizableBlank = 0x2, - Debug = 0xF, - - // popup widgets: 0x10 - PopupMenu = TAKES_CONTENT | 0x11, - SubMenu = TAKES_CONTENT | 0x12, - - // labels: 0x20 - ContentLabel = TAKES_CONTENT | 0x20, - TextLabel = 0x21, - - // content functions: 0x30 - editContent = FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x30, - addContent = FUNCTION | 0x31, - flatMenuContent = FUNCTION | TAKES_CONTENT | SAFE_RECURSION | 0x32, - subMenuContent = FUNCTION | TAKES_CONTENT | 0x33, - - // content widgets: 0x40 - DisplayContent = TAKES_CONTENT | 0x40, - BoolContent = TAKES_CONTENT | 0x41, - AStringContent = TAKES_CONTENT | 0x42, - ButtonContent = TAKES_CONTENT | 0x43, - MenuButtonContent = TAKES_CONTENT | 0x44, - - GridLayout = TAKES_CONTENT | 0x100, - ContentList = TAKES_CONTENT | SAFE_RECURSION | 0x110, - - FloatingArea = 0x200, -} - -//const char[][int] WIDGET_NAMES; - -// Only used for binarySearch algorithm generation; must be ordered by numerical values. -const char[][] WIDGETS = [ - "FixedBlank", - "SizableBlank", - "Debug", - "TextLabel", - "FloatingArea", - "addContent", - "PopupMenu", - "SubMenu", - "ContentLabel", - "DisplayContent", - "BoolContent", - "AStringContent", - "ButtonContent", - "MenuButtonContent", - "GridLayout", - "subMenuContent", - "ContentList", - "editContent", - "flatMenuContent"]; - -/* Generates a binary search algorithm. */ -char[] binarySearch (char[] var, char[][] consts) { - if (consts.length > 3) { - return "if (" ~ var ~ " <= WIDGET_TYPE." ~ consts[$/2 - 1] ~ ") {\n" ~ - binarySearch (var, consts[0 .. $/2]) ~ - "} else {\n" ~ - binarySearch (var, consts[$/2 .. $]) ~ - "}\n"; - } else { - char[] ret; - foreach (c; consts) { - ret ~= "if (" ~ var ~ " == WIDGET_TYPE." ~ c ~ ") {\n"~ - /+if ((WIDGET_TYPE."~c~" & WIDGET_TYPE.SAFE_RECURSION) || - "not being recursed (no parent with same id)") {\n - For recursion detection; would probably work with above check and some modification to widgets. +/ - "debug (mdeWidgets) logger.trace (\"Creating new "~c~"Widget.\");\n - static if (WIDGET_TYPE."~c~" & WIDGET_TYPE.FUNCTION)\n - return " ~ c ~ " (mgr, id, data, content);\n - else static if (WIDGET_TYPE."~c~" & WIDGET_TYPE.TAKES_CONTENT)\n - return new " ~ c ~ "Widget (mgr, id, data, content);\n - else\n - return new " ~ c ~ "Widget (mgr, id, data);\n"~ - /+} else - throw new GuiException (\"Widget not safe to be recursed: "~c~"\");+/ - "} else "; - } - ret = ret[0..$-6] ~ '\n'; // remove last else - return ret; - } -} - -debug { // check items in WIDGETS are listed in order - char[] WIDGETS_check () { - char[] ret; - for (int i = WIDGETS.length-2; i > 0; --i) { - ret ~= "WIDGET_TYPE."~WIDGETS[i] ~" >= WIDGET_TYPE."~ WIDGETS[i+1]; - if (i>1) ret ~= " || "; - } - return ret; - } - mixin ("static if ("~WIDGETS_check~") - static assert (false, \"WIDGETS is not in order!\");"); -} \ No newline at end of file
--- a/mde/gui/widget/layout.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/layout.d Fri Jan 02 18:07:10 2009 +0000 @@ -56,26 +56,25 @@ * list) for the widget in row i and column j. The number of parameters must be r*c + 3. * * The content parameter is passed on to all children accepting an IContent. */ - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content) { // Get grid size and check data // Check sufficient data for type, align-flags, rows, cols, and possibly row/col widths. if (data.ints.length != 4) throw new WidgetDataException (this); rows = data.ints[2]; cols = data.ints[3]; - // Check: at least one sub-widget, ints length == 3, strings' length is correct: - if (rows < 1 || cols < 1 || data.ints.length != 4 || data.strings.length != rows * cols) + // Check: at least one sub-widget and strings's length is correct: + if (rows < 1 || cols < 1 || data.strings.length != rows * cols) throw new WidgetDataException (this); + super (mgr, parent, id, data); // Get all sub-widgets subWidgets.length = rows*cols; foreach (i, ref subWidget; subWidgets) { - subWidget = mgr.makeWidget (data.strings[i], content); + subWidget = mgr.makeWidget (this, data.strings[i], content); } initWidths = mgr.dimData (id); // may be null, tested later - - super (mgr, id, data); } // Save column/row sizes. Currently always do so. @@ -83,7 +82,7 @@ foreach (widget; subWidgets) // recurse on subwidgets widget.saveChanges (); - mgr.setDimData (id, col.width ~ row.width); + mgr.dimData (id, col.width ~ row.width); return true; } protected: @@ -98,25 +97,27 @@ *************************************************************************************************/ class ContentListWidget : GridWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent content) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content) { cList = cast(IContentList) content; WDCCheck (data, 2, 1, cList); - + logger.trace ("Got data: {} and {}", data.ints, data.strings); cols = 1; - if ((rows = cList.list.length) > 0) { - subWidgets.length = rows; - foreach (i, c; cList.list) { - subWidgets[i] = mgr.makeWidget (data.strings[0], c); - } - } else { - rows = 1; - subWidgets = [mgr.makeWidget (data.strings[0], new ErrorContent ("<empty list>"))]; - } + rows = cList.list.length; + subWidgets.length = rows; if (data.ints[1] & 8) { // orient horizontally cols = rows; rows = 1; } - super (mgr, id, data); + super (mgr, parent, id, data); + + if (subWidgets) { // i.e. rows*cols > 0 + foreach (i, c; cList.list) { + subWidgets[i] = mgr.makeWidget (this, data.strings[0], c); + } + } else { + rows = cols = 1; + subWidgets = [mgr.makeWidget (this, data.strings[0], new ErrorContent ("<empty list>"))]; + } } override bool saveChanges () { @@ -146,14 +147,13 @@ //BEGIN Creation & saving /** Partial constructor for a grid layout widget. * - * Deriving classes should check data lengths, and set rows, cols, and the subWidgets array, - * before calling this super constructor. (If it's necessary to call super(...) first, - * the call to genCachedConstructionData can be moved to the derived this() methods.) + * Deriving classes should check data lengths, and set rows and cols + * before calling this super constructor. * * Derived constructors may also set initWidths to the array of column widths followed by * row heights used to initially set the row/column dimensions. */ - protected this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); + protected this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { + super (mgr, parent, id); // Create cell aligners with appropriate col/row adjustment function if (data.ints[1] & 1) @@ -173,7 +173,8 @@ * * As such, this must be the first function called after this(). */ override bool setup (uint n, uint flags) { - // Run all internal calculations regardless of changes, then check dimensions for changes. + debug (mdeWidgets) logger.trace ("GridWidget.setup"); + // Run all internal calculations regardless of changes, then check dimensions for changes. // Don't try shortcutting internal calculations when there are no changes - I've tried, and // doing so adds enough overhead to make doing so almost(?) worthless (or at least large // increases in complexity). @@ -202,7 +203,7 @@ widget.setWidth (col.width[i % cols], -1); widget.setHeight (row.width[i / cols], -1); } - return (ow != w || oh != h); + return (ow != w || oh != h); } //END Creation & saving @@ -293,14 +294,16 @@ if (sADD_n == n) return; // cached data is current sADD_n = n; + debug (mdeWidgets) logger.trace ("GridWidget: setup on subWidgets..."); foreach (widg; subWidgets) { // make sure all subwidgets have been set up debug assert (widg); widg.setup (n,flags); } + debug (mdeWidgets) logger.trace ("GridWidget: setup on subWidgets...done"); // make sure both AlignColumns are set up (since first call to setup(n) calls reset): col.setup (n, flags); row.setup (n, flags); - + // Note: shared AlignColumns get this set by all sharing GridWidgets col.spacing = row.spacing = useSpacing ? mgr.renderer.layoutSpacing : 0;
--- a/mde/gui/widget/miscContent.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/miscContent.d Fri Jan 02 18:07:10 2009 +0000 @@ -27,6 +27,7 @@ import mde.gui.renderer.IRenderer; import mde.content.AStringContent; import mde.content.miscContent; +import Items = mde.content.Items; debug { import tango.util.log.Log : Log, Logger; @@ -37,39 +38,51 @@ } /************************************************************************************************* + * 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, IParentWidget parent, widgetID, WidgetData data, IContent) { + if (data.strings.length != 2) throw new WidgetDataException; + return mgr.makeWidget (parent, data.strings[1], Items.get (data.strings[0])); +} + +/************************************************************************************************* * 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) { +IChildWidget editContent (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent c) { // Note: SAFE_RECURSION enabled if (c is null) throw new ContentException; - else if (cast(AStringContent) c) { + if (cast(AStringContent) c) { if (cast(EnumContent) c) // can be PopupMenuWidget or ContentListWidget - return new ContentListWidget(mgr,id,data,c); - else if (cast(BoolContent) c) - return new BoolContentWidget(mgr,id,data,c); - else - return new AStringContentWidget(mgr,id,data,c); - } else if (cast(IContentList) c) - return new ContentListWidget(mgr,id,data,c); - else if (cast(EventContent) c) - return new ButtonContentWidget(mgr,id,data,c); - else // generic uneditable option - return new DisplayContentWidget(mgr,id,data,c); + return new ContentListWidget(mgr,parent,id,data,c); + if (cast(BoolContent) c) + return new BoolContentWidget(mgr,parent,id,data,c); + return new AStringContentWidget(mgr,parent,id,data,c); + } + if (cast(IContentList) c) + return new ContentListWidget(mgr,parent,id,data,c); + if (cast(EventContent) c) + return new ButtonContentWidget(mgr,parent,id,data,c); + // generic uneditable option + return new DisplayContentWidget(mgr,parent,id,data,c); } /// Editable boolean widget class BoolContentWidget : AButtonWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { content = cast(BoolContent) c; - WDCMinCheck(data, 1,0, content); + if (content is null) throw new ContentException (this); + super (mgr, parent, id); wdimPair s = mgr.renderer.getToggleSize; w = mw = s.x; h = mh = s.y; - super (mgr, id, data); } override void draw () { @@ -87,11 +100,11 @@ /// A button connected to an EventContent class ButtonContentWidget : AButtonWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData, IContent c) { content = cast(EventContent) c; - WDCMinCheck (data, 1,0, content); - adapter = mgr.renderer.getAdapter (); - super (mgr, id, data); + if (content is null) throw new ContentException (this); + adapter = mgr.renderer.getAdapter (); + super (mgr, parent, id); } override bool setup (uint n, uint flags) {
--- a/mde/gui/widget/miscWidgets.d Thu Jan 01 15:16:00 2009 +0000 +++ b/mde/gui/widget/miscWidgets.d Fri Jan 02 18:07:10 2009 +0000 @@ -31,9 +31,9 @@ /// A fixed-size blank widget. class FixedBlankWidget : FixedWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { WDCheck (data, 3); - super (mgr, id, data); + super (mgr, parent, id, data); } override void draw () { @@ -46,9 +46,8 @@ /// A completely resizable blank widget (initial size zero). class SizableBlankWidget : SizableWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { - WDCheck (data, 1); - super (mgr, id, data); + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData) { + super (mgr, parent, id); } override void draw () { @@ -61,9 +60,9 @@ /// A debug widget. Essentially as SizableBlankWidget but doesn't mind any amount of data and prints it. class DebugWidget : SizableWidget { - this (IWidgetManager mgr, widgetID id, WidgetData data) { - super (mgr, id, data); - logger.warn ("Debug widget - parameters: ints = {}, strings = {}", data.ints, data.strings); + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { + super (mgr, parent, id); + logger.warn ("Debug widget ({}); parameters: ints = {}, strings = {}", id, data.ints, data.strings); } override void draw () {