Changeset 34:6b4116e6355c

Show
Ignore:
Timestamp:
05/02/08 11:03:52 (8 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
convert_revision:
186d654d66a060c879792643ca123ca0fa499fbf
Message:

Work on the Gui: some of the framework for drag & drop. Also made Window an IWidget.

Implemented getWidget(x,y) to find the widget under this location for IWidgets (but not Gui).
Made Window an IWidget and made it work a little more similarly to widgets.
Implemented callbacks on the Gui for mouse events (enabling drag & drop, etc.).

committer: Diggory Hardy <diggory.hardy@gmail.com>

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/gui/GUI notes.txt

    r33 r34  
    1818->  Text rendering 
    1919    -> text library? 
     20->  Drag & drop 
     21    ->  click/drag start triggers a callback on the widget 
     22        ->  when button is released, callback: 
     23            ->  finds release location 
     24            ->  checks if this is a valid drop target 
     25            ->  if so, acts on it 
    2026 
    2127 
  • codeDoc/jobs.txt

    r33 r34  
    44 
    55In progress: 
    6 FreeType implementing... 
    76 
    87 
     
    5049 
    5150Done (for git log message): 
    52 Lots of changes to the GUI. Renderer is now used exclusively for rendering and WidgetDecoration is gone. 
    53 Renamed lots of files to conform to case policies. 
     51Implemented getWidget(x,y) to find the widget under this location for IWidgets (but not Gui). 
     52Made Window an IWidget and made it work a little more similarly to widgets. 
     53Implemented callbacks on the Gui for mouse events (enabling drag & drop, etc.). 
  • data/conf/gui.mtt

    r32 r34  
    22<char[]|Renderer="Simple"> 
    33{W1} 
    4 <int|x=0> 
    5 <int|y=0> 
     4<int|x=30> 
     5<int|y=80> 
    66<int[][int]|widgetData=[0:[1003,200,200]]> 
    77{W2} 
    88<int|x=150> 
    99<int|y=200> 
    10 <int[][int]|widgetData=[0:[1002,3,2,2,2,3,3,2,5],2:[1001,150,150],3:[1003,150,150],5:[1002,2,2,6,6,6,6],6:[1003,73,73]]> 
     10<int[][int]|widgetData=[0:[1002,1,3,2,3,5],2:[1001,150,150],3:[1003,150,150],5:[1002,2,1,6,7],6:[1003,73,73],7:[1003,73,73]]> 
  • mde/gl/basic.d

    r31 r34  
    5050} 
    5151void drawBox (int x, int y, int w, int h) { 
    52     glBegin (GL_QUADS); 
    53     { 
    54         glVertex2i (x, y+h); 
    55         glVertex2i (x+w, y+h); 
    56         glVertex2i (x+w, y); 
    57         glVertex2i (x, y); 
    58     } 
    59     glEnd(); 
     52    glRecti(x, y+h, x+w, y); 
    6053} 
    6154//END Drawing utils 
  • mde/gui/Gui.d

    r32 r34  
    2323 
    2424import mde.gui.IGui; 
     25import mde.gui.widget.Ifaces; 
    2526import mde.gui.widget.Window; 
    2627import mde.gui.renderer.createRenderer; 
     
    4748 
    4849/** A GUI handles a bunch of windows, all to be drawn to the same device. */ 
    49 /* NOTE: currently GUI just keeps a list of windows and simply calls draw and clickEvent on them all. 
    50 * Coords should be stored here and draw/clickEvent should be called like for widgets. 
    51 * (Also functionality like z-order?) */ 
    5250class Gui : IGui { 
    5351    //BEGIN Methods for external use 
     
    6361        try { 
    6462            reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_ONLY, null, true); 
    65             reader.dataSecCreator = delegate mt.IDataSection(mt.ID) { 
    66                 return new Window
     63            reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 
     64                return new Window (id)
    6765            }; 
    6866            reader.read; 
     
    106104    * which don't need redrawing. */ 
    107105    void draw() { 
    108         foreach (w; windows) 
    109             w.draw()
     106        foreach_reverse (w; windows)    // Draw, starting with back-most window. 
     107            w.draw
    110108    } 
    111109     
    112     /** Send an input event
     110    /** For mouse click events
    113111    * 
    114     * I.e. send all mouse click events to all active GUIs, which check the coordinates and forward 
    115     * to any relevent windows. */ 
     112    * Sends the event on to the relevant windows and all click callbacks. */ 
    116113    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 
    117         foreach (w; windows) 
    118             w.clickEvent (cx,cy,b,state); 
     114        // NOTE: buttons receive the up-event even when drag-callbacks are in place. 
     115        foreach (dg; clickCallbacks) 
     116            dg (cx, cy, b, state); 
     117         
     118        foreach (w; windows) { 
     119            IWidget widg = w.getWidget (cx,cy); 
     120            if (widg !is null) { 
     121                widg.clickEvent (cx,cy,b,state); 
     122                return;     // only pass to first window 
     123            } 
     124        } 
    119125    } 
     126     
     127    /** For mouse motion events. 
     128    * 
     129    * Sends the event on to all motion callbacks. */ 
     130    void motionEvent (ushort cx, ushort cy) { 
     131        foreach (dg; motionCallbacks) 
     132            dg (cx, cy); 
     133    } 
     134     
    120135    //END Methods for external use 
    121136     
     
    127142        return rend; 
    128143    } 
     144     
     145    void addClickCallback (void delegate(ushort, ushort, ubyte, bool) dg) { 
     146        clickCallbacks[dg.ptr] = dg; 
     147    } 
     148    void addMotionCallback (void delegate(ushort, ushort) dg) { 
     149        motionCallbacks[dg.ptr] = dg; 
     150    } 
     151    void removeCallbacks (void* frame) { 
     152        clickCallbacks.remove(frame); 
     153        motionCallbacks.remove(frame); 
     154    } 
    129155    //END IGui methods 
    130156     
    131157private: 
    132     Window[] windows; 
     158    Window[] windows;   // Windows. First window is "on top", others may be obscured. 
    133159    IRenderer rend; 
     160    // callbacks indexed by their frame pointers: 
     161    void delegate(ushort cx, ushort cy, ubyte b, bool state) [void*] clickCallbacks; 
     162    void delegate(ushort cx, ushort cy) [void*] motionCallbacks; 
    134163} 
  • mde/gui/IGui.d

    r32 r34  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
     16module mde.gui.IGui; 
     17 
     18public import mde.gui.renderer.IRenderer; 
     19 
    1620/** The Gui interface. 
    1721* 
    1822* This contains the functions for use by Windows, not those for external use (use Gui directly for 
    1923* that). */ 
    20 module mde.gui.IGui; 
    21  
    22 public import mde.gui.renderer.IRenderer; 
    23  
    2424interface IGui 
    2525{ 
    2626    /** Get the Gui's renderer. May be overriden by the window. */ 
    2727    IRenderer renderer (); 
     28     
     29    /** Add a mouse click callback: delegate will be called for all mouse click events recieved. */ 
     30    void addClickCallback (void delegate (ushort cx, ushort cy, ubyte b, bool state) dg); 
     31    /** Add a mouse motion callback: delegate will be called for all motion events recieved. */ 
     32    void addMotionCallback (void delegate (ushort cx, ushort cy) dg); 
     33    /** Remove all event callbacks with _frame pointer frame. */ 
     34    void removeCallbacks (void* frame); 
    2835} 
  • mde/gui/widget/Ifaces.d

    r32 r34  
    1818 
    1919public import mde.gui.renderer.IRenderer; 
     20import mde.gui.IGui; 
    2021 
    2122/** Interface for Window, allowing widgets to call some of Window's methods. 
    2223* 
    2324* Contains the methods in Window available for widgets to call on their root. */ 
    24 interface IWindow : IParentWidget 
     25interface IWindow : IWidget 
    2526{ 
    2627    /** Widget ID type. Each ID is unique under this window. 
     
    3233    * 
    3334    * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet 
    34     * been created, creates it using the Window's widget creation data (throws on error; don't 
    35     * catch the exception). */ 
    36     IWidget getWidget (widgetID i, IParentWidget parent); 
     35    * been created, creates it using the Window's widget creation data. 
     36    * 
     37    * Widgets should never be used more than once (must have a unique parent and position!), and 
     38    * this function is only intended for widgets to get child-widgets, hence a warning is logged 
     39    * if a widget is asked for more than once. */ 
     40    //NOTE: possibly revise: parent isn't actually used any more 
     41    IWidget makeWidget (widgetID i, IWidget parent); 
     42     
     43    /** Get the managing Gui. */ 
     44    IGui gui (); 
    3745     
    3846    /+ Currently draw-on-event isn't used. 
     
    4856} 
    4957 
    50 /** Methods exposed by both Window and Widgets which widgets can call on a parent. */ 
    51 interface IParentWidget 
    52 { 
    53 } 
    54  
    5558/** Interface for widgets. 
    5659* 
     
    6366* A widget's constructor should have this prototype: 
    6467* ---------------------------------- 
    65 * this (IWindow window, IParentWidget parent, int[] data); 
     68* this (IWindow window, IWidget parent, int[] data); 
    6669* ---------------------------------- 
    6770* Where window is the root window (the window to which the widget belongs), parent is the parent 
     
    6972* WidgetDataException (created without parameters) if the data has wrong length or is otherwise 
    7073* invalid. */ 
    71 interface IWidget : IParentWidget 
     74interface IWidget 
    7275{ 
    73     /** Draw, starting from given x and y. 
     76    /** Calculate the minimum size the widget could be shrunk to, taking into account 
     77     * child-widgets. */ 
     78    void getMinimumSize (out int w, out int h); 
     79     
     80    /** Get the current size of the widget. 
    7481     * 
    75      * Maybe later enforce clipping of all sub-widget drawing, particularly for cases where only 
    76      * part of the widget is visible: scroll bars or a hidden window. */ 
    77     void draw (int x, int y); 
     82     * On the first call (during loading), this may be a value saved as part of the config or 
     83     * something else (e.g. revert to getMinimumSize). */ 
     84    void getCurrentSize (out int w, out int h); 
     85     
     86    /** Set the current position (i.e. called on init and move). */ 
     87    void setPosition (int x, int y); 
     88     
     89    /** Recursively scan the widget tree to find the widget under (x,y). 
     90     * 
     91     * If called on a widget, that widget should assume the location is over itself, and so should 
     92     * either return itself or the result of calling getWidget on the appropriate child widget. 
     93     * 
     94     * In the case of Window this may not be the case; it should check and return null if not under 
     95     * (x,y). 
     96     * 
     97     * Note: use global coordinates (x,y) not coordinates relative to the widget. */ 
     98    IWidget getWidget (int x, int y); 
    7899     
    79100    /** Receive a mouse click event. 
     
    85106    void clickEvent (ushort cx, ushort cy, ubyte b, bool state); 
    86107     
    87     /** Calculate the minimum size the widget could be shrunk to, taking into account 
    88      * child-widgets. */ 
    89     void getMinimumSize (out int w, out int h); 
    90      
    91     /** Get the current size of the widget. 
     108    /** Draw, using the stored values of x and y. 
    92109     * 
    93      * On the first call (during loading), this may be a value saved as part of the config or 
    94      * something else (e.g. revert to getMinimumSize). */ 
    95     void getCurrentSize (out int w, out int h); 
     110     * Maybe later enforce clipping of all sub-widget drawing, particularly for cases where only 
     111     * part of the widget is visible: scroll bars or a hidden window. */ 
     112    void draw (); 
    96113} 
  • mde/gui/widget/Widget.d

    r32 r34  
    1818 
    1919public import mde.gui.widget.Ifaces; 
     20import mde.gui.IGui; 
    2021import mde.gui.exception; 
    2122 
     
    3132class Widget : IWidget 
    3233{ 
    33     /** Basic draw method: draw the background */ 
    34     void draw (int x, int y) { 
    35         window.renderer.drawWidgetBack (x,y, w,h); 
    36     } 
    37      
    38     /** Dummy event method (ignore) */ 
    39     void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {} 
    40      
    4134    /** Minimum size is zero. */ 
    4235    void getMinimumSize (out int w, out int h) {}   // w,h initialised to 0 
     
    4740    } 
    4841     
     42    void setPosition (int x, int y) { 
     43        this.x = x; 
     44        this.y = y; 
     45    } 
     46     
     47    /** Return self, since we don't have child widgets and the method wouldn't have been called 
     48    * unless the location was over us. Valid for all widgets without children. */ 
     49    IWidget getWidget (int,int) { 
     50        return this; 
     51    } 
     52     
     53    /** Dummy event method (widget doesn't respond to events) */ 
     54    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) {} 
     55     
     56    /** Basic draw method: draw the background (all widgets should do this) */ 
     57    void draw () { 
     58        window.renderer.drawWidgetBack (x,y, w,h); 
     59    } 
     60     
    4961protected: 
    5062    IWindow window;         // the enclosing window 
     63    int x, y;               // position 
    5164    int w, h;               // size 
    5265} 
     
    5669class BoxWidget : Widget 
    5770{ 
    58     this (IWindow wind, IParentWidget, int[] data) { 
     71    this (IWindow wind, IWidget, int[] data) { 
    5972        if (data.length != 2) throw new WidgetDataException; 
    6073         
     
    6477        h = data[1]; 
    6578    } 
    66     void draw (int x, int y) { 
     79    void draw () { 
    6780        gl.setColor(1f,0f,0f); 
    6881        window.renderer.drawBox (x,y, w,h); 
     
    7386class ButtonWidget : Widget 
    7487{ 
    75     bool pushed = false;// true if button is pushed in 
     88    bool pushed = false;    // true if button is pushed in (visually) 
     89    // pushed is not the same as the button being clicked but not yet released. 
     90    // it is whether the mouse is over the button after being clicked. 
    7691     
    77     this (IWindow wind, IParentWidget, int[] data) { 
     92    this (IWindow wind, IWidget, int[] data) { 
    7893        if (data.length != 2) throw new WidgetDataException; 
    7994         
     
    8499    } 
    85100     
    86     void draw (int x, int y) { 
     101    void draw () { 
    87102        if (pushed) 
    88103            gl.setColor (1f, 0f, 1f); 
     
    98113     
    99114    void clickEvent (ushort, ushort, ubyte b, bool state) { 
    100         if (b == 1) pushed = state; // very basic 
     115        if (b == 1 && state == true) { 
     116            pushed = true; 
     117            window.gui.addClickCallback (&clickWhileHeld); 
     118            window.gui.addMotionCallback (&motionWhileHeld); 
     119        } 
     120    } 
     121    // Called when a mouse motion/click event occurs while (held == true) 
     122    void clickWhileHeld (ushort cx, ushort cy, ubyte b, bool state) { 
     123        if (cx >= x && cx < x+w && cy >= y && cy < y+h) // button event 
     124            Stdout ("Button clicked!").newline; 
     125         
     126        pushed = false; 
     127        window.gui.removeCallbacks (cast(void*) this); 
     128    } 
     129    void motionWhileHeld (ushort cx, ushort cy) { 
     130        if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true; 
     131        else pushed = false; 
    101132    } 
    102133} 
  • mde/gui/widget/Window.d

    r32 r34  
    2828// not yet implemented: 
    2929//import tango.scrapple.text.convert.parseFrom : parseFrom; 
     30 
     31import tango.util.log.Log : Log, Logger; 
     32 
     33private Logger logger; 
     34static this () { 
     35    logger = Log.getLogger ("mde.gui.widget.Window"); 
     36} 
    3037 
    3138/** GUI Window class 
     
    4047{ 
    4148    //BEGIN Methods for GUI 
     49    this (char[] id) { 
     50        name = id; 
     51    } 
     52     
    4253    /** Call after loading is finished to setup the window and confirm that it's valid. 
    4354     * 
    4455     * Throws: WindowLoadException. Do not use the instance in this case! */ 
    45     void finalise (IGui gui) { 
     56    void finalise (IGui gui) 
     57    in { 
     58        assert (gui !is null, "Window.finalise ("~name~"): gui is null"); 
     59    } body { 
    4660        // Check data was loaded: 
    4761        if (widgetData is null) throw new WindowLoadException ("No widget data"); 
    4862         
    49         // Create the renderer: 
     63        gui_ = gui; 
    5064        rend = gui.renderer; 
    5165         
    5266        // Create the primary widget (and indirectly all sub-widgets), throwing on error: 
    53         widget = getWidget (0, this);// primary widget always has ID 0. 
     67        widget = makeWidget (0, this);// primary widget always has ID 0. 
    5468         
    5569        widgetData = null;          // data is no longer needed: allow GC to collect (cannot safely delete) 
     
    5771        widgetX = x + rend.windowBorder;  // widget position 
    5872        widgetY = y + rend.windowBorder;  // must be updated if the window is moved 
     73        widget.setPosition (widgetX, widgetY); 
    5974         
    6075        widget.getCurrentSize (w,h);// Find the initial size 
     
    6580        yh = y+h; 
    6681    } 
    67      
    68     void draw () { 
    69         // background 
    70         rend.drawWindow (x,y, w,h); 
    71          
    72         // Tell the widget to draw itself: 
    73         widget.draw(widgetX, widgetY); 
    74     } 
    75      
    76     void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 
    77         if (cx >= x && cx < xw && cy >= y && cy < yh) { // click on window? 
    78             if (cx >= widgetX && cx < xw-rend.windowBorder && cy >= widgetY && cy < yh-rend.windowBorder)   // click on widget? 
    79                 widget.clickEvent (cx-widgetX, cy-widgetY, b, state); 
    80             // FIXME: else window dragging? 
    81         } 
    82     } 
    83      
    8482    //BEGIN Mergetag code 
    8583    void addTag (char[] tp, mt.ID id, char[] dt) { 
     
    105103     * 
    106104     * Should $(I only) be called internally and by sub-widgets! */ 
    107     IWidget getWidget (widgetID i, IParentWidget parent) 
     105    IWidget makeWidget (widgetID i, IWidget parent) 
    108106    in { 
    109107        // widgetData is normally left to be garbage collected after widgets have been created: 
    110         assert (widgetData !is null, "getWidget: widgetData is null"); 
     108        assert (widgetData !is null, "Window.makeWidget ("~name~"): widgetData is null"); 
    111109    } body { 
    112110        // See if it's already been created: 
    113111        IWidget* p = i in widgets; 
    114         if (p !is null) return *p;  // yes 
    115         else {                      // no 
     112        if (p !is null) {   // yes 
     113            char[128] tmp; 
     114            logger.warn (logger.format (tmp, "Window.makeWidget ("~name~"): widget {} has multiple uses!", i)); 
     115            return *p; 
     116        } 
     117        else {              // no 
    116118            int[]* d = i in widgetData; 
    117             if (d is null) throw new WindowLoadException ("Widget not found"); 
     119            if (d is null) throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found"); 
    118120             
    119121            // Throws WidgetDataException (a WindowLoadException) if bad data: 
     
    124126    } 
    125127     
     128    IGui gui () { 
     129        return gui_; 
     130    } 
     131     
    126132    /+void requestRedraw () { 
    127133    }+/ 
     
    132138    //END IWindow methods 
    133139     
    134     //BEGIN IParentWidget methods 
    135     //END IParentWidget methods 
     140    //BEGIN IWidget methods 
     141    void getMinimumSize (out int w, out int h) { 
     142        widget.getMinimumSize (x,y); 
     143        w += rend.windowBorder * 2;       // Adjust for border 
     144        h += rend.windowBorder * 2; 
     145    } 
     146    void getCurrentSize (out int cw, out int ch) { 
     147        cw = w; 
     148        ch = h; 
     149    } 
     150     
     151    void setPosition (int x, int y) { 
     152        /+ Note: this is currently unused. Maybe only use it internally? 
     153        this.x = x; 
     154        this.y = y; 
     155         
     156        widgetX = x + rend.windowBorder; 
     157        widgetY = y + rend.windowBorder; 
     158         
     159        widget.setPosition (widgetX, widgetY); 
     160        +/ 
     161    } 
     162     
     163    IWidget getWidget (int cx, int cy) { 
     164        if (cx < x || cx >= xw || cy < y || cy >= yh)   // not over window 
     165            return null; 
     166        if (cx >= widgetX && cx < xw-rend.windowBorder && cy >= widgetY && cy < yh-rend.windowBorder) 
     167                                                        // over the widget 
     168            return widget.getWidget (cx, cy); 
     169        else                                            // over the window border 
     170            return this; 
     171    } 
     172    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 
     173        //if (cx >= x && cx < xw && cy >= y && cy < yh) { // click on window? 
     174        // FIXME: repositioning? 
     175    } 
     176     
     177    void draw () { 
     178        // background 
     179        rend.drawWindow (x,y, w,h); 
     180         
     181        // Tell the widget to draw itself: 
     182        widget.draw(); 
     183    } 
     184    //END IWidget methods 
    136185     
    137186private: 
     187    char[] name;                    // The window's name (id from config file) 
     188    IGui gui_;                      // The gui managing this window 
     189     
    138190    int[][widgetID] widgetData;     // Data for all widgets under this window (deleted after loading) 
    139191    IWidget[widgetID] widgets;      // List of all widgets under this window (created on demand). 
     
    141193     
    142194    IRenderer rend;                 // The window's renderer 
     195    // FIXME: revise which parameters are stored once Gui knows window position 
    143196    int x,y;                        // Window position 
    144197    int w,h;                        // Window size (calculated from Widgets) 
  • mde/gui/widget/createWidget.d

    r32 r34  
    3131/** Create a widget of type data[0] (see enum WIDGET_TYPES) for _window window, with initialisation 
    3232* data [1..$]. */ 
    33 IWidget createWidget (IWindow window, IParentWidget parent, int[] data) 
     33IWidget createWidget (IWindow window, IWidget parent, int[] data) 
    3434in { 
    3535    assert (window !is null, "createWidget: window is null"); 
  • mde/gui/widget/layout.d

    r32 r34  
    2323class GridWidget : Widget 
    2424{ 
    25     this (IWindow wind, IParentWidget, int[] data) { 
     25    this (IWindow wind, IWidget, int[] data) { 
    2626        // Get grid size 
    2727        if (data.length < 2) throw new WidgetDataException; 
     
    3636        subWidgets.length = rows*cols; 
    3737        foreach (i, inout subWidget; subWidgets) { 
    38             subWidget = window.getWidget (data[i+2], this); 
     38            subWidget = window.makeWidget (data[i+2], this); 
    3939        } 
    4040         
     
    5858        colW.length = cols; //WARNING: code reliant on these being initialised to zero 
    5959        for (uint i = 0; i < subWidgets.length; ++i) { 
    60             uint x = i / cols;  // row 
    61             if (rowH[x] < widgetH[i]) rowH[x] = widgetH[i]; 
    62             x = i % cols;       // column 
    63             if (colW[x] < widgetW[i]) colW[x] = widgetW[i]; 
     60            uint n = i / cols;  // row 
     61            if (rowH[n] < widgetH[i]) rowH[n] = widgetH[i]; 
     62            n = i % cols;       // column 
     63            if (colW[n] < widgetW[i]) colW[n] = widgetW[i]; 
    6464        } 
    6565         
     
    8383    } 
    8484     
    85     void draw (int x, int y) { 
    86         super.draw (x,y); 
     85    void setPosition (int x, int y) { 
     86        this.x = x; 
     87        this.y = y; 
    8788         
    88         foreach (i,widget; subWidgets) { 
    89             widget.draw (x + colX[i % cols], y + rowY[i / cols]); 
    90         } 
     89        foreach (i,widget; subWidgets) 
     90            widget.setPosition (x + colX[i % cols], y + rowY[i / cols]); 
    9191    } 
    9292     
    93     // Pass event on to relevant widget. Simply return if not on a widget. 
    94     void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 
    95         if (rows*cols == 0) return; // special case 
     93    // Find the relevant widget. 
     94    IWidget getWidget (int cx, int cy) { 
     95        if (rows*cols == 0) return this;    // special case 
     96         
     97        int lx = cx - x, ly = cy - y;       // use coords relative to this widget 
    9698         
    9799        // Find the column 
    98         int i = cols - 1;          // starting from right... 
    99         while (cx < colX[i]) {      // decrement while left of this column 
    100             if (i == 0) return;     // left of first column 
     100        int i = cols - 1;                   // starting from right... 
     101        while (lx < colX[i]) {              // decrement while left of this column 
     102            if (i == 0) return this;        // left of first column 
    101103            --i; 
    102         }                           // now (cx >= colX[i]) 
    103         if (cx >= colX[i] + colW[i]) return;    // between columns 
     104        }                                   // now (lx >= colX[i]) 
     105        if (lx >= colX[i] + colW[i]) return this;   // between columns 
    104106         
    105107        // Find the row; 
    106108        int j = rows - 1; 
    107         while (cy < rowY[j]) { 
    108             if (j == 0) return
     109        while (ly < rowY[j]) { 
     110            if (j == 0) return this
    109111            --j; 
    110112        } 
    111         if (cy >= rowY[j] + rowH[j]) return
     113        if (ly >= rowY[j] + rowH[j]) return this
    112114         
    113115        // Now we know it's in widget (i,j)'s cell (but the widget may not take up the whole cell) 
    114         cx -= colX[i]; 
    115         cy -= rowY[j]; 
     116        lx -= colX[i]; 
     117        ly -= rowY[j]; 
    116118        IWidget widg = subWidgets[i + j*cols]; 
    117119        widg.getCurrentSize (i,j); 
    118         if (cx < i && cy < j) 
    119             widg.clickEvent (cx, cy, b, state); 
     120        if (lx < i && ly < j) 
     121            return widg.getWidget (cx, cy); 
     122    } 
     123     
     124    void draw () { 
     125        super.draw (); 
     126         
     127        foreach (widget; subWidgets) 
     128            widget.draw (); 
    120129    } 
    121130     
     
    127136    int[] colX;         // cumulative colW[i-1] + border and padding 
    128137    IWidget[] subWidgets;   // all widgets in the grid (by row): 
    129     /* SubWidget order:    [ 2 3
    130     *                      [ 0 1 ] */ 
     138    /* SubWidget order:    [ 0 1
     139    *                      [ 2 3 ] */ 
    131140} 
  • mde/input/Input.d

    r32 r34  
    3131import tango.util.log.Log : Log, Logger; 
    3232 
    33 /// Class encapsulating all input functionality. 
     33/** Class encapsulating all input functionality. 
     34 * 
     35 * The following methods are provided for Gui mouse input: 
     36 * --- 
     37 * void getMouseScreenPos (out uint x, out uint y); 
     38 * void addMouseClickCallback (MouseClickCallback dg); 
     39 * void addMouseMotionCallback (MouseMotionCallback dg); 
     40 * --- 
     41 * 
     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 * 
     61 * The following methods are provided for setup & posting events: 
     62 * --- 
     63 * bool opCall (ref SDL_Event event); 
     64 * void frameReset (); 
     65 * void loadConfig (char[] profile = "Default"); 
     66 * --- 
     67 ***************************************************/ 
     68// FIXME: remove getMouseScreenPos (no use)? 
     69// FIXME: add an Axis1Callback similar to getAxis1? Or remove getAxis1 and provide a conversion 
     70// function? 
    3471class Input 
    3572{ 
     
    3976    alias void delegate(inputID, short)     AxisCallback; 
    4077    alias void delegate(inputID, real,real) RelMotionCallback; 
    41     alias void delegate(ushort, ushort, ubyte, bool)    MouseClickCallback; 
     78    alias void delegate(ushort, ushort, ubyte, bool)    MouseClickCallback; 
     79    alias void delegate(ushort, ushort)                 MouseMotionCallback; 
    4280 
    4381    /** Get key status at this ID. 
     
    74112    * multiple mice, in case future platforms do. 
    75113    * Also joystick balls (supported by SDL) can be used in the same way as a mouse for relative 
    76     * positions. 
    77     */ 
     114    * positions. */ 
    78115    void getRelMotion (inputID id, out real x = 0.0, out real y = 0.0) { 
    79116        RelPair* rp = id in relMotion; 
     
    86123    * Window managers only support one mouse, so there will only be one screen coordinate. 
    87124    * Unlike nearly everything else, this is not configurable. 
    88     */ 
     125    * 
     126    * Also see addMouseMotionCallback. */ 
    89127    void getMouseScreenPos (out uint x, out uint y) { 
    90128        x = mouse_x;    y = mouse_y; 
     
    95133    /** Adds a callback delegate for key events (both DOWN and UP) with this ID. 
    96134    * 
    97     * Delegate receives event status. 
    98     */ 
     135    * Delegate receives event status. */ 
    99136    void addButtonCallback (inputID id, ButtonCallback dg) { 
    100137        buttonCallbacks[id] ~= dg; 
     
    103140    /** Adds a callback delegate for axis events with this ID. 
    104141    * 
    105     * Delegate receives event status (as per what getAxis returns). 
    106     */ 
     142    * Delegate receives event status (as per what getAxis returns). */ 
    107143    void addAxisCallback (inputID id, AxisCallback dg) { 
    108144        axisCallbacks[id] ~= dg; 
     
    116152    * (A separate callback for mouse screen position changes is not 
    117153    * necessary since this will be triggered by the same event - use mouseScreenPos from within the 
    118     * function to get new screen coordinates.) 
    119     */ 
     154    * function to get new screen coordinates.) */ 
    120155    void addRelMotionCallback (inputID id, RelMotionCallback dg) { 
    121156        relMotionCallbacks[id] ~= dg; 
     
    130165    * The point of this over a standard button callback is firstly to avoid mouse configuration for 
    131166    * the GUI, and secondly to give the pointer position at the time of the event, not the time the 
    132     * callback gets called. 
    133     */ 
     167    * callback gets called. */ 
    134168    void addMouseClickCallback (MouseClickCallback dg) { 
    135169        mouseClickCallbacks ~= dg; 
    136170    } 
     171     
     172    /** Adds a callback delegate for all mouse motion events. 
     173    * 
     174    * Really just for graphical user interfaces. Use addRelMotionCallback for relative motion (for 
     175    * manipulating 3D views, etc.). */ 
     176    void addMouseMotionCallback (MouseMotionCallback dg) { 
     177        mouseMotionCallbacks ~= dg; 
     178    } 
    137179 
    138180    /** Feed an SDL_Event struct (only uses if it's a key, mouse or joystick event). 
     
    142184    * 
    143185    * May throw InputClassExceptions (on configuration errors). Catching the exception and continuing should 
    144     * be fine. 
    145     */ 
     186    * be fine. */ 
    146187    bool opCall (ref SDL_Event event) { 
    147188        /* Non-config events. 
     
    164205                mouse_x = event.motion.x - 1; 
    165206                mouse_y = event.motion.y - 1; 
     207                 
     208                foreach (dg; mouseMotionCallbacks) 
     209                    dg (event.motion.x - 1, event.motion.y - 1); 
    166210                break; 
    167211             
     
    171215        /* No config available, so don't try to access it and segfault. 
    172216        * Don't log a message because this function is called per-event (i.e. frequently). 
    173         * A message should already have been logged by loadConfig anyway. 
    174         */ 
     217        * A message should already have been logged by loadConfig anyway. */ 
    175218        if (!config) return false; 
    176219         
     
    287330    * 
    288331    * Should be called once-per-frame if these are used, but must be called after their state has 
    289     * been read (e.g. just before updating the input). 
    290     */ 
     332    * been read (e.g. just before updating the input). */ 
    291333    void frameReset () { 
    292334        foreach (rp; relMotion) { 
     
    298340    * 
    299341    * Throws: ConfigLoadException if unable to load any configs or the requested config id wasn't 
    300     *   found. 
    301     */ 
     342    *   found. */ 
    302343    void loadConfig (char[] profile = "Default") { 
    303344        Config.load("input");   // FIXME: filename 
     
    343384    RelMotionCallback[][inputID] relMotionCallbacks; 
    344385    MouseClickCallback[]    mouseClickCallbacks; 
     386    MouseMotionCallback[]   mouseMotionCallbacks; 
    345387         
    346388    //BEGIN Event stream functionality 
     
    348390    * functions), and finally output to one (or more) of the state tables (the event stream). 
    349391    * 
    350     * Adjuster and other event functions should have a format to fit the ES_X_Func types, for X is B 
    351     * (button event), A (axis event) or M (mouse relative motion event or joystick ball event). 
     392    * Adjuster and other event functions should have a format to fit the ES_X_Func types, for X is 
     393    * B (button event), A (axis event) or M (mouse relative motion event or joystick ball event). 
    352394    * Adjusters should call one of the xEvent() functions with their output and the remainder of 
    353395    * the readOutQueue. 
    354396    * 
    355     * To control which adjusters get called and pass parameters, a stack of sorts is used: outQueue. 
    356     *
     397    * To control which adjusters get called and pass parameters, a stack of sorts is used: 
     398    * outQueue. *
    357399    //BEGIN ES Definitions 
    358400    /* Note: We really want an array, not a stack. We cannot edit the lists, so we can either 
    359     * copy to a stack or just iterate through it as an array. 
    360     */ 
     401    * copy to a stack or just iterate through it as an array. */ 
    361402    alias Config.outQueue outQueue; 
    362403    struct readOutQueue {       // A convenient structure for reading an outQueue item by item. 
     
    390431    /* These are the codes allowing the config to specify event functions. 
    391432    * 
    392     * They are organised as defined in doc/input_ID_assignments. 
    393     */ 
     433    * They are organised as defined in doc/input_ID_assignments. */ 
    394434    enum ES_B : uint { 
    395435        OUT = 0x1000u, 
     
    489529    * 
    490530    * It relies on config loaded from a file (dependant on where input bindings are loaded from; 
    491     * currently conf/input.mtt). 
    492     */ 
     531    * currently conf/input.mtt). */ 
    493532    debug (mdeUnitTest) unittest { 
    494533        Input ut = new Input(); 
  • mde/scheduler/init2.d

    r33 r34  
    7070            } 
    7171        } ); 
     72         
     73        // Aught to be added by the gui, but it doesn't know if input exists then. 
    7274        global.input.addMouseClickCallback(&gui.clickEvent); 
     75        global.input.addMouseMotionCallback(&gui.motionEvent); 
    7376    } catch (Exception e) { 
    7477        logger.fatal ("initInput failed: " ~ e.msg);