# HG changeset patch # User Diggory Hardy # Date 1231090515 0 # Node ID d3b2cefd46c9aed3722c9e62b7a8c7002aa4268f # Parent f96e8d18c00a6b3f4607780613c0c67e73e8dbcc minSizeChange() allows run-time changes to widgets' minimal size (except for shrinking in a GridLayoutWidget). FloatingAreaWidget: correct resize limiters AStringContent: allows space and ignores modifier keys diff -r f96e8d18c00a -r d3b2cefd46c9 codeDoc/ideas.txt --- a/codeDoc/ideas.txt Fri Jan 02 18:10:14 2009 +0000 +++ b/codeDoc/ideas.txt Sun Jan 04 17:35:15 2009 +0000 @@ -29,3 +29,6 @@ -> with complicated substructures, may not be very intuitive -> limit to popup menus? -> these keyboard events only passed if activated by code outside the WidgetManager and no text input callback is active? + +Content: +-> Per-content undo support? diff -r f96e8d18c00a -r d3b2cefd46c9 codeDoc/jobs.txt --- a/codeDoc/jobs.txt Fri Jan 02 18:10:14 2009 +0000 +++ b/codeDoc/jobs.txt Sun Jan 04 17:35:15 2009 +0000 @@ -3,15 +3,13 @@ In progress: +Dynamic minimal size change: can only increase minimal size in layouts. To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): Also see todo.txt and FIXME/NOTE comment marks. -5 Protection on recursion of widgets; can cause sigsegv (enum using ContentListWidget & optVal refering to optBox). Only allow non-content widgets to be instanced once? -4 Make popups use ordinary widgets with int to determine type like other widgets. 4 Close popup menu on button activation/click. -4 When child widgets are resized: must tell parent (bug); 3 Make EnumContent a derivative of BoolContent to use to solve callback problems. 3 Synchronization of IContent with gui (e.g. multiple edit widgets): worth adding (temporary) callbacks? 3 Widget saving: how to deal with modifier functions, esp. when they discard parameters? Remove feature except for dimdata and handle gui editing separately? diff -r f96e8d18c00a -r d3b2cefd46c9 data/conf/guiDemo.mtt --- a/data/conf/guiDemo.mtt Fri Jan 02 18:10:14 2009 +0000 +++ b/data/conf/guiDemo.mtt Sun Jan 04 17:35:15 2009 +0000 @@ -2,8 +2,10 @@ {Working} - - + + + + @@ -14,7 +16,8 @@ - +!{use optBox for no description, optDBox for descriptions under entries} + diff -r f96e8d18c00a -r d3b2cefd46c9 mde/content/AStringContent.d --- a/mde/content/AStringContent.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/content/AStringContent.d Sun Jan 04 17:35:15 2009 +0000 @@ -75,7 +75,7 @@ char[] keyStroke (ushort sym, char[] i) { debug assert (i.length, "StringContent.keyStroke: no value (??)"); // impossible? char k = *i; - if (k > 0x20) { + if (k >= 0x20) { if (k == 0x7f) { // delete size_t p = pos; if (p < sv.length) ++p; @@ -91,7 +91,9 @@ pos = npos; } } else { // use sym; many keys output 0 - if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8 + if (sym >= SDLK_RSHIFT && (sym <= SDLK_COMPOSE || sym == SDLK_MENU)) { + } // all modifier keys and contect menu key; should be ignored + else if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8 char[] tail = sv[pos..$]; if (pos) --pos; while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) @@ -231,7 +233,12 @@ alias opCall opCast; override char[] endEdit () { - v = Int.toInt (sv); + try { + v = Int.toInt (sv); + } catch (Exception e) { + logger.warn (e.msg); + sv = Int.toString (v); + } endEvent; return sv; } @@ -264,7 +271,12 @@ alias opCall opCast; override char[] endEdit () { - v = Float.toFloat (sv); + try { + v = Float.toFloat (sv); + } catch (Exception e) { + logger.warn (e.msg); + sv = Float.toString (v); + } endEvent; return sv; } diff -r f96e8d18c00a -r d3b2cefd46c9 mde/content/Items.d --- a/mde/content/Items.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/content/Items.d Sun Jan 04 17:35:15 2009 +0000 @@ -64,7 +64,7 @@ else if (h == "quit" && item is null) return imde.quit; } - throw new ContentItemException (h); + return new ErrorContent ("Error: bad content specifier",h); } /** Creates some content on first run (required by get()). diff -r f96e8d18c00a -r d3b2cefd46c9 mde/content/miscContent.d --- a/mde/content/miscContent.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/content/miscContent.d Sun Jan 04 17:35:15 2009 +0000 @@ -52,15 +52,16 @@ } /** Created on errors to display and log a message. */ -class ErrorContent : IContent +class ErrorContent : Content { - this (char[] msg) { - this.msg = msg; + this (char[] name, char[] msg) { + super (name); + this.msg = msg; } override char[] toString (uint i) { return i == 0 ? msg - : i == 1 ? "Error" + : i == 1 ? name_ : null; } @@ -71,4 +72,8 @@ /** A Content with no value but able to pass on an event. * * The point being that a button can be tied to one of these. */ -alias Content EventContent; +class EventContent : Content { + this (char[] symbol) { + super (symbol); + } +} diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/WidgetManager.d Sun Jan 04 17:35:15 2009 +0000 @@ -242,6 +242,8 @@ // If call reaches the widget manager there isn't any recursion. //NOTE: should be override final void recursionCheck (widgetID) {} + //FIXME: implement + override void minSizeChange (IChildWidget widget, wdim mw, wdim mh) {} //END IParentWidget methods //BEGIN IWidgetManager methods @@ -404,7 +406,7 @@ GridLayout = TAKES_CONTENT | 0x100, ContentList = TAKES_CONTENT | SAFE_RECURSION | 0x110, - FloatingArea = 0x200, + FloatingArea = TAKES_CONTENT | 0x200, } // Only used for binarySearch algorithm generation; must be ordered by numerical values. @@ -413,7 +415,6 @@ "SizableBlank", "Debug", "TextLabel", - "FloatingArea", "addContent", "PopupMenu", "SubMenu", @@ -424,6 +425,7 @@ "ButtonContent", "MenuButtonContent", "GridLayout", + "FloatingArea", "subMenuContent", "ContentList", "editContent", diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/renderer/IRenderer.d --- a/mde/gui/renderer/IRenderer.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/renderer/IRenderer.d Sun Jan 04 17:35:15 2009 +0000 @@ -139,8 +139,9 @@ //BEGIN Methods /** Returns a Border containing dimensions. * - * The dimensions may depend on whether the widget is resizable (horizontally and vertically). - */ + * Params: + * type = Flags from BTYPE describing whether the border allows resizing and moving and + * its size. */ Border getBorder (Border.BTYPE type, bool wSizable, bool hSizable); /** Return the renderer's between-widget spacing (for layout widgets). */ diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/widget/Floating.d --- a/mde/gui/widget/Floating.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/widget/Floating.d Sun Jan 04 17:35:15 2009 +0000 @@ -18,6 +18,7 @@ import mde.gui.widget.Widget; import mde.gui.exception; +import mde.content.Content; import tango.util.log.Log : Log, Logger; @@ -32,13 +33,12 @@ * Rationale: parents' need to set subwidgets' positions when its position is set, so it needs to * know their positions. * - * Data: Each string item is interpreted as a subwidget widgetID. - * 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). - */ + * Data: Each string item is interpreted as a sub-widget widgetID to add as a floating window. + * Ints consist of widget type followed by a border type (flags from IRenderer.Border.BTYPE) for + * each sub-widget. */ class FloatingAreaWidget : AParentWidget { - this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data) { + this (IWidgetManager mgr, IParentWidget parent, widgetID id, WidgetData data, IContent content) { if (data.ints.length != 1 + data.strings.length) throw new WidgetDataException (this); super (mgr, parent, id); @@ -47,7 +47,7 @@ sWOrder.length = subWidgets.length; sWData.length = subWidgets.length; foreach (i,s; data.strings) { - subWidgets[i] = mgr.makeWidget (this, s); + subWidgets[i] = mgr.makeWidget (this, s, content); sWOrder[i] = i; sWData[i].borderType = cast(BTYPE) data.ints[i+1]; } @@ -116,6 +116,39 @@ subWidgets[i].setPosition (x + d.x + d.border.x1, y + d.y + d.border.y1); } + override void minSizeChange (IChildWidget widg, wdim nmw, wdim nmh) { + size_t i = 0; + while (i < subWidgets.length) { + if (subWidgets[i] is widg) + goto gotI; + ++i; + } + debug logger.error ("minSizeChange: widget is not my child (code error)"); + return; + + gotI: + with (sWData[i]) { + mw = nmw + border.x1 + border.x2; + mh = nmh + border.y1 + border.y2; + + if (mw > w || !widg.isWSizable) { + w = mw; + widg.setWidth (w - border.x1 - border.x2, -1); + } + if (mh > h || !widg.isHSizable) { + h = mh; + widg.setHeight (h - border.y1 - border.y2, -1); + } + + if (x + w > this.w) + x = (w > this.w) ? 0 : this.w - w; + if (y + h > this.h) + y = (h > this.h) ? 0 : this.h - h; + + mgr.requestRedraw; + } + } + override void draw () { super.draw; @@ -210,14 +243,20 @@ with (sWData[active]) { if (resizeType & RESIZE.X1) { wdim ow = w; - w = xDrag - cx; - if (w < mw) w = mw; - x += ow - w; + w = xDrag - cx; // new width + if (w < mw) w = mw; // limit to min width + x += ow - w; // move by difference + if (x < 0) { // limit to left edge of area + w += x; + x = 0; + } subWidgets[active].setWidth (w - border.x1 - border.x2, 1); } else if (resizeType & RESIZE.X2) { - w = xDrag + cx; - if (w < mw) w = mw; + w = xDrag + cx; // new width + if (w < mw) w = mw; // limit to min width + if (x + w > this.w) // limit to right edge of area + w = this.w - x; subWidgets[active].setWidth (w - border.x1 - border.x2, -1); } if (resizeType & RESIZE.Y1) { @@ -225,11 +264,17 @@ h = yDrag - cy; if (h < mh) h = mh; y += oh - h; + if (y < 0) { + h += y; + y = 0; + } subWidgets[active].setHeight (h - border.y1 - border.y2, 1); } else if (resizeType & RESIZE.Y2) { h = yDrag + cy; if (h < mh) h = mh; + if (y + h > this.h) + h = this.h - y; subWidgets[active].setHeight (h - border.y1 - border.y2, -1); } // Reposition widget and sub-widgets: diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/widget/Ifaces.d Sun Jan 04 17:35:15 2009 +0000 @@ -31,6 +31,8 @@ /************************************************************************************************* * Interface for parent widgets, including IWidgetManager. * + * All widgets implement this via AWidget to make things simpler (code sharing). + * * 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 @@ -40,6 +42,18 @@ { /** Checks for recursion of unsafe widgets to prevent infinite recursion. */ void recursionCheck (widgetID); + + /** Child widgets should call this on their parent if their minimal size changes, since they + * cannot resize themselves. + * + * Parents should resize children calling this when the size is increased, and possibly when + * decreased and the widget is not resizable (but should not do so when it is). + * + * Params: + * widget = The child widget calling the function + * mw = New minimal width + * mh = New minimal height */ + void minSizeChange (IChildWidget widget, wdim mw, wdim mh); } diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/widget/TextWidget.d --- a/mde/gui/widget/TextWidget.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/widget/TextWidget.d Sun Jan 04 17:35:15 2009 +0000 @@ -145,7 +145,8 @@ override void keyEvent (ushort s, char[] i) { adapter.text = content.keyStroke (s, i); adapter.index = content.editIndex; - adapter.getDimensions (mw, mh); // NOTE: only passively change size: next resize will see new minimal size + adapter.getDimensions (mw, mh); + parent.minSizeChange (this, mw, mh); mgr.requestRedraw; } override void keyFocusLost () { diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/widget/Widget.d --- a/mde/gui/widget/Widget.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/widget/Widget.d Sun Jan 04 17:35:15 2009 +0000 @@ -56,6 +56,9 @@ throw new GuiException ("Infite recursion of "~a); parent.recursionCheck (a); } + + // Parent widgets need to implement this. + override void minSizeChange (IChildWidget widget, wdim mw, wdim mh) {} //END IParentWidget methods //BEGIN Load and save diff -r f96e8d18c00a -r d3b2cefd46c9 mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/gui/widget/layout.d Sun Jan 04 17:35:15 2009 +0000 @@ -100,7 +100,6 @@ 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; rows = cList.list.length; subWidgets.length = rows; @@ -116,7 +115,8 @@ } } else { rows = cols = 1; - subWidgets = [mgr.makeWidget (this, data.strings[0], new ErrorContent (""))]; + subWidgets = [mgr.makeWidget (this, data.strings[0], + new ErrorContent ("",null)) ]; } } @@ -142,6 +142,7 @@ * * The grid has no border but has spacing between widgets. *************************************************************************************************/ +// Note: mw, mh inherited from AWidget are not used; use col.mw, row.mw instead. abstract class GridWidget : AParentWidget { //BEGIN Creation & saving @@ -192,8 +193,6 @@ } initWidths = null; // free - mw = col.mw; - mh = row.mw; w = col.w; h = row.w; @@ -215,6 +214,14 @@ return row.firstSizable >= 0; } + // mw, mh not used + override wdim minWidth () { + return col.mw; + } + override wdim minHeight () { + return row.mw; + } + override void setWidth (wdim nw, int dir) { w = col.resizeWidth (nw, dir); // Note: setPosition must be called after! @@ -232,6 +239,26 @@ foreach (i,widget; subWidgets) widget.setPosition (x + col.pos[i % cols], y + row.pos[i / cols]); } + + override void minSizeChange (IChildWidget widg, wdim nmw, wdim nmh) { + size_t c = 0; + while (c < subWidgets.length) { + if (subWidgets[c] is widg) + goto gotCell; + ++c; + } + debug logger.error ("minSizeChange: widget is not my child (code error)"); + return; + + gotCell: + if (col.newMinWidth (c % cols, nmw) || + row.newMinWidth (c / cols, nmh)) { + parent.minSizeChange (this, col.mw, row.mw); + w = col.w; + h = row.w; + } + mgr.requestRedraw; + } //END Size & position @@ -545,6 +572,7 @@ diff = adjustCellSizes (diff, minWidth.length-1, -1); else diff = adjustCellSizes (diff, (dir == -1 ? lastSizable : firstSizable), dir); + genPositions; debug if (nw != w) { logger.trace ("resizeWidth on {} to {} failed, new width: {}, diff {}, firstSizable {}, columns {}",cast(void*)this, nw,w, diff, firstSizable, minWidth.length); @@ -596,6 +624,29 @@ diff = -adjustCellSizes (diff, resizeD, -1); adjustCellSizes (diff, resizeU, 1); } + genPositions; + } + + /** Called when one of the cells in column col now has minimal width nmw. + * + * Enlarges column minimal width if necessary; tries to keep total width the same but returns + * true if it cannot. */ + bool newMinWidth (myDiff col, wdim nmw) { + bool r = false; + if (minWidth[col] < nmw) { + minWidth[col] = nmw; + wdim d = nmw - width[col]; + if (d > 0 || (d < 0 && !sizable[col])) { + d = -adjustCellSizes (d, col, -1); + if (d != adjustCellSizes (d, minWidth.length-1, -1)) + r = true; // if unable to keep overall size the same + genPositions; + } + mw = spacing * cast(wdim)(minWidth.length - 1); + foreach (imw; minWidth) + mw += imw; + } + return r; } /* Generate position infomation for each column and set w. */ @@ -666,7 +717,6 @@ } // else no adjustment needed (diff == 0) - genPositions; return diff; } @@ -687,11 +737,11 @@ * Treat as READ ONLY outside this class! */ wdim[] width; // only adjusted within the class wdim[] pos; /// ditto + wdim spacing; // used by genPositions (which cannot access the layout class's data) + wdim w,mw; // current & minimal widths protected: myDiff resizeD, // resizeCols works down from this index (<0 if not resizing) resizeU; // and up from this index - wdim spacing; // used by genPositions (which cannot access the layout class's data) - wdim w,mw; // current & minimal widths /* indicies of the first/last resizable column (negative if none are resizable). */ myDiff firstSizable = -1, lastSizable = -1; // set by calcFLSbl // Callbacks used to actually adjust a column's width: diff -r f96e8d18c00a -r d3b2cefd46c9 mde/imde.d --- a/mde/imde.d Fri Jan 02 18:10:14 2009 +0000 +++ b/mde/imde.d Sun Jan 04 17:35:15 2009 +0000 @@ -26,10 +26,11 @@ input = new Input(); mainSchedule = new Scheduler; - quit = (new EventContent("quit")).addCallback ((Content){ + quit = new EventContent("quit"); + quit.addCallback ((Content){ run = false; }); - menu = new ContentList ("menu",[quit, + menu = new ContentList ("menu",[cast(Content) quit, new EventContent("a"), new ContentList("subMenu",[ new EventContent("b"),