changeset 124:a2ef6b549101

Dynamic minimal size changing is now fully supported. Support for reducing minimal size in layouts. Editing numbers as text now always converts new number back to string at end of edit. Floating point number display format changed.
author Diggory Hardy <diggory.hardy@gmail.com>
date Mon, 05 Jan 2009 12:43:27 +0000
parents d3b2cefd46c9
children 3e648bc53bde
files codeDoc/jobs.txt data/conf/guiDemo.mtt mde/content/AStringContent.d mde/gui/widget/Ifaces.d mde/gui/widget/layout.d mde/input/Input.d
diffstat 6 files changed, 158 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/codeDoc/jobs.txt	Sun Jan 04 17:35:15 2009 +0000
+++ b/codeDoc/jobs.txt	Mon Jan 05 12:43:27 2009 +0000
@@ -3,7 +3,6 @@
 
 
 In progress:
-Dynamic minimal size change: can only increase minimal size in layouts.
 
 
 
--- a/data/conf/guiDemo.mtt	Sun Jan 04 17:35:15 2009 +0000
+++ b/data/conf/guiDemo.mtt	Mon Jan 05 12:43:27 2009 +0000
@@ -2,9 +2,8 @@
 <char[]|Renderer="Simple">
 <char[]|Design="Working">
 {Working}
-<WidgetData|root={0:[0x4100,0,3,1],1:["bar","fC","bar"]}>
-<WidgetData|fC={0:[0x2031],1:["Options.MiscOptions.logOutput","float"]}>
-<WidgetData|float={0:[0x4200,14],1:["optBox"]}>
+<WidgetData|root={0:[0x4100,0,3,1],1:["bar","float","bar"]}>
+<WidgetData|float={0:[0x4200,14],1:["optaC"]}>
 <WidgetData|bar={0:[0x4100,14,1,3],1:["menu","blank","menu"]}>
 <WidgetData|menu={0:[0x2031],1:["imde.menu","menu0"]}>
 <WidgetData|menu0={0:[0x4011,0],1:["menu1"]}>
--- a/mde/content/AStringContent.d	Sun Jan 04 17:35:15 2009 +0000
+++ b/mde/content/AStringContent.d	Mon Jan 05 12:43:27 2009 +0000
@@ -171,7 +171,8 @@
     
     override char[] endEdit () {
 	v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1');
-	endEvent;
+        sv = v ? "true" : "false";
+        endEvent;
 	return sv;
     }
     
@@ -237,9 +238,9 @@
             v = Int.toInt (sv);
         } catch (Exception e) {
             logger.warn (e.msg);
-            sv = Int.toString (v);
         }
-	endEvent;
+        sv = Int.toString (v);
+        endEvent;
 	return sv;
     }
     
@@ -258,7 +259,7 @@
     
     void assignNoCb (double val) {
 	v = val;
-	sv = Float.toString (v);
+	sv = Float.toString (v, 8, 4);
 	if (pos > sv.length) pos = sv.length;
     }
     void opAssign (double val) {
@@ -275,9 +276,9 @@
             v = Float.toFloat (sv);
         } catch (Exception e) {
             logger.warn (e.msg);
-            sv = Float.toString (v);
         }
-	endEvent;
+        sv = Float.toString (v, 8, 4);
+        endEvent;
 	return sv;
     }
     
--- a/mde/gui/widget/Ifaces.d	Sun Jan 04 17:35:15 2009 +0000
+++ b/mde/gui/widget/Ifaces.d	Mon Jan 05 12:43:27 2009 +0000
@@ -46,7 +46,7 @@
     /** 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
+     * Parents should resize children calling this when the size is increased, and when
      * decreased and the widget is not resizable (but should not do so when it is).
      * 
      * Params:
--- a/mde/gui/widget/layout.d	Sun Jan 04 17:35:15 2009 +0000
+++ b/mde/gui/widget/layout.d	Mon Jan 05 12:43:27 2009 +0000
@@ -251,11 +251,14 @@
         return;
         
         gotCell:
-        if (col.newMinWidth (c % cols, nmw) ||
-            row.newMinWidth (c / cols, nmh)) {
-            parent.minSizeChange (this, col.mw, row.mw);
+        size_t r = c / cols;
+        c = c % cols;
+        bool sizeChange = col.newMinWidth (c, r+colR, nmw);
+        sizeChange = row.newMinWidth (r, c+rowR, nmh) || sizeChange;
+        if (sizeChange) {
             w = col.w;
             h = row.w;
+            parent.minSizeChange (this, col.mw, row.mw);
         }
         mgr.requestRedraw;
     }
@@ -269,8 +272,8 @@
         debug assert (cx >= x && cx < x + w && cy >= y && cy < y + h, "getWidget: not on widget (code error)");
         
         // Find row/column:
-        myDiff i = col.getCell (cx - x);
-        myDiff j = row.getCell (cy - y);
+        ptrdiff_t i = col.getCell (cx - x);
+        ptrdiff_t j = row.getCell (cy - y);
         if (i < 0 || j < 0)	// on a space between widgets
             return this;
         
@@ -323,7 +326,7 @@
 	
         debug (mdeWidgets) logger.trace ("GridWidget: setup on subWidgets...");
 	foreach (widg; subWidgets) {	// make sure all subwidgets have been set up
-	    debug assert (widg);
+	    debug assert (widg, "null widg");
 	    widg.setup (n,flags);
 	}
         debug (mdeWidgets) logger.trace ("GridWidget: setup on subWidgets...done");
@@ -335,22 +338,24 @@
         col.spacing = row.spacing = useSpacing ? mgr.renderer.layoutSpacing : 0;
         
         // Calculate the minimal column and row sizes:
+        if (colR == size_t.max)
+            colR = col.addRows (rows);
+        if (rowR == size_t.max)
+            rowR = row.addRows (cols);
         // AlignColumns (row, col) takes care of initializing minWidth.
-        foreach (i,widget; subWidgets) {
-            // Increase dimensions if current minimal size is larger:
-            myIt j = i % cols;	// column
-            wdim md = widget.minWidth;
-            if (col.minWidth[j] < md) col.minWidth[j] = md;
-            j = i / cols;		// row
-            md = widget.minHeight;
-            if (row.minWidth[j] < md) row.minWidth[j] = md;
+        for (size_t r = 0; r < rows; ++r) {
+            for (size_t c = 0; c < cols; ++c) {
+                size_t i = r*cols + c;
+                col.minCellWidths[i+colR*cols] = subWidgets[i].minWidth;
+            	row.minCellWidths[(c+rowR)*rows+r] = subWidgets[i].minHeight;
+            }
         }
         
         // Find which cols/rows are resizable:
         // AlignColumns initializes sizable, and sets first and last sizables.
         forCols:
-        for (myIt i = 0; i < cols; ++i) {				// for each column
-            for (myIt j = 0; j < subWidgets.length; j += cols)	// for each row
+        for (size_t i = 0; i < cols; ++i) {				// for each column
+            for (size_t j = 0; j < subWidgets.length; j += cols)	// for each row
                 if (!subWidgets[i+j].isWSizable)	// column not resizable
                     continue forCols;			// continue the outer for loop
             
@@ -359,8 +364,8 @@
         }
         
         forRows:
-        for (myIt i = 0; i < subWidgets.length; i += cols) {	// for each row
-            for (myIt j = 0; j < cols; ++j)			// for each column
+        for (size_t i = 0; i < subWidgets.length; i += cols) {	// for each row
+            for (size_t j = 0; j < cols; ++j)			// for each column
                 if (!subWidgets[i+j].isHSizable)
                     continue forRows;
             
@@ -369,13 +374,13 @@
     }
     
 private:
-    override void setColWidth (myIt i, wdim w, int dir) {
-        for (myIt j = 0; j < rows; ++j) {
+    override void setColWidth (size_t i, wdim w, int dir) {
+        for (size_t j = 0; j < rows; ++j) {
             subWidgets[i + cols*j].setWidth (w, dir);
         }
     }
-    override void setRowHeight (myIt j, wdim h, int dir) {
-        for (myIt i = 0; i < cols; ++i) {
+    override void setRowHeight (size_t j, wdim h, int dir) {
+        for (size_t i = 0; i < cols; ++i) {
             subWidgets[i + cols*j].setHeight (h, dir);
         }
     }
@@ -408,16 +413,18 @@
     wdim dragX, dragY;	// coords where drag starts
     //END Col/row resizing callback
     
-    myIt cols, rows;	// number of cells in grid
+    size_t cols, rows;	// number of cells in grid
     wdim[] initWidths;  // see this / setInitialSize
     uint sADD_n = uint.max;	// param n of last setup call after setupAlignDimData has run
     bool useSpacing;	// add inter-row/col spacing?
     
     /* All widgets in the grid, by row. Order:  [ 0 1 ]
      *                                          [ 2 3 ] */
-    //IChildWidget[] subWidgets;
+    //IChildWidget[] subWidgets; (inherited from AParentWidget)
     
-    AlignColumns col, row;
+    AlignColumns col, row;	// aligners for cols and rows
+				// "rows" allocated in col and row; return value of *.addRows():
+    size_t colR = size_t.max, rowR = size_t.max;
 }
 
 
@@ -438,10 +445,10 @@
     /** Instance returned will be shared with any other widgets of same widgetID.
      *
      * Also ensures each widget sharing an instance expects the same number of columns. */
-    static AlignColumns getInstance (widgetID id, myIt columns) {
+    static AlignColumns getInstance (widgetID id, size_t columns) {
         AlignColumns* p = id in instances;
         if (p) {
-            if (p.minWidth.length != columns)
+            if (p.cols != columns)
                 throw new GuiException ("AlignColumns: no. of columns varies between sharing widgets (code error)");
             //logger.trace ("Shared alignment for: "~id);
             return *p;
@@ -457,11 +464,12 @@
      * 
      * After creation, minimal widths should be set for all columns (minWidth) and
      * setWidths must be called before other functions are used. */
-    this (myIt columns) {
+    this (size_t columns) {
 	if (columns < 1)
 	    throw new GuiException("AlignColumns: created with <1 column (code error)");
 	minWidth.length = columns;
 	sizable.length = columns;
+        cols = columns;
     }
     
     /** Like IChildWidget's setup; calls sADD delegates. */
@@ -469,7 +477,7 @@
 	if (n != setup_n) {
 	    setup_n = n;
 	    setupWidths = false;
-	    reset (minWidth.length);
+	    reset (cols);
 	    
 	    foreach (dg; sADD)
 		dg (n, flags);	// set flag 1
@@ -479,13 +487,24 @@
     /** Reset all column information (only keep set callbacks).
      *
      * Widths should be set after calling, as on creation. */
-    void reset (myIt columns) {
+    void reset (size_t columns) {
         minWidth[] = 0;
         sizable[] = false;
         firstSizable = -1;
         lastSizable = -1;
     }
     
+    /** Add num "rows" to the aligner. They start at the returned index r, thus the values in
+     * minCellWidths to set are minCellWidths[cols*r..cols*(r+num)].
+     * 
+     * Calling this function is necessary to allocate room in minCellWidths. */
+    size_t addRows (size_t num) {
+        size_t r = rows;
+        rows += num;
+        minCellWidths.length = cols*rows;
+        return r;
+    }
+    
     /** Initialize widths, either from minWidths or from supplied list, checking validity.
      *
      * Also calculates first/lastSizable from sizable, overall minimal width and column positions.
@@ -493,9 +512,25 @@
     void setWidths (wdim[] data = null) {
 	if (!setupWidths) {
 	    setupWidths = true;
+            
+            // Set minWidth
+            assert (minCellWidths.length == rows * cols, "minCellWidths: bad length");
+            for (size_t c = 0; c < cols; ++c)
+                for (size_t r = 0; r < rows; ++r) {
+                    wdim mcw = minCellWidths[c+r*cols];
+                    if (minWidth[c] < mcw)
+                        minWidth[c] = mcw;
+                }
+            
+            /* Calculate the minimal width of all columns plus spacing. */
+            mw = spacing * cast(wdim)(cols - 1);
+            foreach (imw; minWidth)
+                mw += imw;
+            
+            // set width
             if (data || width) {	// use existing/external data: need to check validity
                 if (data) {
-                    assert (data.length == minWidth.length, "setWidths called with bad data length (code error)");
+                    assert (data.length == cols, "setWidths called with bad data length (code error)");
                     width = data.dup;	// data is shared by other widgets with same id so must be .dup'ed
                 }
                 foreach (i, m; minWidth) {
@@ -505,11 +540,6 @@
             } else
                 width = minWidth.dup;
             
-            /* Calculate the minimal width of all columns plus spacing. */
-            mw = spacing * cast(wdim)(minWidth.length - 1);
-            foreach (imw; minWidth)
-                mw += imw;
-            
             genPositions;
             
             foreach (i,s; sizable) {
@@ -531,7 +561,7 @@
     
     /** Add a callback to be called to notify changes in a column's width, and the sADD callback.
      */
-    typeof(this) addCallbacks (void delegate (myIt,wdim,int) setCW, void delegate (uint,uint) sDg) {
+    typeof(this) addCallbacks (void delegate (size_t,wdim,int) setCW, void delegate (uint,uint) sDg) {
 	assert (setCW && sDg, "AlignColumns.addCallbacks: null callback (code error)");
         setWidthCb ~= setCW;
 	sADD ~= sDg;
@@ -542,15 +572,15 @@
      *
      * returns:
      * -i if in space to left of col i, or i if on col i. */
-    myDiff getCell (wdim l) {
+    ptrdiff_t getCell (wdim l) {
         debug assert (width, "AlignColumns not initialized when getCell called (code error)");
-        myDiff i = minWidth.length - 1;     // starting from right...
+        ptrdiff_t i = cols - 1;     // starting from right...
         while (l < pos[i]) {                // decrement while left of this column
             debug assert (i > 0, "getCell: l < pos[0] (code error)");
             --i;
         }                                   // now (l >= pos[i])
         if (l >= pos[i] + width[i]) {       // between columns
-            debug assert (i+1 < minWidth.length, "getCell: l >= total width (code error)");
+            debug assert (i+1 < cols, "getCell: l >= total width (code error)");
             return -i - 1;                  // note: i might be 0 so cannot just return -i
         }
         return i;
@@ -569,13 +599,13 @@
         
         wdim diff = nw - w;
         if (firstSizable == -1)
-            diff = adjustCellSizes (diff, minWidth.length-1, -1);
+            diff = adjustCellSizes (diff, cols-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);
+            logger.trace ("resizeWidth on {} to {} failed, new width: {}, diff {}, firstSizable {}, columns {}",cast(void*)this, nw,w, diff, firstSizable, cols);
             /+ Also print column widths & positions:
             logger.trace ("resizeWidth to {} failed! Column dimensions and positions:",nw);
             foreach (i,w; width)
@@ -590,12 +620,12 @@
     bool findResizeCols (wdim l) {
         resizeU = -getCell (l);             // potential start for upward-resizes
         if (resizeU <= 0)
-            return true;        // not on a space between cells
+            return true;		// not on a space between cells
         resizeD = resizeU - 1;              // potential start for downward-resizes
         
         while (!sizable[resizeU]) {         // find first actually resizable column (upwards)
             ++resizeU;
-            if (resizeU >= minWidth.length) {       // cannot resize
+            if (resizeU >= cols) {	// cannot resize
                 resizeU = -1;
                 return true;
             }
@@ -604,7 +634,7 @@
         while (!sizable[resizeD]) {         // find first actually resizable column (downwards)
             --resizeD;
             if (resizeD < 0) {              // cannot resize
-                resizeU = -1;       // resizeU is tested to check whether resizes are possible
+                resizeU = -1;		// resizeU is tested to check whether resizes are possible
                 return true;
             }
         }
@@ -631,27 +661,44 @@
      *
      * 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) {
+    bool newMinWidth (size_t col, size_t row, wdim nmw) {
+        minCellWidths[col + row*cols] = nmw;
+        wdim nd = 0;	// negative diff to keep overall size constant if possible
+        if (minWidth[col] < nmw) {	// increase minimal
+            minWidth[col] = nmw;
+            nd = width[col] - nmw;	// negative diff
+        } else if (minWidth[col] > nmw) {	// potentially decrease minimal
+            nmw = 0;
+            for (size_t r = 0; r < rows; ++r) {
+                wdim mcw = minCellWidths[col+r*cols];
+                if (nmw < mcw)
+                    nmw = mcw;
+            }
             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;
+            if (!sizable[col])
+                nd = width[col] - nmw;
+        } else
+            return false;
+        
+        mw = spacing * cast(wdim)(cols - 1);
+        foreach (imw; minWidth)
+            mw += imw;
+        
+        if (nd != 0) {	// needs enlarging
+            width[col] = nmw;
+            foreach (dg; setWidthCb)
+                dg(col, nmw, -1);
+            if (lastSizable >= 0)
+            	adjustCellSizes (nd, lastSizable, -1);	// doesn't necessarily resize exactly
+            genPositions;
         }
-        return r;
+
+        return true;
     }
     
     /* Generate position infomation for each column and set w. */
     private void genPositions () {
-        pos.length = minWidth.length;
+        pos.length = cols;
         
         w = 0;
         foreach (i, cw; width) {
@@ -665,24 +712,26 @@
     *
     * Params:
     *  diff = amount to increase/decrease the total size
-    *  start= index for col/row to start resizing on
+    *  start= index for col/row to start resizing on; assumed to be sizable
     *  incr = direction to resize in (added to index each step). Must be either -1 or +1.
     *
     * Returns:
     *  The amount adjusted. This may be larger than diff, since cellD is clamped by cellDMin.
     *
+    * Doesn't touch non-sizable columns (except start which is only assumed sizable).
+    *
     * Note: Check variable used for start is valid before calling! If a non-sizable column's
     *  index is passed, this should get increased (if diff > 0) but not decreased.
     */
-    private wdim adjustCellSizes (wdim diff, myDiff start, int incr)
+    private wdim adjustCellSizes (wdim diff, ptrdiff_t start, int incr)
     in {
-        assert (width.length == minWidth.length, "CellAlign.adjustCellSizes: width is null (code error)");
+        assert (width.length == cols, "CellAlign.adjustCellSizes: width is invalid (code error)");
         // Most likely if passed negative when sizing is disabled:
-        assert (start >= 0 && start < minWidth.length, "adjustCellSizes: invalid start");
+        assert (start >= 0 && start < cols, "adjustCellSizes: invalid start");
         debug assert (incr == 1 || incr == -1, "adjustCellSizes: invalid incr");
     } body {
         debug scope(failure) logger.trace ("adjustCellSizes: failure");
-        myDiff i = start;
+        ptrdiff_t i = start;
         if (diff > 0) {             // increase size of first resizable cell
             width[i] += diff;
             foreach (dg; setWidthCb)
@@ -708,7 +757,7 @@
                 
                 do {
                     i += incr;
-                    if (i < 0 || i >= minWidth.length) {    // run out of next cells
+                    if (i < 0 || i >= cols) {	// run out of next cells
                         diff -= rd; // still had rd left to decrease
                         break aCSwhile;     // exception: Array index out of bounds
                     }
@@ -720,12 +769,14 @@
         return diff;
     }
     
-    /** Minimal width for each column.
+    
+    /** Minimal widths per cell.
      *
-     * Initialized to zero. Each class using this AlignColumns should, for each column, increase
-     * this value to the maximum of the minimal widths (in other words, set
-     * minWidth[i] = max(minWidth[i], cell.minWidth) for each cell in column i). */
-    wdim[]  minWidth;           // minimal widths (set by genCachedConstructionData)
+     * Array of all cells, organised like GridLayoutWidget.subWidgets when representing columns,
+     * with rows and columns swapped when representing rows.
+     * 
+     * Then minWidth[i] = min(minCellWidths[i]) (where min acts on an array). */
+    wdim[] minCellWidths;
     
     /** For each column i, sizable[i] is true if that column is resizable.
      * 
@@ -740,12 +791,19 @@
     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)
+    /* Minimal width for each column.
+     *
+     * Set by setWidths. */
+    wdim[]  minWidth;
+    size_t  cols, rows;		// number of columns and rows (wrong way round when AlignColumns
+				// represents rows)
+    
+    ptrdiff_t  resizeD,            // resizeCols works down from this index (<0 if not resizing)
             resizeU;            // and up from this index
     /* indicies of the first/last resizable column (negative if none are resizable). */
-    myDiff  firstSizable = -1, lastSizable = -1;  // set by calcFLSbl
+    ptrdiff_t  firstSizable = -1, lastSizable = -1;  // set by calcFLSbl
     // Callbacks used to actually adjust a column's width:
-    void delegate (myIt,wdim,int) setWidthCb[]; // set width of a column, with resize direction
+    void delegate (size_t,wdim,int) setWidthCb[]; // set width of a column, with resize direction
     void delegate (uint,uint) sADD[];	// setupAlignDimData dlgs
     
     uint setup_n = uint.max;	// param n of last setup call
@@ -759,18 +817,18 @@
     debug invariant()
     {
         if (setupWidths) {
-            assert (width.length == minWidth.length);
+            assert (width.length == cols, "invariant: bad width length");
             wdim x = 0;
             foreach (i,w; width) {
-                assert (minWidth[i] <= w);	// even when "not sizable", cols may get enlarged
-                assert (x == pos[i]);
+                assert (minWidth[i] <= w, "invariant: min size not reached");	// even when "not sizable", cols may get enlarged
+                assert (x == pos[i], "invariant: position wrong");
                 x += w + spacing;
             }
-            assert (x - spacing == w);
-            x = spacing * cast(wdim)(minWidth.length - 1);
+            assert (x - spacing == w, "invariant: w is wrong");
+            x = spacing * cast(wdim)(cols - 1);
             foreach (mw; minWidth)
                 x += mw;
-            assert (x == mw);
+            assert (x == mw, "invariant: mw is wrong");
         }
     }
     
@@ -815,9 +873,3 @@
         logger.info ("Unittest complete.");
     }
 }
-
-// Index types. Note that in some cases they need to hold negative values.
-// int is used for resizing direction (although ptrdiff_t would be more appropriate),
-// since the value must always be -1 or +1.
-alias size_t myIt;
-alias ptrdiff_t myDiff;
--- a/mde/input/Input.d	Sun Jan 04 17:35:15 2009 +0000
+++ b/mde/input/Input.d	Mon Jan 05 12:43:27 2009 +0000
@@ -217,12 +217,20 @@
     bool opCall (ref SDL_Event event) {
         /* Non-config events.
         *
-        * Mouse events don't need config for the GUI. Handle them first so that if no config exists
-        * some functionality at least is retained.
+        * Handle these first so that if no config exists some functionality at least is retained.
         *
         * Coordinates don't need adjusting (they put the top-left most pixel at 0,0).
         */
         switch (event.type) {
+            case SDL_KEYDOWN:
+                if (letterCallback) {
+                    try
+                        letterCallback (event.key.keysym.sym, Utf.toString ([cast(wchar)event.key.keysym.unicode], cast(char[])utfBuf));
+                    catch (Exception e)
+                        logger.error (CB_EXC ~ e.msg);
+                }
+                break;
+            
             case SDL_MOUSEBUTTONDOWN:
             case SDL_MOUSEBUTTONUP:
                 foreach (dg; mouseClickCallbacks) {
@@ -254,8 +262,6 @@
         switch (event.type) {
             // Keyboard events:
             case SDL_KEYDOWN:
-                if (letterCallback)
-		    letterCallback (event.key.keysym.sym, Utf.toString ([cast(wchar)event.key.keysym.unicode], cast(char[])utfBuf));
             case SDL_KEYUP:
 		if (letterCallback) break;	// text input mode; no keyboard input from mappings
                 outQueue[]* p = (Config.B.SDLKEY | event.key.keysym.sym) in config.button;