Changeset 99:5de5810e3516

Show
Ignore:
Timestamp:
11/14/08 07:44:32 (2 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

Implemented an editable TextContent? widget; it's now possible to edit text options using the GUI.

The widget supports moving the text entry-point using arrows and home/end, but there's no visual indicator or edit-point setting using the mouse.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • data/conf/gui.mtt

    r98 r99  
    1515<WidgetData|optVal={0:[0x6030]}> 
    1616<WidgetData|optSep={0:[0x21, 0xff],1:["="]}> 
    17 <WidgetData|floating={0:[0x8200,1,6,14],1:["text","button","blank"]}> 
    18 <WidgetData|text={0:[0x21,0xE0E000],1:["Floating text"]}> 
     17<WidgetData|floating={0:[0x8200,13,6,14],1:["text","button","blank"]}> 
     18<WidgetData|text={0:[0x4032]}> 
    1919<WDims|content=[736,221,272,79]> 
    2020<WDims|root=[6,736,50,6,580,6]> 
  • mde/gui/WidgetManager.d

    r97 r99  
    103103        IChildWidget widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); 
    104104        //debug logger.trace ("Click on {}", widg); 
    105         if (widg !is null) 
    106             widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state); 
     105    if (keyFocus && keyFocus !is widg) { 
     106        keyFocus.keyFocusLost; 
     107        keyFocus = null; 
     108        imde.input.setLetterCallback (null); 
     109    } 
     110        if (widg !is null) { 
     111        if (widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { 
     112        keyFocus = widg; 
     113        imde.input.setLetterCallback (&widg.keyEvent); 
     114        } 
     115    } 
    107116    } 
    108117     
     
    183192    } 
    184193     
     194    void preSave () { 
     195    if (keyFocus) { 
     196        keyFocus.keyFocusLost; 
     197        keyFocus = null; 
     198        imde.input.setLetterCallback (null); 
     199    } 
     200    } 
     201     
    185202private: 
     203    IRenderer rend; 
     204    wdim w,h;       // area available to the widgets 
     205    wdim mw,mh;     // minimal area available to the widgets 
    186206    // callbacks indexed by their frame pointers: 
    187207    bool delegate(wdabs cx, wdabs cy, ubyte b, bool state) [void*] clickCallbacks; 
    188208    void delegate(wdabs cx, wdabs cy) [void*] motionCallbacks; 
    189     IRenderer rend; 
    190     wdim w,h;       // area available to the widgets 
    191     wdim mw,mh;     // minimal area available to the widgets 
     209    IChildWidget keyFocus;  // widget receiving keyboard input when non-null 
    192210} 
    193211 
     
    287305    */ 
    288306    void loadDesign (char[] name = null) { 
    289         if (changes !is null) 
     307        if (changes !is null)  // A design was previously loaded 
    290308            save;       // own lock 
    291309             
    292             mutex.lock; 
     310   mutex.lock; 
    293311        scope(exit) mutex.unlock; 
    294312         
     
    316334         
    317335        mt.IDataSection* q = name in changesDS.sec; 
    318         if (q && ((changes = cast(WidgetDataChanges) *q) !is null)) {} 
    319         else { 
     336        if (!q || ((changes = cast(WidgetDataChanges) *q) is null)) { 
    320337            changes = new WidgetDataChanges (curData); 
    321338            changesDS.sec[name] = changes; 
     
    330347    * Is run when the manager is destroyed, but could be run at other times too. */ 
    331348    void save () { 
     349    preSave; 
     350     
    332351        mutex.lock; 
    333352        scope(exit) mutex.unlock; 
     
    455474    void createRootWidget(); 
    456475     
    457     protected: 
    458         final char[] fileName; 
    459         char[] defaultDesign;       // The design specified in the file header. 
    460         char[] rendName;    // Name of renderer; for saving and creating renderers 
    461          
    462         // Loaded data, indexed by design name. May not be loaded for all gui designs: 
    463         scope WidgetDataSet[char[]] data; 
    464         private bool allLoaded = false;  // applies to data 
    465         WidgetDataSet curData;      // Current data 
    466         WidgetDataChanges changes;  // Changes for the current design. 
    467         scope mt.DataSet changesDS; // changes and sections from user file (used for saving) 
    468         bool loadUserFile = true;   // still need to load user file for saving? 
    469          
    470         scope IChildWidget child;   // The primary widget. 
    471          
    472         Mutex mutex;    // lock on methods for use outside the package. 
     476    /** Called before saving (usually when the GUI is about to be destroyed, although not 
     477     *  necessarily). */ 
     478    void preSave () {} 
     479     
     480protected: 
     481    final char[] fileName; 
     482    char[] defaultDesign;       // The design specified in the file header. 
     483    char[] rendName;            // Name of renderer; for saving and creating renderers 
     484     
     485    // Loaded data, indexed by design name. May not be loaded for all gui designs: 
     486    scope WidgetDataSet[char[]] data; 
     487    private bool allLoaded = false; // applies to data 
     488    WidgetDataSet curData;      // Current data 
     489    WidgetDataChanges changes;      // Changes for the current design. 
     490    scope mt.DataSet changesDS;     // changes and sections from user file (used for saving) 
     491    bool loadUserFile = true;       // still need to load user file for saving? 
     492     
     493    scope IChildWidget child;       // The primary widget. 
     494     
     495    Mutex mutex;            // lock on methods for use outside the package. 
    473496} 
  • mde/gui/content/Content.d

    r98 r99  
    2121import Int = tango.text.convert.Integer; 
    2222import Float = tango.text.convert.Float; 
     23import derelict.sdl.keysym; 
    2324 
    2425debug { 
     
    152153        symb = symbol; 
    153154        v = val; 
     155    pos = v.length; 
    154156    } 
    155157     
     
    178180    alias opCall opCast; 
    179181     
     182    /** Acts on a keystroke and returns the new value. 
     183     * 
     184     * Supports one-line editing: left/right, home/end, backspace/delete. */ 
     185    char[] keyStroke (ushort sym, char[] i) { 
     186    debug assert (i.length, "TextContent.keyStroke: no value (??)");    // impossible? 
     187    char k = *i; 
     188    if (k > 0x20) { 
     189        if (k == 0x7f) {        // delete 
     190        size_t p = pos; 
     191        if (p < v.length) ++p; 
     192        while (p < v.length && (v[p] & 0x80) && !(v[p] & 0x40)) 
     193            ++p; 
     194        v = v[0..pos] ~ v[p..$]; 
     195        } else {            // insert character 
     196        char[] tail = v[pos..$]; 
     197        v.length = v.length + i.length; 
     198        size_t npos = pos+i.length; 
     199        if (tail) v[npos..$] = tail.dup;    // cannot assign with overlapping ranges 
     200            v[pos..npos] = i; 
     201        pos = npos; 
     202        } 
     203    } else {            // use sym; many keys output 0 
     204        if (sym == SDLK_BACKSPACE) {    // backspace; k == 0x8 
     205        char[] tail = v[pos..$]; 
     206        if (pos) --pos; 
     207        while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40)) 
     208            --pos; 
     209        v = v[0..pos] ~ tail; 
     210        } else if (sym == SDLK_LEFT) { 
     211        if (pos) --pos; 
     212        while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40)) 
     213            --pos; 
     214        } else if (sym == SDLK_RIGHT) { 
     215        if (pos < v.length) ++pos; 
     216        while (pos < v.length && (v[pos] & 0x80) && !(v[pos] & 0x40)) 
     217            ++pos; 
     218        } else if (sym == SDLK_HOME || sym == SDLK_UP) { 
     219        pos = 0; 
     220        } else if (sym == SDLK_END || sym == SDLK_DOWN) { 
     221        pos = v.length; 
     222        } else 
     223        debug logger.trace ("Symbol: {}", sym); 
     224    } 
     225    return v; 
     226    } 
     227     
     228    /// Gives all callbacks the modified value 
     229    void endEdit () { 
     230    foreach (cb; cngCb) 
     231        cb(symb, v); 
     232    } 
     233     
    180234    char[] v; 
    181235protected: 
    182236    char[] symb; 
     237    size_t pos;     // editing position; used by keyStroke 
    183238    void delegate (char[],char[])[] cngCb; 
    184239} 
  • mde/gui/renderer/IRenderer.d

    r97 r99  
    8888     * interfaces when available to allow flexibility. */ 
    8989    struct TextAdapter { 
    90         void set (char[] c, int col) { 
     90        void setText (char[] c) { 
    9191            content = c; 
    92             colour = Colour (col); 
     92        textCache.cacheVer = -1;    // force update 
    9393        } 
     94     
     95    void setColour (int col = 0xFFFFFF) { 
     96        colour = Colour (col); 
     97    } 
    9498         
    9599        void getDimensions (out wdsize w, out wdsize h) { 
  • mde/gui/renderer/SimpleRenderer.d

    r97 r99  
    134134        TextAdapter a; 
    135135        a.font = defaultFont; 
    136         a.set (text, col); 
     136        a.content = text; 
     137    a.colour = Colour (col); 
    137138        return a; 
    138139    } 
  • mde/gui/widget/Floating.d

    r97 r99  
    150150    } 
    151151     
    152     void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 
    153         if (event > subWidgets.length) return
     152    int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 
     153        if (event > subWidgets.length) return 0
    154154        if (b == 1 && state == true) { 
    155155            active = event; 
     
    181181            } 
    182182        } 
     183    return 0; 
    183184    } 
    184185     
  • mde/gui/widget/Ifaces.d

    r96 r99  
    103103     * gui. */ 
    104104    void addMotionCallback (void delegate (wdabs cx, wdabs cy) dg); 
    105      
    106     // FIXME: keyboard callback (letter only, for text input? Also used for setting keybindings though...) 
    107105     
    108106    /** Remove all event callbacks on this widget (according to the delegate's .ptr). */ 
     
    237235    IChildWidget getWidget (wdim x, wdim y); 
    238236     
    239     /** Receive a mouse click event. 
    240      * 
    241      * See mde.input.input.Input.MouseClickCallback for parameters. However, cx and cy are adjusted 
    242      * to the Widget's local coordinates. 
    243      * 
    244      * Widget may assume coordinates are on the widget (caller must check). */ 
    245     void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state); 
     237    /** Receive a mouse click event at cx,cy from button b (1-5 correspond to L,M,B, wheel up,down) 
     238     * which is a down-click if state is true. 
     239     * 
     240     * Widget may assume coordinates are on the widget (caller must check). 
     241     * 
     242     * The return value has the following flags: 1 to request keyboard input. */ 
     243    int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state); 
     244     
     245    /** Receives keyboard events when requested. 
     246     * 
     247     * Params: 
     248     *  sym SDLKey key sym, useful for keys with no character code such as arrow keys 
     249     *  letter  The character input, in UTF-8 */ 
     250    void keyEvent (ushort sym, char[] letter); 
     251     
     252    /** Called when keyboard input focus is lost. */ 
     253    void keyFocusLost (); 
    246254//END Events 
    247255     
  • mde/gui/widget/TextWidget.d

    r96 r99  
    7777{ 
    7878    this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { 
    79         debug assert (c, "content is null (code error)"); 
    8079        WDCheck (data, 3, 0); 
    8180        content = c; 
  • mde/gui/widget/Widget.d

    r96 r99  
    124124     
    125125    /* Dummy event method (suitable for all widgets which don't respond to events). */ 
    126     void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) {} 
     126    int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 
     127    return 0; 
     128    } 
     129     
     130    /* Dummy functions: suitable for widgets with no text input. */ 
     131    void keyEvent (ushort, char[]) {} 
     132    void keyFocusLost () {} 
    127133//END Events 
    128134     
     
    222228     
    223229    /// Handles the down-click 
    224     void clickEvent (wdabs, wdabs, ubyte b, bool state) { 
     230    int clickEvent (wdabs, wdabs, ubyte b, bool state) { 
    225231        if (b == 1 && state == true) { 
    226232            pushed = true; 
     
    229235            mgr.addMotionCallback (&motionWhilePushed); 
    230236        } 
     237    return 0; 
    231238    } 
    232239     
  • mde/gui/widget/createWidget.d

    r98 r99  
    2929import mde.gui.widget.TextWidget; 
    3030import mde.gui.widget.miscContent; 
     31import mde.gui.widget.textContent; 
    3132import mde.gui.widget.Floating; 
    3233import tango.util.log.Log : Log, Logger; 
     
    8384/// Widget types. 
    8485enum WIDGET_TYPE : int { 
    85     FUNCTION                = 0x2000,   // Function called instead of widget created (no "Widget" appended to fct name) 
    86     TAKES_CONTENT           = 0x4000,   // Flag indicates widget's this should be passed an IContent reference. 
    87     PARENT                  = 0x8000,   // widget can have children; not used by code (except in data files) 
     86    FUNCTION       = 0x2000,   // Function called instead of widget created (no "Widget" appended to fct name) 
     87    TAKES_CONTENT  = 0x4000,   // Flag indicates widget's this should be passed an IContent reference. 
     88    PARENT     = 0x8000,   // widget can have children; not used by code (except in data files) 
    8889     
    8990    // Use widget names rather than usual capitals convention 
    90     Unnamed                 = 0x0,      // Only for use by widgets not created with createWidget 
     91    Unnamed        = 0x0,      // Only for use by widgets not created with createWidget 
    9192     
    9293    // blank: 0x1 
    93     FixedBlank              = 0x1, 
    94     SizableBlank            = 0x2, 
    95     Debug                   = 0xF, 
     94    FixedBlank     = 0x1, 
     95    SizableBlank   = 0x2, 
     96    Debug      = 0xF, 
    9697     
    9798    // buttons: 0x10 
    98     Button                  = 0x10, 
     99    Button     = 0x10, 
    99100     
    100101    // labels: 0x20 
    101     ContentLabel        = TAKES_CONTENT | 0x20, 
    102     TextLabel               = 0x21, 
     102    ContentLabel    = TAKES_CONTENT | 0x20, 
     103    TextLabel      = 0x21, 
    103104     
    104105    // content editables: 0x30 
    105     editContent             = FUNCTION | TAKES_CONTENT | 0x30, 
    106     DisplayContent          = TAKES_CONTENT | 0x30, 
    107     BoolContent             = TAKES_CONTENT | 0x31, 
     106    editContent     = FUNCTION | TAKES_CONTENT | 0x30, 
     107    DisplayContent  = TAKES_CONTENT | 0x30, 
     108    BoolContent     = TAKES_CONTENT | 0x31, 
     109    TextContent     = TAKES_CONTENT | 0x32, 
    108110     
    109     GridLayout              = TAKES_CONTENT | PARENT | 0x100, 
    110     TrialContentLayout      = PARENT | 0x110, 
     111    GridLayout     = TAKES_CONTENT | PARENT | 0x100, 
     112    TrialContentLayout = PARENT | 0x110, 
    111113     
    112     FloatingArea            = PARENT | 0x200, 
     114    FloatingArea   = PARENT | 0x200, 
    113115} 
    114116 
     
    125127        "DisplayContent", 
    126128        "BoolContent", 
     129    "TextContent", 
    127130        "editContent", 
    128131        "TrialContentLayout", 
  • mde/gui/widget/layout.d

    r95 r99  
    248248     
    249249    // Resizing columns & rows 
    250     void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 
     250    int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { 
    251251        debug scope (failure) 
    252252                logger.warn ("clickEvent: failure"); 
     
    258258            // find col/row's resizeD & resizeU 
    259259            if (col.findResizeCols (cx - x) && row.findResizeCols (cy - y)) 
    260                 return;       // unable to resize 
     260                return 0;     // unable to resize 
    261261             
    262262            dragX = cx; 
     
    266266            mgr.addMotionCallback (&resizeCallback); 
    267267        } 
     268    return 0; 
    268269    } 
    269270     
  • mde/gui/widget/miscContent.d

    r95 r99  
    1717module mde.gui.widget.miscContent; 
    1818 
     19import mde.gui.widget.textContent; 
    1920import mde.gui.widget.Widget; 
    2021import mde.gui.widget.TextWidget; 
     
    2829    if (cast(BoolContent) c) 
    2930        return new BoolContentWidget(mgr,id,data,c); 
     31    else if (cast(TextContent) c) 
     32    return new TextContentWidget(mgr,id,data,c); 
    3033    else        // generic uneditable option 
    3134        return new DisplayContentWidget(mgr,id,data,c); 
  • mde/input/Input.d

    r89 r99  
    2626// sdl imports 
    2727import derelict.sdl.events; 
     28import derelict.sdl.keyboard; 
    2829import derelict.sdl.types;  // only SDL_PRESSED 
    2930import derelict.sdl.joystick;   // SDL_HAT_* 
    3031 
     32import Utf = tango.text.convert.Utf; 
    3133import tango.util.log.Log : Log, Logger; 
    3234 
    33 /** Class encapsulating all input functionality. 
     35/************************************************************************************************** 
     36 * Class encapsulating all input functionality. 
    3437 * 
    35  * The following methods are provided for Gui mouse input: 
     38 * This class has several modes which affect output: interaction mode (default), text input mode, 
     39 * mouse gui mode and axis/button binding modes. 
     40 * 
     41 * TODO: Gui mode and button capture and axis capture modes for key binding, disabling all 
     42 * other modes (except gui-type mouse info?). 
     43 * TODO: Possible revisions: remove by-index lookup, only providing callbacks? 
     44 * TODO: Make callbacks send the time of the event? 
     45 * TODO: Adjusters, e.g. double-press, hold/click differences. Axis output: via short or double? 
     46 * TODO: add an Axis1Callback similar to getAxis1? Or remove getAxis1 and provide a conversion function? 
     47 * TODO: allow callbacks to be removed. Currently not needed. 
     48 * TODO: modifiers in text-input mode: shortcut handling? Global shortcuts - either mode? 
     49 * 
     50 * The primary mode is the interaction mode, mapping each button and axis to a configurable index, 
     51 * and allowing event callback functions to be bound per index as well as allowing the state to be 
     52 * looked up directly. 
     53 * --- 
     54 * // For keyboard, joystick and mouse button input 
     55 * bool getButton (inputID id); 
     56 * void addButtonCallback (inputID id, ButtonCallback dg);  // callback receives both up and down events 
     57 * 
     58 * // For joystick axis input 
     59 * short getAxis (inputID id);      // range: -32767 .. 32767 
     60 * double getAxis1 (inputID id);    // range: -1.0 .. 1.0 
     61 * void addAxisCallback (inputID id, AxisCallback dg); 
     62 * 
     63 * // For mouse (and joystick ball) relative motion input 
     64 * void getRelMotion (inputID id, out double x, out double y); 
     65 * void addRelMotionCallback (inputID id, RelMotionCallback dg); 
     66 * --- 
     67 * 
     68 * The keyboard can be put in text input mode, disabling interaction-mode keyboard access and 
     69 * providing a callback called on each letter press with it's UTF-8 code. Setting a LetterCallback 
     70 * activates text input mode and removing it disables this mode; only one may be active at once. 
     71 * --- 
     72 * void setLetterCallback (LetterCallback dg); 
     73 * --- 
     74 * 
     75 * Mouse input can be recieved via gui-oriented click/coordinate callbacks in both interaction 
     76 * mode and gui mode, however interaction-mode button and relative motion input is not received in 
     77 * gui mode. 
    3678 * --- 
    3779 * void getMouseScreenPos (out uint x, out uint y); 
     
    4082 * --- 
    4183 * 
    42  * The following methods are provided for mouse (and joystick ball) relative motion input: 
    43  * --- 
    44  * void getRelMotion (inputID id, out real x = 0.0, out real y = 0.0); 
    45  * void addRelMotionCallback (inputID id, RelMotionCallback dg); 
    46  * --- 
    47  * 
    48  * The following methods are provided for joystick axis input: 
    49  * --- 
    50  * short getAxis (inputID id); 
    51  * real getAxis1 (inputID id); 
    52  * void addAxisCallback (inputID id, AxisCallback dg); 
    53  * --- 
    54  * 
    55  * The following methods are provided for keyboard, joystick and mouse button input: 
    56  * --- 
    57  * bool getButton (inputID id); 
    58  * void addButtonCallback (inputID id, ButtonCallback dg) 
    59  * --- 
    60  * 
    6184 * The following methods are provided for setup & posting events: 
    6285 * --- 
    63  * bool opCall (ref SDL_Event event); 
    64  * void frameReset (); 
    65  * void loadConfig (char[] profile = "Default"); 
     86 * bool opCall (ref SDL_Event event);   // Handles an event, making all the above work 
     87 * void frameReset ();  // Needs to be called once per frame for correct relative input 
     88 * void loadConfig (char[] profile = "Default");    // Configuration for interaction-mode indexes 
    6689 * --- 
    6790 ***************************************************/ 
    68 // FIXME: remove getMouseScreenPos (no use)? 
    69 // FIXME: add an Axis1Callback similar to getAxis1? Or remove getAxis1 and provide a conversion 
    70 // function? 
    7191class Input 
    7292{ 
     
    7595    alias void delegate(inputID, bool)      ButtonCallback; 
    7696    alias void delegate(inputID, short)     AxisCallback; 
    77     alias void delegate(inputID, real,real) RelMotionCallback; 
    78     alias void delegate(ushort, ushort, ubyte, bool)    MouseClickCallback; 
    79     alias void delegate(ushort, ushort)                 MouseMotionCallback; 
     97    alias void delegate(inputID, double,double) RelMotionCallback; 
     98    alias void delegate(ushort, ushort, ubyte, bool)    MouseClickCallback; 
     99    alias void delegate(ushort, ushort)     MouseMotionCallback; 
     100    alias void delegate(ushort, char[])     LetterCallback; 
    80101 
    81102    /** Get key status at this ID. 
     
    98119    /** Get axis status at this ID. 
    99120    * 
    100     * Returns: value (real; range roughly -1.0 .. 1.0) or 0 if no value at this ID. */ 
    101     real getAxis1 (inputID id) { 
     121    * Returns: value (double; range roughly -1.0 .. 1.0) or 0 if no value at this ID. */ 
     122    double getAxis1 (inputID id) { 
    102123        short* retp = id in axis; 
    103124        if (retp) return (*retp) * 3.0518509475997192e-05; 
    104125        else return 0.0; 
    105126    } 
    106              
     127     
    107128    /** Get the relative motion of the mouse or a joystick ball (since last frameReset() call). 
    108129    * 
    109     * Future: Converts to a real via sensitivity settings (defaults may be set and overriden per item). 
     130    * Future: Converts to a double via sensitivity settings (defaults may be set and overriden per item). 
    110131    * 
    111132    * To avoid confusion over the ID here, the idea is for the input-layer upward to support 
     
    113134    * Also joystick balls (supported by SDL) can be used in the same way as a mouse for relative 
    114135    * positions. */ 
    115     void getRelMotion (inputID id, out real x = 0.0, out real y = 0.0) { 
     136    void getRelMotion (inputID id, out double x = 0.0, out double y = 0.0) { 
    116137        RelPair* rp = id in relMotion; 
    117138        if (rp) { 
     
    119140        } 
    120141    } 
    121     /** Get mouse pointer position in screen coordinates. 
    122     * 
    123     * Window managers only support one mouse, so there will only be one screen coordinate. 
    124     * Unlike nearly everything else, this is not configurable. 
    125     * 
    126     * Also see addMouseMotionCallback. */ 
    127     void getMouseScreenPos (out uint x, out uint y) { 
    128         x = mouse_x;    y = mouse_y; 
    129     } 
    130     // /// Is this modifier on? 
    131     //bool modifierStatus (inputID id); 
    132142 
    133143    /** Adds a callback delegate for key events (both DOWN and UP) with this ID. 
     
    176186    void addMouseMotionCallback (MouseMotionCallback dg) { 
    177187        mouseMotionCallbacks ~= dg; 
     188    } 
     189     
     190    /** Sets a callback delegate to recieve key presses as a Utf-8 char[]. 
     191    * 
     192    * Since it is normal to type into only one location at once, setting a new LetterCallback 
     193    * removes the last set one (however active ButtonCallbacks will still receive events). 
     194    * Supplying a null delegate will turn off the slight overhead of unicode conversion. 
     195    *  
     196    * The char[] received by the delegate must be copied and not stored or edited directly. */ 
     197    void setLetterCallback (LetterCallback dg = null) { 
     198        if (dg) { 
     199        SDL_EnableUNICODE (1); 
     200        SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); 
     201    } else { 
     202        SDL_EnableUNICODE (0); 
     203        SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL); 
     204    } 
     205        letterCallback = dg; 
    178206    } 
    179207 
     
    208236             
    209237            case SDL_MOUSEMOTION: 
    210                 mouse_x = event.motion.x; 
    211                 mouse_y = event.motion.y; 
    212                  
    213238                foreach (dg; mouseMotionCallbacks) { 
    214239                    try 
     
    230255            // Keyboard events: 
    231256            case SDL_KEYDOWN: 
     257                if (letterCallback) 
     258            letterCallback (event.key.keysym.sym, Utf.toString ([cast(wchar)event.key.keysym.unicode], cast(char[])utfBuf)); 
    232259            case SDL_KEYUP: 
     260        if (letterCallback) break;  // text input mode; no keyboard input from mappings 
    233261                outQueue[]* p = (Config.B.SDLKEY | event.key.keysym.sym) in config.button; 
    234262                if (p) foreach (outQueue q; *p) { 
     
    371399     
    372400    struct RelPair {    // for mouse/joystick ball motion 
    373         real x, y; 
    374         static RelPair opCall (real a, real b) { 
     401        double x, y; 
     402        static RelPair opCall (double a, double b) { 
    375403            RelPair ret; 
    376404            ret.x = a;  ret.y = b; 
     
    383411    static Logger logger; 
    384412 
    385     Config config;         // Configuration 
    386      
    387     bool[inputID] button;      // Table of button states 
    388     short[inputID] axis;       // Table of axes states 
    389     ushort mouse_x, mouse_y;       // Current screen coords of the window manager mouse 
    390     RelPair[inputID] relMotion;    // Table of relative mouse / joystick ball motions 
     413    Config config;      // Configuration 
     414    char[6] utfBuf;        // Buffer for Utf.toString; reallocates if less than 5. 
     415     
     416    bool[inputID] button;  // Table of button states 
     417    short[inputID] axis;   // Table of axes states 
     418    RelPair[inputID] relMotion; // Table of relative mouse / joystick ball motions 
    391419     
    392420    // NOTE: currently no means of removal 
     
    396424    MouseClickCallback[]    mouseClickCallbacks; 
    397425    MouseMotionCallback[]   mouseMotionCallbacks; 
    398          
     426    LetterCallback              letterCallback; 
     427     
    399428    //BEGIN Event stream functionality 
    400429    /* This section contains functions called on an event, which may modify the event (adjuster 
     
    579608            counters[3] += 1; 
    580609        }); 
    581         ut.addRelMotionCallback (0x11F0, delegate void(inputID id, real x, real y) { 
     610        ut.addRelMotionCallback (0x11F0, delegate void(inputID id, double x, double y) { 
    582611            assert (x == 14.0); 
    583612            assert (y == -1.0); 
     
    661690        assert (ut.getAxis1(0x22F0) == 0.0); 
    662691             
    663         real s,t; 
     692        double s,t; 
    664693        ut.getRelMotion(0x10F0, s,t); 
    665694        assert (s == 14.0);