# HG changeset patch # User Diggory Hardy # Date 1224579439 -3600 # Node ID 085f2ca31914e70c568ea947713f42a9768afa55 # Parent 4d5d53e4f881cfda9497597bfda35a4236432c80 Shared alignments supported in more complex cases. diff -r 4d5d53e4f881 -r 085f2ca31914 data/conf/gui.mtt --- a/data/conf/gui.mtt Thu Oct 16 17:43:48 2008 +0100 +++ b/data/conf/gui.mtt Tue Oct 21 09:57:19 2008 +0100 @@ -7,9 +7,11 @@ - + + + diff -r 4d5d53e4f881 -r 085f2ca31914 data/conf/options.mtt --- a/data/conf/options.mtt Thu Oct 16 17:43:48 2008 +0100 +++ b/data/conf/options.mtt Tue Oct 21 09:57:19 2008 +0100 @@ -5,11 +5,6 @@ - - - - - {font} diff -r 4d5d53e4f881 -r 085f2ca31914 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Thu Oct 16 17:43:48 2008 +0100 +++ b/mde/gui/WidgetManager.d Tue Oct 21 09:57:19 2008 +0100 @@ -62,8 +62,7 @@ Screen.addDrawable (this); } - // NOTE - temporarily here to allow CTOR to run safely during static this - // called during init + // 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: @@ -170,6 +169,7 @@ rend = createRenderer (rendName); child = makeWidget ("root"); + finalize; mw = child.minWidth; mh = child.minHeight; @@ -384,7 +384,7 @@ /** Create a widget by ID. * * A widget instance is created from data found under ID. Multiple instances may be created. - * FIXME - data conflicts when saving? + * 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 @@ -394,6 +394,37 @@ return createWidget (this, id, curData[id], 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 + * shared alignments to be initialized properly: + * 1. all sharing members need to know their children's min size + * 2. all sharing members need to add their children's min size to the alignment + * 3. all sharing members can only then get their min size + * This method will fail if alignment members are not all of the same generation. An alternate + * method without this drawback would be to have shared alignments created with a list of + * pointers to their members, and once all members have been created the alignment could + * initialize itself, first making sure each members' children have been initialized. */ + void finalize () { + IChildWidget[][] descendants; // first index: depth; is a list of widgets at each depth + + void recurseChildren (size_t depth, IChildWidget widget) { + foreach (child; widget.children) + recurseChildren (depth+1, child); + + if (descendants.length <= depth) + descendants.length = depth * 2 + 1; + descendants[depth] ~= widget; + } + + recurseChildren (0, child); + foreach_reverse (generation; descendants) { + foreach (widget; generation) + widget.prefinalize; + foreach (widget; generation) + widget.finalize; + } + } + /** For making changes. */ void setData (widgetID id, WidgetData d) { changes[id] = d; // also updates WidgetDataSet in data. @@ -405,6 +436,7 @@ * --- * // 1. Create the root widget: * child = makeWidget ("root"); + * finalize; * // 2. Set the setSize, e.g.: * child.setWidth (child.minWidth, 1); * child.setHeight (child.minHeight, 1); diff -r 4d5d53e4f881 -r 085f2ca31914 mde/gui/widget/Floating.d --- a/mde/gui/widget/Floating.d Thu Oct 16 17:43:48 2008 +0100 +++ b/mde/gui/widget/Floating.d Tue Oct 21 09:57:19 2008 +0100 @@ -53,12 +53,14 @@ * Ints supplied may consist of just the widget type or * additionally an (x,y) coordinate for each subwidget (all x coords first, then all y coords). */ -class FloatingAreaWidget : SizableWidget +class FloatingAreaWidget : ParentWidget { this (IWidgetManager mgr, widgetID id, WidgetData data) { subWidgets.length = data.strings.length; foreach (i,s; data.strings) subWidgets[i] = mgr.makeWidget (s); + foreach (w; subWidgets) + w.finalize; sWCoords.length = subWidgets.length; if (data.ints.length != 1) { @@ -80,6 +82,9 @@ } } + bool isWSizable () { return true; } + bool isHSizable () { return true; } + void setPosition (wdim x, wdim y) { super.setPosition (x,y); @@ -97,7 +102,6 @@ } protected: - IChildWidget[] subWidgets; // all subwidgets, framed or not wdimPair[] sWCoords; // coords for subwidgets, relative to this widget /+ diff -r 4d5d53e4f881 -r 085f2ca31914 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Thu Oct 16 17:43:48 2008 +0100 +++ b/mde/gui/widget/Ifaces.d Tue Oct 21 09:57:19 2008 +0100 @@ -135,11 +135,24 @@ * A parent widget is responsible for setting the size of its children widgets, however it must * satisfy their minimal sizes as available from minWidth() and minHeight(). setWidth() and * setHeight() are called on all widgets after creation. + * + * Also see finalize(). *************************************************************************************************/ //NOTE: add another this() without the data for default initialization, for the GUI editor? interface IChildWidget : IWidget { //BEGIN Load and save + // NOTE - change? + /** Called on all widgets after all widgets have been created in a deepest first order. + * + * Must be called before any other methods on the widget, which means this cannot call sub- + * widgets' methods, but finalize can. */ + void prefinalize (); + void finalize (); /// ditto + + /** Widget should return a list of all its children. */ + IChildWidget[] children (); + /** When this is called, if the widget has any changed data to save it should call * IWidgetManager.setData (id, data) to set it and return true. Otherwise it should return * false. @@ -177,10 +190,7 @@ /** The minimal size the widget could be shrunk to (or its fixed size). * - * Takes into account child-widgets and any other contents. - * - * Note: layout uses these calls to initialize it's alignment device. So, after creating a - * (layout) widget, minWidth should be the first function called on it! */ + * Takes into account child-widgets and any other contents. */ wdim minWidth (); wdim minHeight (); /// ditto diff -r 4d5d53e4f881 -r 085f2ca31914 mde/gui/widget/Widget.d --- a/mde/gui/widget/Widget.d Thu Oct 16 17:43:48 2008 +0100 +++ b/mde/gui/widget/Widget.d Tue Oct 21 09:57:19 2008 +0100 @@ -49,7 +49,17 @@ this.mgr = mgr; } + // Most widgets don't need this; all initialization os usually done in this() + void prefinalize () {} + void finalize () {} + + // ParentWidget is inteded for parent widgets to derive + IChildWidget[] children () { + return null; + } + // Don't save any data: fine for many widgets. + // FIXME: implementation could be added to ParentWidget bool saveChanges (widgetID) { return false; } @@ -141,6 +151,23 @@ // resizible; both types of widgets should actually be expandable. } +/************************************************************************************************* +* An abstract base widget class for parent widgets. +*************************************************************************************************/ +abstract class ParentWidget : Widget +{ + this (IWidgetManager mgr, widgetID id, WidgetData data) { + super (mgr, id, data); + } + + IChildWidget[] children () { + return subWidgets; + } + +protected: + IChildWidget[] subWidgets; +} + /** A base for fixed-size widgets taking their size from the creation data. */ class FixedWidget : Widget { // Check data.length is at least 3 before calling! diff -r 4d5d53e4f881 -r 085f2ca31914 mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Thu Oct 16 17:43:48 2008 +0100 +++ b/mde/gui/widget/layout.d Tue Oct 21 09:57:19 2008 +0100 @@ -114,17 +114,26 @@ OptionList optsList = OptionList.trial(); rows = optsList.list.length; cols = 1; + sWId = data.strings[0]; // Get all sub-widgets subWidgets.length = rows*cols; foreach (i, c; optsList.list) { - subWidgets[i] = mgr.makeWidget (data.strings[0], c); + subWidgets[i] = mgr.makeWidget (sWId, c); } super (mgr, id, data); } + bool saveChanges (widgetID id) { + // Since all sub-widgets have the same id, it only makes sense to call on one + if (subWidgets is null) + return false; + return subWidgets[0].saveChanges (sWId); + } + private: OptionList optsList; + widgetID sWId; // sub-widget's ID, for calling saveChanges FIXME no longer pass? } @@ -138,7 +147,7 @@ * * The grid has no border but has spacing between widgets. *************************************************************************************************/ -abstract class GridWidget : Widget +abstract class GridWidget : ParentWidget { //BEGIN Creation & saving /** Partial constructor for a grid layout widget. @@ -148,7 +157,10 @@ * the call to genCachedConstructionData can be moved to the derived this() methods.) * * Derived constructors may also set initWidths to the array of column widths followed by - * row heights used to initially set the row/column dimensions. */ + * row heights used to initially set the row/column dimensions. + * + * Sub-widgets are finalized here, so no methods should be called on sub-widgets before calling + * this super. */ protected this (IWidgetManager mgr, widgetID id, WidgetData data) { super (mgr, id, data); @@ -163,40 +175,39 @@ else row = (new AlignColumns (rows)); row.addSetCallback (&setRowHeight); - - // Calculate cached construction data - genCachedConstructionData; + } + + /** Prior to finalizing but after sub-widgets are finalized, some information needs to be + * passed to the AlignColumns. */ + void prefinalize () { + genCachedConstructionData; // min widths, sizableness } /** Responsible for calculating the minimal size and initializing some stuff. * * As such, this must be the first function called after this(). */ - wdim minWidth () { - if (!alignInit) { // assumes col & row.width are initialized simultaneously - alignInit = true; - if (initWidths) { - debug assert (initWidths.length == cols + rows, "initWidths provided but has bad length"); - col.setWidths (initWidths[0..cols]); - row.setWidths (initWidths[cols..$]); - initWidths = null; // free - } else { - col.setWidths; - row.setWidths; - } - - mw = col.mw; - mh = row.mw; - w = col.w; - h = row.w; - - // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. - foreach (i,widget; subWidgets) { - // Resizing direction is arbitrarily set to negative: - widget.setWidth (col.width[i % cols], -1); - widget.setHeight (row.width[i / cols], -1); - } + void finalize () { + if (initWidths) { + debug assert (initWidths.length == cols + rows, "initWidths provided but has bad length"); + col.setWidths (initWidths[0..cols]); + row.setWidths (initWidths[cols..$]); + initWidths = null; // free + } else { + col.setWidths; + row.setWidths; } - return mw; + + mw = col.mw; + mh = row.mw; + w = col.w; + h = row.w; + + // Tell subwidgets their new sizes. Positions are given by a later call to setPosition. + foreach (i,widget; subWidgets) { + // Resizing direction is arbitrarily set to negative: + widget.setWidth (col.width[i % cols], -1); + widget.setHeight (row.width[i / cols], -1); + } } //END Creation & saving @@ -279,9 +290,10 @@ * Also need to be re-run if the renderer changes. * * rows, cols and subWidgets must be set before calling. Part of the set-up for AlignColumns - * (col and row). */ + * (col and row). subWidgets need to know their minimal size and resizability. */ void genCachedConstructionData () { // Will only change if renderer changes: + // NOTE shared AlignColumns get this set by all sharing GridWidgets col.spacing = row.spacing = mgr.renderer.layoutSpacing; // Calculate the minimal column and row sizes: @@ -361,11 +373,10 @@ myIt cols, rows; // number of cells in grid wdim[] initWidths; // see this / setInitialSize - bool alignInit; // have AlignColumns instances been initialized? /* All widgets in the grid, by row. Order: [ 0 1 ] * [ 2 3 ] */ - IChildWidget[] subWidgets; + //IChildWidget[] subWidgets; AlignColumns col, row; }