# HG changeset patch # User Diggory Hardy # Date 1226666672 0 # Node ID 5de5810e35163d6ab7c50e8717b9fb1c81140567 # Parent 49e7cfed4b34cad9f91a840884f56d6fb86b7967 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. diff -r 49e7cfed4b34 -r 5de5810e3516 data/conf/gui.mtt --- a/data/conf/gui.mtt Wed Nov 12 13:18:51 2008 +0000 +++ b/data/conf/gui.mtt Fri Nov 14 12:44:32 2008 +0000 @@ -14,8 +14,8 @@ - + + diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/WidgetManager.d --- a/mde/gui/WidgetManager.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/WidgetManager.d Fri Nov 14 12:44:32 2008 +0000 @@ -102,8 +102,17 @@ } IChildWidget widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); //debug logger.trace ("Click on {}", widg); - if (widg !is null) - widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state); + if (keyFocus && keyFocus !is widg) { + keyFocus.keyFocusLost; + keyFocus = null; + imde.input.setLetterCallback (null); + } + if (widg !is null) { + if (widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { + keyFocus = widg; + imde.input.setLetterCallback (&widg.keyEvent); + } + } } /** For mouse motion events. @@ -182,13 +191,22 @@ child.setPosition (0,0); } + void preSave () { + if (keyFocus) { + keyFocus.keyFocusLost; + keyFocus = null; + imde.input.setLetterCallback (null); + } + } + private: + IRenderer rend; + wdim w,h; // area available to the widgets + wdim mw,mh; // minimal area available to the widgets // callbacks indexed by their frame pointers: bool delegate(wdabs cx, wdabs cy, ubyte b, bool state) [void*] clickCallbacks; void delegate(wdabs cx, wdabs cy) [void*] motionCallbacks; - IRenderer rend; - wdim w,h; // area available to the widgets - wdim mw,mh; // minimal area available to the widgets + IChildWidget keyFocus; // widget receiving keyboard input when non-null } @@ -286,10 +304,10 @@ * name = Design to load. If null, the default will be loaded. */ void loadDesign (char[] name = null) { - if (changes !is null) + if (changes !is null) // A design was previously loaded save; // own lock - mutex.lock; + mutex.lock; scope(exit) mutex.unlock; // Load data (loadData tests if it's already loaded first): @@ -315,8 +333,7 @@ changesDS = new mt.DataSet; mt.IDataSection* q = name in changesDS.sec; - if (q && ((changes = cast(WidgetDataChanges) *q) !is null)) {} - else { + if (!q || ((changes = cast(WidgetDataChanges) *q) is null)) { changes = new WidgetDataChanges (curData); changesDS.sec[name] = changes; } @@ -329,6 +346,8 @@ * * Is run when the manager is destroyed, but could be run at other times too. */ void save () { + preSave; + mutex.lock; scope(exit) mutex.unlock; @@ -454,20 +473,24 @@ */ void createRootWidget(); - protected: - final char[] fileName; - char[] defaultDesign; // The design specified in the file header. - char[] rendName; // Name of renderer; for saving and creating renderers - - // Loaded data, indexed by design name. May not be loaded for all gui designs: - scope WidgetDataSet[char[]] data; - private bool allLoaded = false; // applies to data - WidgetDataSet curData; // Current data - WidgetDataChanges changes; // Changes for the current design. - scope mt.DataSet changesDS; // changes and sections from user file (used for saving) - bool loadUserFile = true; // still need to load user file for saving? - - scope IChildWidget child; // The primary widget. - - Mutex mutex; // lock on methods for use outside the package. + /** Called before saving (usually when the GUI is about to be destroyed, although not + * necessarily). */ + void preSave () {} + +protected: + final char[] fileName; + char[] defaultDesign; // The design specified in the file header. + char[] rendName; // Name of renderer; for saving and creating renderers + + // Loaded data, indexed by design name. May not be loaded for all gui designs: + scope WidgetDataSet[char[]] data; + private bool allLoaded = false; // applies to data + WidgetDataSet curData; // Current data + WidgetDataChanges changes; // Changes for the current design. + scope mt.DataSet changesDS; // changes and sections from user file (used for saving) + bool loadUserFile = true; // still need to load user file for saving? + + scope IChildWidget child; // The primary widget. + + Mutex mutex; // lock on methods for use outside the package. } diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/content/Content.d --- a/mde/gui/content/Content.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/content/Content.d Fri Nov 14 12:44:32 2008 +0000 @@ -20,6 +20,7 @@ //FIXME: efficient conversions? Need to dup result when formatting a string anyway? import Int = tango.text.convert.Integer; import Float = tango.text.convert.Float; +import derelict.sdl.keysym; debug { import tango.util.log.Log : Log, Logger; @@ -151,6 +152,7 @@ this (char[] symbol, char[] val = null) { symb = symbol; v = val; + pos = v.length; } /** Adds cb to the list of callback functions called when the value is changed. Returns this. */ @@ -177,9 +179,62 @@ } alias opCall opCast; + /** Acts on a keystroke and returns the new value. + * + * Supports one-line editing: left/right, home/end, backspace/delete. */ + char[] keyStroke (ushort sym, char[] i) { + debug assert (i.length, "TextContent.keyStroke: no value (??)"); // impossible? + char k = *i; + if (k > 0x20) { + if (k == 0x7f) { // delete + size_t p = pos; + if (p < v.length) ++p; + while (p < v.length && (v[p] & 0x80) && !(v[p] & 0x40)) + ++p; + v = v[0..pos] ~ v[p..$]; + } else { // insert character + char[] tail = v[pos..$]; + v.length = v.length + i.length; + size_t npos = pos+i.length; + if (tail) v[npos..$] = tail.dup; // cannot assign with overlapping ranges + v[pos..npos] = i; + pos = npos; + } + } else { // use sym; many keys output 0 + if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8 + char[] tail = v[pos..$]; + if (pos) --pos; + while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40)) + --pos; + v = v[0..pos] ~ tail; + } else if (sym == SDLK_LEFT) { + if (pos) --pos; + while (pos && (v[pos] & 0x80) && !(v[pos] & 0x40)) + --pos; + } else if (sym == SDLK_RIGHT) { + if (pos < v.length) ++pos; + while (pos < v.length && (v[pos] & 0x80) && !(v[pos] & 0x40)) + ++pos; + } else if (sym == SDLK_HOME || sym == SDLK_UP) { + pos = 0; + } else if (sym == SDLK_END || sym == SDLK_DOWN) { + pos = v.length; + } else + debug logger.trace ("Symbol: {}", sym); + } + return v; + } + + /// Gives all callbacks the modified value + void endEdit () { + foreach (cb; cngCb) + cb(symb, v); + } + char[] v; protected: char[] symb; + size_t pos; // editing position; used by keyStroke void delegate (char[],char[])[] cngCb; } diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/renderer/IRenderer.d --- a/mde/gui/renderer/IRenderer.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/renderer/IRenderer.d Fri Nov 14 12:44:32 2008 +0000 @@ -87,10 +87,14 @@ * NOTE: currently inflexible. Could use (function) pointers, class interfaces or struct * interfaces when available to allow flexibility. */ struct TextAdapter { - void set (char[] c, int col) { + void setText (char[] c) { content = c; - colour = Colour (col); + textCache.cacheVer = -1; // force update } + + void setColour (int col = 0xFFFFFF) { + colour = Colour (col); + } void getDimensions (out wdsize w, out wdsize h) { font.updateBlock (content, textCache); diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/renderer/SimpleRenderer.d --- a/mde/gui/renderer/SimpleRenderer.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/renderer/SimpleRenderer.d Fri Nov 14 12:44:32 2008 +0000 @@ -133,7 +133,8 @@ TextAdapter getAdapter (char[] text, int col) { TextAdapter a; a.font = defaultFont; - a.set (text, col); + a.content = text; + a.colour = Colour (col); return a; } diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/Floating.d --- a/mde/gui/widget/Floating.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/Floating.d Fri Nov 14 12:44:32 2008 +0000 @@ -149,8 +149,8 @@ return this; // no match } - void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { - if (event > subWidgets.length) return; + int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { + if (event > subWidgets.length) return 0; if (b == 1 && state == true) { active = event; with (sWData[active]) { @@ -180,6 +180,7 @@ } } } + return 0; } protected: diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/Ifaces.d --- a/mde/gui/widget/Ifaces.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/Ifaces.d Fri Nov 14 12:44:32 2008 +0000 @@ -103,8 +103,6 @@ * gui. */ void addMotionCallback (void delegate (wdabs cx, wdabs cy) dg); - // FIXME: keyboard callback (letter only, for text input? Also used for setting keybindings though...) - /** Remove all event callbacks on this widget (according to the delegate's .ptr). */ // Note: don't try to pass a reference and cast to void* in the function; it's a different address. void removeCallbacks (void* frame); @@ -236,13 +234,23 @@ * Note: use global coordinates (x,y) not coordinates relative to the widget. */ IChildWidget getWidget (wdim x, wdim y); - /** Receive a mouse click event. + /** Receive a mouse click event at cx,cy from button b (1-5 correspond to L,M,B, wheel up,down) + * which is a down-click if state is true. + * + * Widget may assume coordinates are on the widget (caller must check). * - * See mde.input.input.Input.MouseClickCallback for parameters. However, cx and cy are adjusted - * to the Widget's local coordinates. + * The return value has the following flags: 1 to request keyboard input. */ + int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state); + + /** Receives keyboard events when requested. * - * Widget may assume coordinates are on the widget (caller must check). */ - void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state); + * Params: + * sym SDLKey key sym, useful for keys with no character code such as arrow keys + * letter The character input, in UTF-8 */ + void keyEvent (ushort sym, char[] letter); + + /** Called when keyboard input focus is lost. */ + void keyFocusLost (); //END Events /** Draw, using the stored values of x and y. diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/TextWidget.d --- a/mde/gui/widget/TextWidget.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/TextWidget.d Fri Nov 14 12:44:32 2008 +0000 @@ -76,7 +76,6 @@ class ContentLabelWidget : ATextWidget { this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { - debug assert (c, "content is null (code error)"); WDCheck (data, 3, 0); content = c; if (!content) throw new ContentException (); diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/Widget.d --- a/mde/gui/widget/Widget.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/Widget.d Fri Nov 14 12:44:32 2008 +0000 @@ -123,7 +123,13 @@ } /* Dummy event method (suitable for all widgets which don't respond to events). */ - void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) {} + int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { + return 0; + } + + /* Dummy functions: suitable for widgets with no text input. */ + void keyEvent (ushort, char[]) {} + void keyFocusLost () {} //END Events /* Basic draw method: draw the background (all widgets should do this). */ @@ -221,13 +227,14 @@ } /// Handles the down-click - void clickEvent (wdabs, wdabs, ubyte b, bool state) { + int clickEvent (wdabs, wdabs, ubyte b, bool state) { if (b == 1 && state == true) { pushed = true; mgr.requestRedraw; mgr.addClickCallback (&clickWhilePushed); mgr.addMotionCallback (&motionWhilePushed); } + return 0; } /// Called when a mouse click event occurs while held; handles up-click diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/createWidget.d --- a/mde/gui/widget/createWidget.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/createWidget.d Fri Nov 14 12:44:32 2008 +0000 @@ -28,6 +28,7 @@ import mde.gui.widget.miscWidgets; import mde.gui.widget.TextWidget; import mde.gui.widget.miscContent; +import mde.gui.widget.textContent; import mde.gui.widget.Floating; import tango.util.log.Log : Log, Logger; @@ -82,34 +83,35 @@ private: /// Widget types. enum WIDGET_TYPE : int { - FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) - TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. - PARENT = 0x8000, // widget can have children; not used by code (except in data files) + FUNCTION = 0x2000, // Function called instead of widget created (no "Widget" appended to fct name) + TAKES_CONTENT = 0x4000, // Flag indicates widget's this should be passed an IContent reference. + PARENT = 0x8000, // widget can have children; not used by code (except in data files) // Use widget names rather than usual capitals convention - Unnamed = 0x0, // Only for use by widgets not created with createWidget + Unnamed = 0x0, // Only for use by widgets not created with createWidget // blank: 0x1 - FixedBlank = 0x1, - SizableBlank = 0x2, - Debug = 0xF, + FixedBlank = 0x1, + SizableBlank = 0x2, + Debug = 0xF, // buttons: 0x10 - Button = 0x10, + Button = 0x10, // labels: 0x20 - ContentLabel = TAKES_CONTENT | 0x20, - TextLabel = 0x21, + ContentLabel = TAKES_CONTENT | 0x20, + TextLabel = 0x21, // content editables: 0x30 - editContent = FUNCTION | TAKES_CONTENT | 0x30, - DisplayContent = TAKES_CONTENT | 0x30, - BoolContent = TAKES_CONTENT | 0x31, + editContent = FUNCTION | TAKES_CONTENT | 0x30, + DisplayContent = TAKES_CONTENT | 0x30, + BoolContent = TAKES_CONTENT | 0x31, + TextContent = TAKES_CONTENT | 0x32, - GridLayout = TAKES_CONTENT | PARENT | 0x100, - TrialContentLayout = PARENT | 0x110, + GridLayout = TAKES_CONTENT | PARENT | 0x100, + TrialContentLayout = PARENT | 0x110, - FloatingArea = PARENT | 0x200, + FloatingArea = PARENT | 0x200, } //const char[][int] WIDGET_NAMES; @@ -124,6 +126,7 @@ "ContentLabel", "DisplayContent", "BoolContent", + "TextContent", "editContent", "TrialContentLayout", "FloatingArea", diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/layout.d --- a/mde/gui/widget/layout.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/layout.d Fri Nov 14 12:44:32 2008 +0000 @@ -247,7 +247,7 @@ } // Resizing columns & rows - void clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { + int clickEvent (wdabs cx, wdabs cy, ubyte b, bool state) { debug scope (failure) logger.warn ("clickEvent: failure"); if (b == 1 && state == true) { @@ -257,7 +257,7 @@ // find col/row's resizeD & resizeU if (col.findResizeCols (cx - x) && row.findResizeCols (cy - y)) - return; // unable to resize + return 0; // unable to resize dragX = cx; dragY = cy; @@ -265,6 +265,7 @@ mgr.addClickCallback (&endCallback); mgr.addMotionCallback (&resizeCallback); } + return 0; } void draw () { diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/miscContent.d --- a/mde/gui/widget/miscContent.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/gui/widget/miscContent.d Fri Nov 14 12:44:32 2008 +0000 @@ -16,6 +16,7 @@ /** Some content widgets. */ module mde.gui.widget.miscContent; +import mde.gui.widget.textContent; import mde.gui.widget.Widget; import mde.gui.widget.TextWidget; import mde.gui.exception; @@ -27,6 +28,8 @@ IChildWidget editContent (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { if (cast(BoolContent) c) return new BoolContentWidget(mgr,id,data,c); + else if (cast(TextContent) c) + return new TextContentWidget(mgr,id,data,c); else // generic uneditable option return new DisplayContentWidget(mgr,id,data,c); } diff -r 49e7cfed4b34 -r 5de5810e3516 mde/gui/widget/textContent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/gui/widget/textContent.d Fri Nov 14 12:44:32 2008 +0000 @@ -0,0 +1,54 @@ +/* LICENSE BLOCK +Part of mde: a Modular D game-oriented Engine +Copyright © 2007-2008 Diggory Hardy + +This program is free software: you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation, either +version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ + +/** Content widgets based on a text block. */ +module textContent; + +import mde.gui.widget.Widget; +import mde.gui.widget.TextWidget; +import mde.gui.exception; +import mde.gui.renderer.IRenderer; + +import mde.gui.content.Content; + + +class TextContentWidget : ATextWidget +{ + this (IWidgetManager mgr, widgetID id, WidgetData data, IContent c) { + WDCheck(data, 1); + content = cast(TextContent) c; + if (!content) content = new TextContent (null, null); + //throw new ContentException (); + adapter = mgr.renderer.getAdapter (content.toString(0)); + super (mgr, id, data); + } + + /** On click, request keyboard input. */ + int clickEvent (wdabs, wdabs, ubyte, bool state) { + return 1; // get keyboard input via keyEvent + } + + void keyEvent (ushort s, char[] i) { + adapter.setText (content.keyStroke (s, i)); + adapter.getDimensions (mw, mh); // FIXME: only passively change size: next resize will see new minimal size + mgr.requestRedraw; + } + void keyFocusLost () { + content.endEdit; // update other users of content relying on callbacks + } + +protected: + TextContent content; +} diff -r 49e7cfed4b34 -r 5de5810e3516 mde/input/Input.d --- a/mde/input/Input.d Wed Nov 12 13:18:51 2008 +0000 +++ b/mde/input/Input.d Fri Nov 14 12:44:32 2008 +0000 @@ -25,58 +25,79 @@ // sdl imports import derelict.sdl.events; +import derelict.sdl.keyboard; import derelict.sdl.types; // only SDL_PRESSED import derelict.sdl.joystick; // SDL_HAT_* +import Utf = tango.text.convert.Utf; import tango.util.log.Log : Log, Logger; -/** Class encapsulating all input functionality. +/************************************************************************************************** + * Class encapsulating all input functionality. + * + * This class has several modes which affect output: interaction mode (default), text input mode, + * mouse gui mode and axis/button binding modes. + * + * TODO: Gui mode and button capture and axis capture modes for key binding, disabling all + * other modes (except gui-type mouse info?). + * TODO: Possible revisions: remove by-index lookup, only providing callbacks? + * TODO: Make callbacks send the time of the event? + * TODO: Adjusters, e.g. double-press, hold/click differences. Axis output: via short or double? + * TODO: add an Axis1Callback similar to getAxis1? Or remove getAxis1 and provide a conversion function? + * TODO: allow callbacks to be removed. Currently not needed. + * TODO: modifiers in text-input mode: shortcut handling? Global shortcuts - either mode? * - * The following methods are provided for Gui mouse input: + * The primary mode is the interaction mode, mapping each button and axis to a configurable index, + * and allowing event callback functions to be bound per index as well as allowing the state to be + * looked up directly. + * --- + * // For keyboard, joystick and mouse button input + * bool getButton (inputID id); + * void addButtonCallback (inputID id, ButtonCallback dg); // callback receives both up and down events + * + * // For joystick axis input + * short getAxis (inputID id); // range: -32767 .. 32767 + * double getAxis1 (inputID id); // range: -1.0 .. 1.0 + * void addAxisCallback (inputID id, AxisCallback dg); + * + * // For mouse (and joystick ball) relative motion input + * void getRelMotion (inputID id, out double x, out double y); + * void addRelMotionCallback (inputID id, RelMotionCallback dg); + * --- + * + * The keyboard can be put in text input mode, disabling interaction-mode keyboard access and + * providing a callback called on each letter press with it's UTF-8 code. Setting a LetterCallback + * activates text input mode and removing it disables this mode; only one may be active at once. + * --- + * void setLetterCallback (LetterCallback dg); + * --- + * + * Mouse input can be recieved via gui-oriented click/coordinate callbacks in both interaction + * mode and gui mode, however interaction-mode button and relative motion input is not received in + * gui mode. * --- * void getMouseScreenPos (out uint x, out uint y); * void addMouseClickCallback (MouseClickCallback dg); * void addMouseMotionCallback (MouseMotionCallback dg); * --- * - * The following methods are provided for mouse (and joystick ball) relative motion input: - * --- - * void getRelMotion (inputID id, out real x = 0.0, out real y = 0.0); - * void addRelMotionCallback (inputID id, RelMotionCallback dg); - * --- - * - * The following methods are provided for joystick axis input: - * --- - * short getAxis (inputID id); - * real getAxis1 (inputID id); - * void addAxisCallback (inputID id, AxisCallback dg); - * --- - * - * The following methods are provided for keyboard, joystick and mouse button input: - * --- - * bool getButton (inputID id); - * void addButtonCallback (inputID id, ButtonCallback dg) - * --- - * * The following methods are provided for setup & posting events: * --- - * bool opCall (ref SDL_Event event); - * void frameReset (); - * void loadConfig (char[] profile = "Default"); + * bool opCall (ref SDL_Event event); // Handles an event, making all the above work + * void frameReset (); // Needs to be called once per frame for correct relative input + * void loadConfig (char[] profile = "Default"); // Configuration for interaction-mode indexes * --- ***************************************************/ -// FIXME: remove getMouseScreenPos (no use)? -// FIXME: add an Axis1Callback similar to getAxis1? Or remove getAxis1 and provide a conversion -// function? class Input { /// Typedef for all indexes (type is uint). typedef uint inputID; alias void delegate(inputID, bool) ButtonCallback; alias void delegate(inputID, short) AxisCallback; - alias void delegate(inputID, real,real) RelMotionCallback; - alias void delegate(ushort, ushort, ubyte, bool) MouseClickCallback; - alias void delegate(ushort, ushort) MouseMotionCallback; + alias void delegate(inputID, double,double) RelMotionCallback; + alias void delegate(ushort, ushort, ubyte, bool) MouseClickCallback; + alias void delegate(ushort, ushort) MouseMotionCallback; + alias void delegate(ushort, char[]) LetterCallback; /** Get key status at this ID. * @@ -97,38 +118,27 @@ } /** Get axis status at this ID. * - * Returns: value (real; range roughly -1.0 .. 1.0) or 0 if no value at this ID. */ - real getAxis1 (inputID id) { + * Returns: value (double; range roughly -1.0 .. 1.0) or 0 if no value at this ID. */ + double getAxis1 (inputID id) { short* retp = id in axis; if (retp) return (*retp) * 3.0518509475997192e-05; else return 0.0; } - + /** Get the relative motion of the mouse or a joystick ball (since last frameReset() call). * - * Future: Converts to a real via sensitivity settings (defaults may be set and overriden per item). + * Future: Converts to a double via sensitivity settings (defaults may be set and overriden per item). * * To avoid confusion over the ID here, the idea is for the input-layer upward to support * multiple mice, in case future platforms do. * Also joystick balls (supported by SDL) can be used in the same way as a mouse for relative * positions. */ - void getRelMotion (inputID id, out real x = 0.0, out real y = 0.0) { + void getRelMotion (inputID id, out double x = 0.0, out double y = 0.0) { RelPair* rp = id in relMotion; if (rp) { x = rp.x; y = rp.y; } } - /** Get mouse pointer position in screen coordinates. - * - * Window managers only support one mouse, so there will only be one screen coordinate. - * Unlike nearly everything else, this is not configurable. - * - * Also see addMouseMotionCallback. */ - void getMouseScreenPos (out uint x, out uint y) { - x = mouse_x; y = mouse_y; - } - // /// Is this modifier on? - //bool modifierStatus (inputID id); /** Adds a callback delegate for key events (both DOWN and UP) with this ID. * @@ -176,6 +186,24 @@ void addMouseMotionCallback (MouseMotionCallback dg) { mouseMotionCallbacks ~= dg; } + + /** Sets a callback delegate to recieve key presses as a Utf-8 char[]. + * + * Since it is normal to type into only one location at once, setting a new LetterCallback + * removes the last set one (however active ButtonCallbacks will still receive events). + * Supplying a null delegate will turn off the slight overhead of unicode conversion. + * + * The char[] received by the delegate must be copied and not stored or edited directly. */ + void setLetterCallback (LetterCallback dg = null) { + if (dg) { + SDL_EnableUNICODE (1); + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); + } else { + SDL_EnableUNICODE (0); + SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL); + } + letterCallback = dg; + } /** Feed an SDL_Event struct (only uses if it's a key, mouse or joystick event). * @@ -207,9 +235,6 @@ break; case SDL_MOUSEMOTION: - mouse_x = event.motion.x; - mouse_y = event.motion.y; - foreach (dg; mouseMotionCallbacks) { try dg (event.motion.x, event.motion.y); @@ -229,7 +254,10 @@ 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; if (p) foreach (outQueue q; *p) { bEvent (this, event.key.state == SDL_PRESSED, readOutQueue(q)); @@ -370,8 +398,8 @@ } struct RelPair { // for mouse/joystick ball motion - real x, y; - static RelPair opCall (real a, real b) { + double x, y; + static RelPair opCall (double a, double b) { RelPair ret; ret.x = a; ret.y = b; return ret; @@ -382,12 +410,12 @@ static Logger logger; - Config config; // Configuration + Config config; // Configuration + char[6] utfBuf; // Buffer for Utf.toString; reallocates if less than 5. - bool[inputID] button; // Table of button states - short[inputID] axis; // Table of axes states - ushort mouse_x, mouse_y; // Current screen coords of the window manager mouse - RelPair[inputID] relMotion; // Table of relative mouse / joystick ball motions + bool[inputID] button; // Table of button states + short[inputID] axis; // Table of axes states + RelPair[inputID] relMotion; // Table of relative mouse / joystick ball motions // NOTE: currently no means of removal ButtonCallback[][inputID] buttonCallbacks; @@ -395,7 +423,8 @@ RelMotionCallback[][inputID] relMotionCallbacks; MouseClickCallback[] mouseClickCallbacks; MouseMotionCallback[] mouseMotionCallbacks; - + LetterCallback letterCallback; + //BEGIN Event stream functionality /* This section contains functions called on an event, which may modify the event (adjuster * functions), and finally output to one (or more) of the state tables (the event stream). @@ -578,7 +607,7 @@ assert (x == (counters[3] ? 0 : 32767)); counters[3] += 1; }); - ut.addRelMotionCallback (0x11F0, delegate void(inputID id, real x, real y) { + ut.addRelMotionCallback (0x11F0, delegate void(inputID id, double x, double y) { assert (x == 14.0); assert (y == -1.0); counters[4] += 1; @@ -660,7 +689,7 @@ assert (ut.getAxis(0x21F0) == -32767); assert (ut.getAxis1(0x22F0) == 0.0); - real s,t; + double s,t; ut.getRelMotion(0x10F0, s,t); assert (s == 14.0); assert (t == -1.0);