Mercurial > projects > mde
changeset 39:5132301e9ed7
Implemented widget saving.
Widget creation data saving (sub-widgets, etc:) code there but not used.
Widget mutable data saving & loading: window size/position, row/column dimensions saved (still needs a fix in GridWidget.setSize()).
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Wed, 07 May 2008 13:07:03 +0100 |
parents | 8c4c96f04e7f |
children | b28d7adc786b |
files | mde/gui/Gui.d mde/gui/exception.d mde/gui/renderer/SimpleRenderer.d mde/gui/widget/Ifaces.d mde/gui/widget/Widget.d mde/gui/widget/Window.d mde/gui/widget/createWidget.d mde/gui/widget/layout.d mde/scheduler/Scheduler.d mde/scheduler/init2.d mde/scheduler/initFunctions.d |
diffstat | 11 files changed, 374 insertions(+), 147 deletions(-) [+] |
line wrap: on
line diff
--- a/mde/gui/Gui.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/Gui.d Wed May 07 13:07:03 2008 +0100 @@ -37,6 +37,7 @@ import mt = mde.mergetag.DefaultData; import mt = mde.mergetag.exception; import mde.mergetag.Reader; +import mde.mergetag.Writer; import mde.resource.paths; import tango.util.log.Log : Log, Logger; @@ -56,7 +57,7 @@ //BEGIN Methods for external use //BEGIN Loading code /** Load all windows from the file gui. */ - void load(char[] fileName) { + void load (char[] fileName) { 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!) @@ -64,7 +65,7 @@ IReader reader; try { - reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY, null, true); + reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { return new Window (id); }; @@ -77,12 +78,13 @@ } // Get the renderer - char[]* p = "Renderer" in reader.dataset.header.Arg!(char[]).Arg; + char[]* p = RENDERER in reader.dataset.header.Arg!(char[]).Arg; if (p is null || *p is null) { logger.error ("Loading GUI aborted: no renderer specified"); return; } - rend = createRenderer (*p); + rendName = *p; + rend = createRenderer (rendName); // get list windows.length = reader.dataset.sec.length; // pre-allocate @@ -104,6 +106,32 @@ imde.input.addMouseClickCallback(&clickEvent); imde.input.addMouseMotionCallback(&motionEvent); } + + void save (char[] fileName) { + mt.DataSet ds = new mt.DataSet; + + // Add header: + ds.header = new mt.DefaultData; + ds.header.Arg!(char[]).Arg[RENDERER] = rendName; + + // Add windows to be saved: + foreach (window; windows) + ds.sec [window.name] = window; + + try { // Save + IWriter writer; + writer = confDir.makeMTWriter (fileName, ds); + writer.write; + } catch (mt.MTException e) { + logger.error ("Saving GUI failed:"); + logger.error (e.msg); + + return; + } + } + private static const { + auto RENDERER = "Renderer"; + } //END Loading code /** Draw each window. @@ -171,7 +199,10 @@ private: Window[] windows; // Windows. First window is "on top", others may be obscured. - IRenderer rend; + + IRenderer rend; // Renderer (synonymous with theme) + char[] rendName; // Name of renderer; for saving + // callbacks indexed by their frame pointers: void delegate(ushort cx, ushort cy, ubyte b, bool state) [void*] clickCallbacks; void delegate(ushort cx, ushort cy) [void*] motionCallbacks;
--- a/mde/gui/exception.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/exception.d Wed May 07 13:07:03 2008 +0100 @@ -37,7 +37,7 @@ } } -/// Thrown when or createWidget a Widget class's this() is called with invalid data. +/// Thrown when createWidget or a Widget class's this() is called with invalid data. class WidgetDataException : WindowLoadException { this () { // Default, by Widget class's this @@ -47,3 +47,14 @@ super (msg); } } + +/// Thrown when a Widget class's adjust() is called with invalid data. +class MutableDataException : WindowLoadException +{ + this () { // Default, by Widget class's this + super ("Bad widget mutable data"); + } + this (char[] msg) { // From createWidget + super (msg); + } +}
--- a/mde/gui/renderer/SimpleRenderer.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/renderer/SimpleRenderer.d Wed May 07 13:07:03 2008 +0100 @@ -29,8 +29,6 @@ * The renderer is intended to be per-GUI. */ class SimpleRenderer : IRenderer { - - BorderDimensions getBorder (BORDER_TYPES type) { BorderDimensions dims; with (BORDER_TYPES) with (dims) {
--- a/mde/gui/widget/Ifaces.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/widget/Ifaces.d Wed May 07 13:07:03 2008 +0100 @@ -20,27 +20,26 @@ import mde.gui.IGui; /** Interface for Window, allowing widgets to call some of Window's methods. -* -* Contains the methods in Window available for widgets to call on their root. */ + * + * Contains the methods in Window available for widgets to call on their root. */ interface IWindow : IWidget { /** Widget ID type. Each ID is unique under this window. - * - * Type is int since this is the widget data type. */ + * + * Type is int since this is the widget data type. */ alias int widgetID; /** Get a widget by ID. - * - * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet - * been created, creates it using the Window's widget creation data. - * - * Widgets should never be used more than once (must have a unique parent and position!), and - * this function is only intended for widgets to get child-widgets, hence a warning is logged - * if a widget is asked for more than once. */ - //NOTE: possibly revise: parent isn't actually used any more - IWidget makeWidget (widgetID i, IWidget parent); + * + * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet + * been created, creates it using the Window's widget creation data. */ + IWidget makeWidget (widgetID i); + + /** Add widget's saveData to the data to be saved, returning it's widgetID. */ + widgetID addCreationData (IWidget widget); /** Get the managing Gui. */ + //FIXME: remove and add requestRedraw to allow for only redrawing the window IGui gui (); /** Get the window's renderer. @@ -51,29 +50,47 @@ } /** Interface for widgets. -* -* Note that Window also implements this interface so that widgets can interact with their parent in -* a uniform way. -* -* A widget is a region of a GUI window which handles rendering and user-interaction for itself -* and is able to communicate with it's window and parent/child widgets as necessary. -* -* A widget's constructor should have this prototype: -* ---------------------------------- -* this (IWindow window, IWidget parent, int[] data); -* ---------------------------------- -* Where window is the root window (the window to which the widget belongs), parent is the parent -* widget, and data is an array of initialisation data. The method should throw a -* WidgetDataException (created without parameters) if the data has wrong length or is otherwise -* invalid. -* -* The widget's size should be set either by it's this() method or by the first call to -* setSize(). setSize() is called on all widgets immediately after their creation, and throwing an -* exception at this point (but not on later calls to setSize) is an acceptible method of failure. -*/ + * + * Note that Window also implements this interface so that widgets can interact with their parent + * in a uniform way. + * + * A widget is a region of a GUI window which handles rendering and user-interaction for itself + * and is able to communicate with it's window and parent/child widgets as necessary. + * + * A widget's constructor should have this prototype: + * ---------------------------------- + * this (IWindow window, int[] data); + * ---------------------------------- + * Where window is the root window (the window to which the widget belongs) and data is an array of + * initialisation data. The method should throw a WidgetDataException (created without parameters) + * if the data has wrong length or is otherwise invalid. + * + * The widget's size should be set either by it's this() method or by the first call to + * setSize(). setSize() is called on all widgets immediately after their creation, and throwing an + * exception at this point (but not on later calls to setSize) is an acceptible method of failure. + */ //NOTE: add another this() without the data for default initialization, for the GUI editor? interface IWidget { +//BEGIN Load and save + /** Called after creating widgets to adjust size & other mutable attributes from saved data. + * + * Each widget should call adjust on each of its sub-widgets in turn with data, each time + * replacing data by the return value of the call. It should then take its own mutable data + * from the beginning of the array and return the remainder of the array. + * + * Throws: on error, throw a MutableDataException. */ + int[] adjust (int[] data); + + /** Output data suitible for recreating the widget (data to be passed to this()). */ + int[] getCreationData (); + + /** Output data containing the widget's current adjustments (data to be passed to adjust()). + * Should be a concatenation of each sub-widget's mutable data and the widget's own. */ + int[] getMutableData (); +//END Load and save + +//BEGIN Size and position /** is the width / height resizable? * * If not, the widget has fixed dimensions equal the output of getMinimalSize. */ @@ -89,6 +106,10 @@ /** Used to adjust the size. * + * setPosition should always be called after setSize (for layout widgets). Adding this + * restriction appears to be the most efficient approach without a lot more tests. + * + * Implementation: * The size should be clamped to the widget's minimal size, i.e. the size set may be larger * than that given by the parameters. Conversely, the size should not be reduced to the * widget's maximal size (if any) but expanded as necessary (alignment to be implemented). @@ -98,8 +119,9 @@ /** Set the current position (i.e. called on init and move). */ void setPosition (int x, int y); +//END Size and position - +//BEGIN Events /** Recursively scan the widget tree to find the widget under (x,y). * * If called on a widget, that widget should assume the location is over itself, and so should @@ -118,7 +140,7 @@ * * Widget may assume coordinates are on the widget (caller must check). */ void clickEvent (ushort cx, ushort cy, ubyte b, bool state); - +//END Events /** Draw, using the stored values of x and y. *
--- a/mde/gui/widget/Widget.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/widget/Widget.d Wed May 07 13:07:03 2008 +0100 @@ -29,6 +29,28 @@ * IWidget); they are simply provided for convenience and to promote code reuse. */ abstract class Widget : IWidget { + // Base this(); all widgets must at least check data.length is correct. + this (IWindow wind, int[] data) { + window = wind; + widgetType = data[0]; + } + + // Most widgets don't need to do adjustments based on mutable data, however they usually do + // still need to set their size. + int[] adjust (int[] data) { + setSize (0,0); + return data; + } + + // Widget type should always be the first value. + int[] getCreationData () { + return [widgetType]; + } + // Most widgets don't use mutable data. + int[] getMutableData () { + return []; + } + void getCurrentSize (out int cw, out int ch) { cw = w; ch = h; @@ -54,12 +76,23 @@ } protected: + final int widgetType; // the type (stored for saving) IWindow window; // the enclosing window int x, y; // position int w, h; // size } /** A base for fixed-size widgets. */ class FixedWidget : Widget { + this (IWindow wind, int[] data) { + super (wind, data); + w = wF = data[1]; + h = hF = data[2]; + } + + int[] getCreationData () { + return [widgetType, wF, hF]; + } + bool isWSizable () { return false; } bool isHSizable () { return false; } @@ -80,6 +113,10 @@ } /** A base for resizable widgets. */ class SizableWidget : Widget { + this (IWindow wind, int[] data) { + super (wind, data); + } + bool isWSizable () { return true; } bool isHSizable () { return true; } @@ -97,23 +134,18 @@ /// A fixed-size blank widget. class FixedBlankWidget : FixedWidget { - this (IWindow wind, IWidget, int[] data) { - if (data.length != 2) throw new WidgetDataException; - - window = wind; - - w = wF = data[0]; - h = hF = data[1]; + this (IWindow wind, int[] data) { + if (data.length != 3) throw new WidgetDataException; + super (wind, data); } } /// A completely resizable blank widget (initial size zero). class SizableBlankWidget : SizableWidget { - this (IWindow wind, IWidget, int[] data) { - if (data.length != 0) throw new WidgetDataException; - - window = wind; + this (IWindow wind, int[] data) { + if (data.length != 1) throw new WidgetDataException; + super (wind, data); } } @@ -124,13 +156,9 @@ // pushed is not the same as the button being clicked but not yet released. // it is whether the mouse is over the button after being clicked. - this (IWindow wind, IWidget, int[] data) { - if (data.length != 2) throw new WidgetDataException; - - window = wind; - - w = wF = data[0]; - h = hF = data[1]; + this (IWindow wind, int[] data) { + if (data.length != 3) throw new WidgetDataException; + super (wind, data); } void draw () {
--- a/mde/gui/widget/Window.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/widget/Window.d Wed May 07 13:07:03 2008 +0100 @@ -25,8 +25,7 @@ import mt = mde.mergetag.DataSet; import tango.scrapple.text.convert.parseTo : parseTo; -// not yet implemented: -//import tango.scrapple.text.convert.parseFrom : parseFrom; +import tango.scrapple.text.convert.parseFrom : parseFrom; import tango.util.log.Log : Log, Logger; @@ -59,20 +58,23 @@ assert (gui !is null, "Window.finalise ("~name~"): gui is null"); } body { // Check data was loaded: - if (widgetData is null) throw new WindowLoadException ("No widget data"); + if (widgetData is null || mutableData is null) + throw new WindowLoadException ("No widget/mutable data"); gui_ = gui; rend = gui.renderer; - // Create the primary widget (and indirectly all sub-widgets), throwing on error: - widget = makeWidget (0, this); // primary widget always has ID 0. - widgetData = null; // data is no longer needed: allow GC to collect (cannot safely delete) - // Get border sizes border = rend.getBorder (BORDER_TYPES.WINDOW_TOTAL); resize = rend.getBorder (BORDER_TYPES.WINDOW_RESIZE); - widget.setSize (0,0); // set the minimal size + // Create the primary widget (and indirectly all sub-widgets), throwing on error: + widget = makeWidget (0); // primary widget always has ID 0. + widgetData = null; // data is no longer needed: allow GC to collect (cannot safely delete) + + widget.adjust (mutableData); // adjust/set size, etc. + mutableData = null; // no longer needed + widget.getCurrentSize (w,h); // and get this size w += border.l + border.r; // Adjust for border h += border.t + border.b; @@ -86,19 +88,46 @@ } //BEGIN Mergetag code void addTag (char[] tp, mt.ID id, char[] dt) { - if (tp == "int[][int]") { - if (id == "widgetData") { + // Priority is HIGH_LOW, so don't overwrite data which has already been loaded. + if (tp == INTAA) { + if (id == WDGD && widgetData == null) { widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt); } - } else if (tp == "int") { - if (id == "x") { + } else if (tp == INTA) { + if (id == MD && mutableData == null) { + mutableData = parseTo!(int[]) (dt); + } + } else if (tp == INT) { + if (id == X && x == -1) { x = parseTo!(int) (dt); - } else if (id == "y") { + } else if (id == Y && y == -1) { y = parseTo!(int) (dt); } } } - void writeAll (ItemDelg dlg) { + void writeAll (ItemDelg dlg) + in { + assert (widgetData is null, "Window.writeAll: widgetData !is null"); + } body { + /+ NOTE: currently editing is impossible... + if (edited) { // only save the widget creation data if it's been adjusted: + addSaveData (widget); // generate widget save data + dlg (INTAA, WDGD, parseFrom!(int[][int]) (widgetData)); + }+/ + // Save mutable data: + dlg (INTA, MD, parseFrom!(int[]) (widget.getMutableData)); + // Save the window position: + dlg (INT, X, parseFrom!(int) (x)); + dlg (INT, Y, parseFrom!(int) (y)); + } + private static const { + auto INTAA = "int[][int]"; + auto INTA = "int[]"; + auto INT = "int"; + auto WDGD = "widgetData"; + auto MD = "mutableData"; + auto X = "x"; + auto Y = "y"; } //END Mergetag code //END Methods for GUI @@ -107,7 +136,7 @@ /** Get/create a widget by ID. * * Should $(I only) be called internally and by sub-widgets! */ - IWidget makeWidget (widgetID i, IWidget parent) + IWidget makeWidget (widgetID i) in { // widgetData is normally left to be garbage collected after widgets have been created: assert (widgetData !is null, "Window.makeWidget ("~name~"): widgetData is null"); @@ -120,11 +149,24 @@ throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found"); // Throws WidgetDataException (a WindowLoadException) if bad data: - IWidget widg = createWidget (this, parent, *d); - widgets ~= widg; - return widg; + return createWidget (this, *d); } + /** Add this widget's data to that to be saved, returning it's widgetID. */ + widgetID addCreationData (IWidget widget) + { + widgetID i; + if (widgetData is null) + i = 0; + else + i = widgetData.keys[$-1] + 1; + + widgetData[i] = null; // Make sure the same ID doesn't get used by a recursive call + widgetData[i] = widget.getCreationData; + + return i; + } + IGui gui () { return gui_; } @@ -135,6 +177,17 @@ //END IWindow methods //BEGIN IWidget methods + //FIXME: how many of these methods are actually needed/used? + int[] adjust (int[]) { // simply not relevant (never used) + return []; + } + int[] getCreationData () { // simply not relevant (never used) + return []; + } + int[] getMutableData () { // simply not relevant (never used) + return []; + } + bool isWSizable () { return widget.isWSizable; } @@ -200,25 +253,32 @@ /* check for resizes (different to above; use whole border giving larger area for * diagonal resizes). */ resizeType = RESIZE.NONE; - if (cx < x + border.l) { - xDrag = w + cx; - resizeType = RESIZE.L; - } - else if (cx >= xw - border.r) { - xDrag = w - cx; - resizeType = RESIZE.R; + + if (isWSizable) { + if (cx < x + border.l) { + xDrag = w + cx; + resizeType = RESIZE.L; + } + else if (cx >= xw - border.r) { + xDrag = w - cx; + resizeType = RESIZE.R; + } } - if (cy < y + border.t) { - yDrag = h + cy; - resizeType |= RESIZE.T; - } - else if (cy >= yh - border.b) { - yDrag = h - cy; - resizeType |= RESIZE.B; + if (isHSizable) { + if (cy < y + border.t) { + yDrag = h + cy; + resizeType |= RESIZE.T; + } + else if (cy >= yh - border.b) { + yDrag = h - cy; + resizeType |= RESIZE.B; + } } - gui_.addClickCallback (&endCallback); - gui_.addMotionCallback (&resizeCallback); + if (resizeType != RESIZE.NONE) { // only if some valid size is being done + gui_.addClickCallback (&endCallback); + gui_.addMotionCallback (&resizeCallback); + } } else { // window is being moved xDrag = cx - x; yDrag = cy - y; @@ -252,22 +312,26 @@ getMinimalSize (mw, nw); // (only want mw) nw = xDrag - cx; if (nw < mw) nw = mw; // clamp - setPosition (x + w - nw, y); + mw = x + w - nw; // reuse setSize (nw, h); + setPosition (mw, y); } else if (resizeType & RESIZE.R) { setSize (xDrag + cx, h); + setPosition (x, y); } if (resizeType & RESIZE.T) { int mh, nh; - getMinimalSize (nh, mh); // (only want mh) + getMinimalSize (nh, mh); nh = yDrag - cy; - if (nh < mh) nh = mh; // clamp - setPosition (x, y + h - nh); + if (nh < mh) nh = mh; + mh = y + h - nh; setSize (w, nh); + setPosition (x, mh); } else if (resizeType & RESIZE.B) { setSize (w, yDrag + cy); + setPosition (x, y); } } void endCallback (ushort cx, ushort cy, ubyte b, bool state) { @@ -284,16 +348,19 @@ RESIZE resizeType; // Type of current resize //END Window moving and resizing - char[] name; // The window's name (id from config file) - IGui gui_; // The gui managing this window + // Load/save data: + public char[] name; // The window's name (id from config file) + //bool edited = false; // True if any widgets have been edited (excluding scaling) + // Data used for saving and loading (null in between): + int[][widgetID] widgetData = null;// Data for all widgets under this window + int[] mutableData = null; // Widget's mutable data (adjusted sizes, etc.) - int[][widgetID] widgetData; // Data for all widgets under this window (deleted after loading) - IWidget[] widgets; // List of all widgets under this window (created on demand). Use for saving? + IGui gui_; // The gui managing this window + IRenderer rend; // The window's renderer + IWidget widget; // The primary widget in this window. - IRenderer rend; // The window's renderer - // FIXME: revise which parameters are stored once Gui knows window position - int x,y; // Window position + int x = -1, y = -1; // Window position int w,h; // Window size (calculated from Widgets) int xw, yh; // x+w, y+h (frequent use by clickEvent) int widgetX, widgetY; // Widget position (= window position plus BORDER_WIDTH)
--- a/mde/gui/widget/createWidget.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/widget/createWidget.d Wed May 07 13:07:03 2008 +0100 @@ -24,14 +24,13 @@ /** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation * data [1..$]. */ -IWidget createWidget (IWindow window, IWidget parent, int[] data) +IWidget createWidget (IWindow window, int[] data) in { assert (window !is null, "createWidget: window is null"); - assert (parent !is null, "createWidget: parent is null"); } body { 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 + // the whole of data is passed to the Widget mixin (binarySearch ("type", WIDGETS)); throw new WidgetDataException ("Bad widget type"); @@ -100,7 +99,7 @@ ret ~= indent(indents); foreach (c; consts) { ret ~= "if (" ~ var ~ " == WIDGET_TYPE." ~ c ~ ") {\n" ~ - indent(indents+1) ~ "return new " ~ c ~ "Widget (window, parent, data);\n" ~ + indent(indents+1) ~ "return new " ~ c ~ "Widget (window, data);\n" ~ indent(indents) ~ "} else "; } ret = ret[0..$-6] ~ '\n'; // remove last else
--- a/mde/gui/widget/layout.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/gui/widget/layout.d Wed May 07 13:07:03 2008 +0100 @@ -17,34 +17,74 @@ module mde.gui.widget.layout; import mde.gui.widget.Widget; -import mde.gui.exception : WidgetDataException; +import mde.gui.exception; /** Encapsulates a grid of Widgets. * * Since a grid with either dimension zero is not useful, there must be at least one sub-widget. */ class GridLayoutWidget : Widget { - this (IWindow wind, IWidget, int[] data) { + this (IWindow wind, int[] data) { // Get grid size and check data // Check sufficient data for rows, cols, and at least one widget: - if (data.length < 3) throw new WidgetDataException; - rows = data[0]; - cols = data[1]; - if (data.length != 2 + rows * cols) throw new WidgetDataException; + if (data.length < 4) throw new WidgetDataException; + super (wind, data); + + rows = data[1]; + cols = data[2]; + if (data.length != 3 + rows * cols) throw new WidgetDataException; /* data.length >= 3 so besides checking the length is correct, this tells us: - * rows * cols >= 3 - 2 = 1 a free check! + * rows * cols >= 4 - 3 = 1 a free check! * The only thing not checked is whether both rows and cols are negative, which would * cause an exception when dynamic arrays are allocated later (and is unlikely). */ - window = wind; - // Get all sub-widgets subWidgets.length = rows*cols; foreach (i, inout subWidget; subWidgets) { - subWidget = window.makeWidget (data[i+2], this); + subWidget = window.makeWidget (data[i+3]); } } + int[] adjust (int[] data) { + // Give all sub-widgets their data: + foreach (widget; subWidgets) + data = widget.adjust (data); + if (data.length < rows + cols) throw new MutableDataException; + + /* We basically short-cut setSize by loading previous col/row sizes and doing the final + * calculations. There isn't checks that the data is valid/up-to-date... worst case is too + * small overlapping widgets or huge ones? + * Note: if setSize gets called afterwards, it should have same dimensions and so not do + * anything. */ + colW = data[0..cols]; + rowH = data[cols..rows+cols]; + setColRowSizes; + w = colW[$-1] + colX[$-1]; + h = rowY[$-1] + rowH[$-1]; + + return data[rows+cols..$]; + } + + int[] getCreationData () { + int[] ret; + ret.length = 3 + subWidgets.length; + + ret [0..3] = [widgetType, rows, cols]; // first data + + foreach (i,widget; subWidgets) // sub widgets + ret[i+3] = window.addCreationData (widget); + + return ret; + } + int[] getMutableData () { + int[] ret; + foreach (widget; subWidgets) + ret ~= widget.getMutableData; + + ret ~= colW ~ rowH; + return ret; + } + bool isWSizable () { if (colSizable == -2) { // check whether any columns are resizable for1: @@ -108,6 +148,19 @@ } void setSize (int nw, int nh) { + /* For each of width and height, there are several cases: + * [new value is] more than old value + * -> enlarge any row/column + * same as old value + * -> do nothing + * more than min but less than current value + * -> find an enlarged row/col and reduce size + * -> repeat if necessary + * minimal value or less + * -> clamp to min and use min col/row sizes + */ + // FIXME: implement! + // Step 1: calculate the minimal row/column sizes. alias w mw; // no need for extra vars, just use these alias h mh; @@ -132,34 +185,15 @@ rowH[$-1] += nh - mh; } - // Step 3: set each sub-widget's size. - foreach (i,widget; subWidgets) - widget.setSize (colW[i % cols], rowH[i / cols]); - w = nw; h = nh; + // Step 3: set each sub-widget's size. // Step 4: calculate the column and row positions - colX.length = cols; - rowY.length = rows; - int spacing = window.renderer.layoutSpacing; - - int cum = 0; - foreach (i, x; rowH) { - rowY[i] = cum; - cum += x + spacing; - } - - cum = 0; - foreach (i, x; colW) { - colX[i] = cum; - cum += x + spacing; - } + setColRowSizes; // Step 5: position needs resetting - // FIXME: find a more efficient method of doing this? - // maybe setPosition should ALWAYS be called after setSize? - setPosition (x,y); + // Currently this happens by specifying that setPosition should be run after setSize. } void setPosition (int x, int y) { @@ -228,6 +262,29 @@ } } + void setColRowSizes () { + // Calculate column and row locations: + colX.length = cols; + rowY.length = rows; + int spacing = window.renderer.layoutSpacing; + + int cum = 0; + foreach (i, x; rowH) { + rowY[i] = cum; + cum += x + spacing; + } + + cum = 0; + foreach (i, x; colW) { + colX[i] = cum; + cum += x + spacing; + } + + // Tell subwidgets their new sizes: + foreach (i,widget; subWidgets) + widget.setSize (colW[i % cols], rowH[i / cols]); + } + protected: int cols, rows; // number of cells in grid
--- a/mde/scheduler/Scheduler.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/scheduler/Scheduler.d Wed May 07 13:07:03 2008 +0100 @@ -119,10 +119,12 @@ /** Generate an ID. All generated IDs are >= 0xF000_0000 to provide plenty of room for other * IDs. */ ID getNewID () { - // For now use a very simple method to find a vacant ID: iterate - for (ID i = 0xF000_0000; i < ID.max; ++i) - if ((i in funcs) is null) - return i; + if (funcs.length == 0) return 0xF000_0000; // otherwise would get an out-of-bounds error + // Take the last used ID and add one, making sure it's at least 0xF000_0000. + // Don't bother checking if it's out of bounds since there's 2^28 available IDs. + ID i = funcs.keys[$-1] + 1; + if (i < 0xF000_0000) i = 0xF000_0000; + return i; } /** This function should get called by the main loop, once per frame.
--- a/mde/scheduler/init2.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/scheduler/init2.d Wed May 07 13:07:03 2008 +0100 @@ -50,12 +50,22 @@ void guiLoad () { // init func try { - gui.load ("gui"); + gui.load (GUI); + cleanup.addFunc (&guiSave, "guiSave"); } catch (Exception e) { logger.fatal ("guiLoad failed: " ~ e.msg); setInitFailure; } } +void guiSave () { // cleanup func + try { + gui.save (GUI); + } catch (Exception e) { + logger.fatal ("guiSave failed: " ~ e.msg); + setInitFailure; + } +} +private const GUI = "gui"; void initInput () { // init func try {
--- a/mde/scheduler/initFunctions.d Mon May 05 17:02:21 2008 +0100 +++ b/mde/scheduler/initFunctions.d Wed May 07 13:07:03 2008 +0100 @@ -65,6 +65,8 @@ } InitStage init; // all functions called during init (all should be thread-safe) +//FIXME: implement: +InitStage save; // all functions to be called to save data (possible to run more than once) InitStage cleanup; // all functions called during cleanup (all should be thread-safe) package: