Changeset 201

Show
Ignore:
Timestamp:
07/26/10 00:52:41 (2 years ago)
Author:
JoeCoder
Message:

Improved cursor positioning for home/end/up/down
Fixed bugs and improved event system interface for mouse events and buttons/scroll wheel.
Fixed Surface.getPolygon not matching its rendered position.
Cursor positioning on mouse click.
Added Surface.localToGlobal, localToParent, parentToLocal, and globalToLocal (although they're not quite finished).
Added Input.mouse to get the current mouse state.
Added Vec toInt and toFloat.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/demo1/main.d

    r198 r201  
    167167    }; 
    168168     
    169     view.onMouseDown = (Surface self, byte buttons, Vec2i coordinates){ 
     169    view.onMouseDown = (Surface self, Input.MouseButton button, Vec2i coordinates){ 
    170170        scene.ship.acceptInput = !scene.ship.acceptInput; 
    171171        self.grabMouse(scene.ship.acceptInput); 
    172172        return false; 
    173173    }; 
    174     view.onMouseMove = (Surface self, byte buttons, Vec2i rel){ 
     174    view.onMouseMove = (Surface self, Vec2i rel){ 
    175175        if(scene.ship.acceptInput) 
    176176            scene.ship.input.mouseDelta += rel; 
     
    180180    // Make a draggable window to show some useful info. 
    181181    auto info = view.addChild(new Surface()); 
    182     info.style.set("top: 5px; right: 12px; width: 110px; height: 100px; color: white; padding: -6px; " ~ 
     182    info.style.set("top: 5px; right: 12px; width: 115px; height: 100px; color: white; " ~ 
    183183        "border-width: 12px; border-image: url('gui/skin/panel1.png'); font-size: 12px"); 
    184184 
    185185    //window.style.backgroundImage = scene.camera.getTexture(); 
    186     info.onMouseDown = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    187         self.raise(); 
    188         self.focus(); 
     186    bool dragging; 
     187    info.onMouseDown = delegate bool(Surface self, Input.MouseButton button, Vec2i coordinates) { 
     188        if (button == Input.MouseButton.LEFT) 
     189            dragging = true; 
    189190        return false; // don't propagate upward 
    190191    }; 
    191     info.onMouseMove = delegate bool(Surface self, byte buttons, Vec2i amount) { 
    192         if(buttons == 1)  
    193             self.move(cast(Vec2f)amount, true); 
    194         return false; 
    195     }; 
    196     info.onMouseUp = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    197         self.blur(); 
    198         return false; 
    199     }; 
    200     info.onMouseOver = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
     192    info.onMouseMove = delegate bool(Surface self, Vec2i amount) { 
     193        if (dragging) 
     194            self.move(amount.toFloat(), true); 
     195        return false; 
     196    }; 
     197    info.onMouseUp = delegate bool(Surface self, Input.MouseButton button, Vec2i coordinates) { 
     198        if (button == Input.MouseButton.LEFT) 
     199            dragging = false; 
     200        return false; 
     201    }; 
     202    info.onMouseOver = delegate bool(Surface self) { 
    201203        self.style.set("border-image: url('gui/skin/panel2.png')"); 
    202204        return false; 
    203205    }; 
    204     info.onMouseOut = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
     206    info.onMouseOut = delegate bool(Surface self) { 
    205207        self.style.set("border-image: url('gui/skin/panel1.png')"); 
    206208        return false; 
     
    229231        {   float framerate = fps/frame.tell(); 
    230232            window.setCaption(format("Yage Demo | %.2f fps\0", framerate)); 
    231             info.innerHtml = format( 
     233            info.setHtml(format( 
    232234                `%.2f <b>fps</span><br/>` 
    233235                `%d <b>objects</b><br/>` 
     
    235237                `%d <b>vertices</b><br/>` 
    236238                `%d <b>lights</b><br/><br/> wasd to move<br/> +q for hyperdrive<br/>space to shoot`, 
    237                 framerate, stats.nodeCount, stats.triangleCount, stats.vertexCount, stats.lightCount) ~ Profile.getTimesAndClear()
     239                framerate, stats.nodeCount, stats.triangleCount, stats.vertexCount, stats.lightCount) ~ Profile.getTimesAndClear())
    238240            frame.seek(0); 
    239241            fps = 0; 
  • trunk/src/demo2/main.d

    r198 r201  
    1818import derelict.opengl.gl; 
    1919import derelict.opengl.glext; 
     20 
     21bool dragging; 
    2022 
    2123// program entry point. 
     
    5355        return true; 
    5456    }; 
    55     view.onMouseDown = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    56         self.grabMouse(!self.getGrabbedMouse()); 
    57         return true; 
    58     }; 
    5957     
    6058    // Lights 
     
    6260    l1.setPosition(Vec3f(0, 200, -30));  
    6361 
    64     // For Testing 
     62    // A window with text in it 
    6563    auto info = view.addChild(new Surface()); 
    6664    info.style.set("top: 40px; left: 40px; width: 500px; height: 260px; padding: 3px; color: #ff8800; " ~ 
     
    7068    info.style.overflowY = Style.Overflow.HIDDEN; 
    7169     
    72     info.onMouseDown = delegate bool(Surface self, byte buttons, Vec2i coordinates){ 
    73         self.raise(); 
    74         self.focus(); 
     70    bool dragging; 
     71    info.onMouseDown = delegate bool(Surface self, Input.MouseButton button, Vec2i coordinates) { 
     72        if (button == Input.MouseButton.LEFT) 
     73            dragging = true; 
    7574        return false; 
    7675    }; 
    77     info.onMouseMove = delegate bool(Surface self, byte buttons, Vec2i amount) { 
    78         if(buttons == 1)  
    79             self.move(cast(Vec2f)amount, true); 
     76    info.onMouseMove = delegate bool(Surface self, Vec2i amount) {      
     77        if (dragging) 
     78            self.move(amount.toFloat(), true); 
    8079        return false; 
    8180    }; 
    82     info.onMouseUp = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    83         self.blur(); 
     81    info.onMouseUp = delegate bool(Surface self, Input.MouseButton button, Vec2i coordinates) { 
     82        if (button == Input.MouseButton.LEFT) 
     83            dragging = false; 
    8484        return false; 
    8585    }; 
    86     info.onMouseOver = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    87         //self.style.set("border-image: url('gui/skin/panel2.png')"); 
     86    info.onMouseOver = delegate bool(Surface self) { 
     87        self.style.set("border-image: url('gui/skin/panel2.png')"); 
    8888        return false; 
    8989    }; 
    90     info.onMouseOut = delegate bool(Surface self, byte buttons, Vec2i coordinates) { 
    91         //self.style.set("border-image: url('gui/skin/panel1.png')"); 
     90    info.onMouseOut = delegate bool(Surface self) { 
     91        self.style.set("border-image: url('gui/skin/panel1.png')"); 
    9292        return false; 
    9393    }; 
    9494    info.editable = view.editable = true; 
    95     //info.style.transform = Matrix().scale(Vec3f(.5, .5, .5)); 
    96      
     95    /* 
     96    // Test overflow clipping 
    9797    auto clip = info.addChild(new Surface()); 
    9898    clip.style.set("width: 60px; height: 60px; background-color: black; top: -30px; left: -30px; overflow: hidden"); 
     
    103103    auto clip3 = info.addChild(new Surface()); 
    104104    clip3.style.set("width: 60px; height: 60px; background-color: orange; top: -30px; right: -30px"); 
    105      
     105    */ 
    106106     
    107107     
     
    115115        auto stats = Render.scene(camera, window); 
    116116        Render.surface(view, window); 
    117  
    118117        Render.complete(); // swap buffers 
    119118         
    120         // Print framerate 
    121         fps++
     119        // Rotate the info box 
     120        float amount = total.tell()
    122121        info.style.transform = Matrix(); 
    123122        info.style.transform = info.style.transform.move(Vec3f(-300, -20, 0)); 
    124         info.style.transform *= Matrix().rotate(Vec3f(0, sin(total.tell()/2)/2, sin(total.tell()/2)/5)); 
     123        info.style.transform *= Matrix().rotate(Vec3f(0, /*sin(amount/2)/2*/0, sin(amount/2)/5)); 
    125124        info.style.transform = info.style.transform.move(Vec3f(300, 20, 0)); 
    126125         
    127126         
     127        fps++; 
    128128        if (frame.tell()>=.25f && !(Surface.getFocusSurface() is info)) 
    129         {    
    130             info.innerHtml = `In a <s>traditional</s> <span style="color: green; text-decoration: overline; font-size:40px">`~ 
    131             `<u>M</u>a<s>nua</s>l <u style="font-size: 18px">printing</u></span> (letterpress) `~ 
    132             `<span style="text-decoration: overline">house</span> the font would refer to a complete set of metal `~ 
    133             `type that <b>would be used</b> to type-set an entire page. Unlike a digital typeface it would not `~ 
    134             `include a single definition of each character, but commonly used characters (such as vowels and periods) `~ 
    135             `would have more <i>physical type-pieces included. A <b>font <i style="font-style: normal">when</i> bought</b> new would often be sold as `~ 
    136             `(for example in a roman alphabet) 12pt 14A 34a, meaning that it would be a <span style="font-size: 30px">size</span> 12pt fount containing `~ 
    137             `14 upper-case 'A's, and 34 lower-case 'A's.</i> The rest of the characters would be provided in quantities `~ 
    138             `appropriate for the language it was required for in order to set a complete page in that language. `~ 
    139             `Some metal type required in type-setting, such as varying sizes of inter-word spacing pieces and `~ 
    140             `line-width spacers, were not part of a specific font in pre-digital usage, but were separate, `~ 
    141             `generic pieces.[1]              `~ Format.convert(` {} fps<br/>`, fps/frame.tell()); 
     129        {   info.setHtml(`Click <s>here</s> <span style="color: green; text-decoration: overline; font-size:40px">`~ 
     130            `<u>To</u><s> type and</s> <u style="font-size: 18px">edit</u></span> this `~ 
     131            `<span style="text-decoration: overline">block</span> of <b>text. <i style="font-style: normal">No,</i> really</b> it `~ 
     132            `works,<br/><br/>Another line of text.<br/><br/><br/> `~ Format.convert(` {} fps<br/>`, fps/frame.tell())); 
    142133             
    143134            frame.seek(0); 
  • trunk/src/yage/core/math/vector.d

    r187 r201  
    159159        assert(Vec2f(0, 0).inside(polygon)); 
    160160        assert(!Vec2f(0, 2).inside(polygon)); 
     161        assert(Vec2f(0, .999999).inside(polygon)); 
     162        assert(!Vec2f(0, 1.00001).inside(polygon)); 
    161163    } 
    162164     
     
    290292     
    291293    /// Allow casting to float where appropriate 
    292     static if (is(T : float))   // if T can be implicitly cast to float 
    293     {   Vec!(S, float) opCast() 
    294         {   Vec!(S, float) result; 
    295             for (int i=0; i<v.length; i++) 
    296                 result.v[i] = v[i]; 
    297             return result; 
    298     }   } 
     294    Vec!(S, float) toFloat() 
     295    {   Vec!(S, float) result; 
     296        for (int i=0; i<v.length; i++) 
     297            result.v[i] = v[i]; 
     298        return result; 
     299    } 
     300     
     301    Vec!(S, int) toInt() 
     302    {   Vec!(S, int) result; 
     303        for (int i=0; i<v.length; i++) 
     304            result.v[i] = cast(int)v[i]; 
     305        return result; 
     306    } 
    299307     
    300308    /// Get the element at i 
  • trunk/src/yage/gui/surface.d

    r200 r201  
    77module yage.gui.surface; 
    88 
     9public import derelict.sdl.sdl; 
     10 
    911import tango.math.IEEE; 
    1012import tango.math.Math; 
    1113import tango.text.convert.Utf; 
    1214import yage.core.all; 
    13 import yage.system.system; 
    14 import yage.system.input; 
    1515import yage.system.log; 
    16 import yage.system.graphics.render; 
    1716import yage.system.window; 
    1817import yage.resource.manager; 
     
    2019import yage.resource.image; 
    2120import yage.resource.material; 
     21import yage.system.input; 
    2222import yage.gui.style; 
    2323import yage.gui.textblock; 
     
    3333{    
    3434    Style style; 
    35      
    36     //protected char[] text; /// This html text will be rendered inside the surface.     
     35         
    3736    bool editable = false; /// The text of this surface is editable. 
    3837    bool mouseChildren = true; /// Allow the mouse to interact with this Surface's children. 
     38    int mouseX, mouseY; /// Current position of the mouse cursor.  (Read-only for now) 
    3939    TextCursor textCursor; /// 
    4040     
     
    4242    bool delegate(Surface self) onBlur; /// 
    4343    bool delegate(Surface self) onFocus; /// 
    44     bool delegate(Surface self, byte buttons, Vec2i coordinates) onClick; /// When a mouse button is pressed and released without moving the mouse. 
    45     bool delegate(Surface self, byte buttons, Vec2i coordinates) onDblCick; /// unfinished 
     44    bool delegate(Surface self, Input.MouseButton button, Vec2i coordinates) onClick; /// When a mouse button is pressed and released without moving the mouse. 
     45    bool delegate(Surface self, Input.MouseButton button, Vec2i coordinates) onDblCick; /// TODO unfinished 
    4646    bool delegate(Surface self, int key, int modifier) onKeyDown; /// Triggered once when a key is pressed down 
    4747    bool delegate(Surface self, int key, int modifier) onKeyUp; /// Triggered once when a key is released 
     
    5151     * Unlike onKeyDown and onKeyUp, key is the unicode value of the key press, instead of the sdl key code. */ 
    5252    bool delegate(Surface self, dchar key, int modifier) onKeyPress;  
    53     bool delegate(Surface self, byte buttons, Vec2i coordinates) onMouseDown; /// 
    54     bool delegate(Surface self, byte buttons, Vec2i coordinates) onMouseUp; /// 
    55     bool delegate(Surface self, byte buttons, Vec2i amount) onMouseMove; /// 
    56     bool delegate(Surface self, byte buttons, Vec2i coordinates) onMouseOver; /// 
    57     bool delegate(Surface self, byte buttons, Vec2i coordinates) onMouseOut; /// 
     53    bool delegate(Surface self, Input.MouseButton button, Vec2i coordinates) onMouseDown; /// 
     54    bool delegate(Surface self, Input.MouseButton button, Vec2i coordinates) onMouseUp; /// 
     55    bool delegate(Surface self, Vec2i amount) onMouseMove; /// 
     56    bool delegate(Surface self) onMouseOver; /// 
     57    bool delegate(Surface self) onMouseOut; /// 
    5858    void delegate(Surface self, Vec2f amount) onResize; /// 
    5959     
    60     /// This is a mirror of SDLMod (SDL's modifier key struct) 
    61     enum ModifierKey 
    62     {   NONE  = 0x0000, /// Allowed values. 
    63         LSHIFT= 0x0001, /// ditto 
    64         RSHIFT= 0x0002, /// ditto 
    65         LCTRL = 0x0040, /// ditto 
    66         RCTRL = 0x0080, /// ditto 
    67         LALT  = 0x0100, /// ditto 
    68         RALT  = 0x0200, /// ditto 
    69         LMETA = 0x0400, /// ditto 
    70         RMETA = 0x0800, /// ditto 
    71         NUM   = 0x1000, /// ditto 
    72         CAPS  = 0x2000, /// ditto 
    73         MODE  = 0x4000, /// ditto 
    74         RESERVED = 0x8000, /// ditto 
    75         CTRL  = LCTRL | RCTRL, /// ditto 
    76         SHIFT = LSHIFT | RSHIFT, /// ditto 
    77         ALT   = LALT | RALT, /// ditto 
    78         META  = LMETA | RMETA /// ditto 
    79     };   
    80  
    8160    protected Texture textTexture;  // texture that constains rendered text image. 
    8261     
     
    8867    protected bool mouseIn;         // used to track mouseover/mouseout 
    8968    protected bool mouseMoved;      // used for click() event. 
     69     
    9070    protected bool resizeDirty = true; 
    9171    protected bool textDirty = true; 
     
    155135    } 
    156136 
     137     
     138     
     139     
    157140    /** 
    158141     * Find the surface at the given coordinates. 
     
    166149     *         the children will not be searched. 
    167150     * Returns: The surface at the coordinates (may be self), or null if coordinates are outside of this surface. */ 
    168     Surface findSurface(float x, float y, bool useMouseChildren=true) 
     151    Surface findSurface(Vec2i xy, bool useMouseChildren=true) 
    169152    {    
    170         // Search children 
    171         if (useMouseChildren && mouseChildren) 
    172         {   // Sort if necessary 
    173             if (!children.sorted(false, (Surface s){return s.style.zIndex;})) 
    174                 children.radixSort(false, (Surface s){return s.style.zIndex;}); 
    175              
    176             foreach(surf; children) 
    177             {   Surface result = surf.findSurface(x, y); 
    178                 if (result) 
    179                     return result; 
    180             } 
    181         } 
    182          
    183153        // Search self 
    184154        Vec2f[4] polygon; 
    185155        getPolygon(polygon); 
    186         if (Vec2f(x, y).inside(polygon)) 
     156        if (xy.toFloat().inside(polygon)) // TODO: This doesn't take clipping into account. 
     157        {    
     158            // Search children 
     159            if (useMouseChildren && mouseChildren) 
     160            {   // Sort if necessary 
     161                if (!children.sorted(false, (Surface s){return s.style.zIndex;})) 
     162                    children.radixSort(false, (Surface s){return s.style.zIndex;}); 
     163                 
     164                foreach(surf; children) 
     165                {   Surface result = surf.findSurface(xy); 
     166                    if (result) 
     167                        return result; 
     168            }   } 
     169             
    187170            return this; 
     171        } 
    188172         
    189173        return null;         
     
    238222    SurfaceGeometry getGeometry() 
    239223    {   return geometry;         
     224    } 
     225     
     226    /** 
     227     * Get a 4-sided polygon of the outline of this surface, after all styles and the transformation are applied. 
     228     * Coordinates are relative to the parent Surface. 
     229     * Params: 
     230     *     polygon = A pointer to a Vec2f[4] where the result will be stored and returned.   
     231     *         If null, new memory will be allocated. */ 
     232    public Vec2f[] getPolygon(in Vec2f[] polygon=null) 
     233    {   if (polygon.length < 4) 
     234            polygon = new Vec2f[4]; 
     235        polygon[0] = localToParent(Vec2f(0));           // top left 
     236        polygon[1] = localToParent(Vec2f(size.x, 0));   // top right 
     237        polygon[2] = localToParent(size);               // bottom right 
     238        polygon[3] = localToParent(Vec2f(0, size.y));   // bottom left 
     239        return polygon; 
    240240    } 
    241241     
     
    255255        {   SDL_WM_GrabInput(SDL_GRAB_OFF);          
    256256            grabbedSurface = null; 
     257            SDL_WarpMouse(mouseX+cast(int)offsetAbsolute.x, mouseY+cast(int)offsetAbsolute.y); 
    257258        }        
    258259    } 
     
    261262    } 
    262263     
    263     /// 
    264     public char[] innerHtml() 
     264    /** 
     265     * Get and set the html text displayed inside this Surface. */ 
     266    public char[] getHtml() 
    265267    {   return textBlock.getHtml(); 
    266268    } 
    267269    /// ditto 
    268     public void innerHtml(char[] html) 
    269     {   //this.text = html; 
    270         textDirty = true; 
    271         Style cs = getComputedStyle(); 
    272         textBlock.update(html, cs, cast(int)width(), cast(int)height()); 
     270    public void setHtml(char[] html) 
     271    {   textDirty = true; 
     272        textBlock.setHtml(html); 
     273    } 
     274     
     275    /** 
     276     * Convert between global, parent, and local coordinate systems. 
     277     * style.transform is taken into account. 
     278     * Params: 
     279     *     xy = Coordinates relative to the left side of the function name. 
     280     * Returns: Coordinates relative to the right side of the function name. */ 
     281    Vec2f localToParent(Vec2f xy) 
     282    {   if (parent) /// TODO: innerXy 
     283            return xy.vec3f.transform(style.transform).vec2f + offset; 
     284        return xy; 
     285    } 
     286    Vec2f localToGlobal(Vec2f xy) /// ditto 
     287    {   if (parent) 
     288            return localToGlobal(localToParent(xy));  // untested 
     289        return xy; 
     290    } 
     291    Vec2f parentToLocal(Vec2f xy) /// ditto 
     292    {   Vec2f parentSize = Vec2f(parentWidth(), parentHeight()); 
     293        Vec2f extra = Vec2f( 
     294            style.borderLeftWidth.toPx(parentSize.x, false) + style.paddingLeft.toPx(parentSize.x, false), 
     295            style.borderTopWidth.toPx(parentSize.y, false) + style.paddingTop.toPx(parentSize.y, false)); 
     296        Vec2f innerXy = xy - offset - extra; 
     297         
     298        if (parent) 
     299        {   if (style.transform == Matrix.IDENTITY) 
     300                return innerXy; // simple common case 
     301            return innerXy.vec3f.transform(style.transform.inverse()).vec2f; // TODO: this fails if there's non-z rotation 
     302        } 
     303        return innerXy; 
     304    } 
     305    Vec2f globalToLocal(Vec2f xy) /// ditto 
     306    {   if (parent) 
     307            return parentToLocal(parent.globalToLocal(xy)); // untested 
     308        return xy; 
    273309    } 
    274310     
     
    360396     * Update all of this Surface's dimensions, geometry, and children to prepare it for rendering. */ 
    361397    void update() 
    362     { 
     398    {   
    363399        Style cs = getComputedStyle(); 
    364400        updateDimensions(cs); 
     
    380416            int height = cast(int)height(); 
    381417             
    382             //textBlock.update(text, cs, width, height); 
    383             //textBlock.update(text, cs, width, height); 
    384             textBlock.update(textBlock.getHtml(), cs, width, height); 
    385             Image textImage = textBlock.render(true, editable && focusSurface is this ? &textCursor : null); // TODO: Change true to Probe.NextPow2 
    386              
    387             if (textImage) 
    388             {   if (!textTexture) // create texture on first go 
    389                     textTexture = new Texture(textImage, Texture.Format.AUTO, false, "Surface Text", true); 
    390                 else 
    391                     textTexture.setImage(textImage); 
    392                 textTexture.padding = Vec2i(nextPow2(width)-width, -(nextPow2(height)-height)); 
    393             } else 
    394                 textTexture = null; 
     418            if (textBlock.update(cs, width, height)) 
     419            {   Image textImage = textBlock.render(true, editable && focusSurface is this ? &textCursor : null); // TODO: Change true to Probe.NextPow2 
     420                 
     421                if (textImage) 
     422                {   if (!textTexture) // create texture on first go 
     423                        textTexture = new Texture(textImage, Texture.Format.AUTO, false, "Surface Text", true); 
     424                    else 
     425                        textTexture.setImage(textImage); 
     426                    textTexture.padding = Vec2i(nextPow2(width)-width, -(nextPow2(height)-height)); 
     427                } else 
     428                    textTexture = null; 
     429            } 
    395430             
    396431            textDirty = false; 
     
    438473    } 
    439474     
    440     /// 
    441     void click(byte buttons, Vec2i coordinates, bool allowFocus=true) 
    442     {   bool propagate = true; 
     475    /**  
     476     * Trigger a click event and call the onClick callback function if set.  
     477     * Click events occur after a mouseDown and mouseUp when the mouse hasn't moved between the two. 
     478     * If the onClick function is not set, or if it returns true (propagate), call the parent's click function.  
     479     * Params: 
     480     *     button =  
     481     *     coordinates = Coordinates relative to the Surface, with style.transform taken into account. 
     482     *     allowFocus = Used internally to prevent focus from propagating updward */ 
     483    void click(Input.MouseButton button, Vec2i coordinates, bool allowFocus=true) 
     484    {   bool propagate = true; // TODO: Should coordinates be relative to inner area? 
    443485        if(onClick) 
    444             propagate = onClick(this, buttons, coordinates); 
    445         if (allowFocus && Surface.focusSurface !is this) 
    446             focus(); // give focus on click if not already focussed. 
     486            propagate = onClick(this, button, coordinates); 
     487        if (editable) 
     488        {   if (allowFocus && Surface.focusSurface !is this) 
     489                focus(); // give focus on click if editable not already focussed. 
     490            textCursor.position = textBlock.xyToCursor(coordinates); 
     491            textDirty = true; // redraw 
     492        } 
     493         
    447494        if(parent && propagate)  
    448             parent.click(buttons, coordinates, false); 
    449     } 
    450  
    451      
     495            parent.click(button, coordinates, false); 
     496    } 
     497 
    452498    /** 
    453499     * Trigger a keyDown event and call the onKeyDown callback function if set.  
    454      * If the onKeyDown function is not set, call the parent's keyDown function.  
     500     * If the onKeyDown function is not set, or if it returns true (propagate), call the parent's keyDown function.  
    455501     * Params: 
    456502     *     key = SDL's key code of the pressed key. 
    457503     *     mod = Modifier key held down while key was pressed.*/  
    458     void keyDown(int key, int mod=ModifierKey.NONE) 
     504    void keyDown(int key, int mod=Input.ModifierKey.NONE) 
    459505    {   bool propagate = true; 
    460506        if(onKeyDown) 
     
    466512    /** 
    467513     * Trigger a keyUp event and call the onKeyUp callback function if set.  
    468      * If the onKeyUp function is not set, call the parent's keyUp function. 
     514     * If the onKeyUp function is not set, or if it returns true (propagate), call the parent's keyUp function. 
    469515     * Params: 
    470516     *     key = SDL's key code of the pressed key. 
    471517     *     mod = Modifier key held down while key was pressed.*/  
    472     void keyUp(int key, int mod=ModifierKey.NONE) 
     518    void keyUp(int key, int mod=Input.ModifierKey.NONE) 
    473519    {   bool propagate = true; 
    474520        if(onKeyUp) 
     
    479525     
    480526    /** 
    481      * Trigger a keyPress event and call the onKeyPress callback function if set.  
    482      * If the onKeyPress function is not set, call the parent's keyPress function. 
     527     * Trigger a keyPress event and call the onKeyPress callback function if set. 
     528     * Keypress events occur after a keyDown event and reoccur at Input's key repeat rates until after a keyUp occurs. 
     529     * If the onKeyPress function is not set, or if it returns true (propagate), call the parent's keyPress function. 
     530     * If editable is true, the TextBlock of this Surface will receive the input. 
    483531     * Params: 
    484532     *     key = SDL's key code of the pressed key. 
    485533     *     mod = Modifier key held down while key was pressed. 
    486534     *     unicode = unicode value of pressed key. */  
    487     void keyPress(int key, int mod=ModifierKey.NONE, dchar unicode=0) 
    488     {    
     535    void keyPress(int key, int mod=Input.ModifierKey.NONE, dchar unicode=0) { 
    489536        bool propagate = true; 
    490537        if(onKeyPress) 
    491             propagate = onKeyPress(this, key, mod);      
    492         if (propagate) 
    493         {   if (editable) 
    494             {    
    495                 textBlock.input(key, mod, unicode, textCursor, getComputedStyle()); 
    496                 textDirty = true; 
    497             } 
    498             if(parent)  
    499                 parent.keyPress(key, mod); 
    500         } 
     538            propagate = onKeyPress(this, key, mod);          
     539        if (editable) 
     540        {   textBlock.input(key, mod, unicode, textCursor); 
     541            textDirty = true; 
     542        } 
     543        if(parent && propagate)  
     544            parent.keyPress(key, mod); 
    501545    } 
    502546 
    503547    /** 
    504548     * Trigger a mouseDown event and call the onMouseDown callback function if set.  
    505      * If the onMouseDown function is not set, call the parent's mouseDown function.*/  
    506     void mouseDown(byte buttons, Vec2i coordinates){  
     549     * If the onMouseDown function is not set, or if it returns true (propagate), call the parent's mouseDown function. 
     550     * Params: 
     551     *     button = Current state of the mouse buttons 
     552     *     coordinates = Coordinates relative to the Surface, with style.transform taken into account. */ 
     553    void mouseDown(Input.MouseButton button, Vec2i coordinates) {  
    507554        mouseMoved = false; 
    508555        bool propagate = true; 
    509556        if(onMouseDown) 
    510             propagate = onMouseDown(this, buttons, coordinates); 
     557            propagate = onMouseDown(this, button, coordinates); 
    511558        if(parent && propagate)  
    512             parent.mouseDown(buttons, coordinates); 
     559            parent.mouseDown(button, coordinates); 
    513560    } 
    514561     
    515562    /** 
    516563     * Trigger a mouseUp event and call the onMouseUp callback function if set.  
    517      * If the onMouseUp function is not set, call the parent's mouseUp function.*/  
    518     void mouseUp(byte buttons, Vec2i coordinates){  
     564     * If the onMouseUp function is not set, or if it returns true (propagate), call the parent's mouseUp function. 
     565     * Params: 
     566     *     button = Current state of the mouse buttons 
     567     *     coordinates = Coordinates relative to the Surface, with style.transform taken into account. */ 
     568    void mouseUp(Input.MouseButton button, Vec2i coordinates) {  
    519569        bool propagate = true; 
    520570        if(onMouseUp) 
    521             propagate = onMouseUp(this, buttons, coordinates); 
     571            propagate = onMouseUp(this, button, coordinates); 
    522572        if (!mouseMoved) // trigger the click event if the mouse button went down and up without the mouse moving. 
    523             click(buttons, coordinates);       
     573            click(button, coordinates);        
    524574        if(parent && propagate)  
    525             parent.mouseUp(buttons, coordinates); 
    526     } 
    527      
    528     /** 
    529      * Trigger a mouseMove event and call the onMouseMove callback function if set. */  
    530     void mouseMove(byte buttons, Vec2i amount){ 
     575            parent.mouseUp(button, coordinates); 
     576    } 
     577     
     578    /** 
     579     * Trigger a mouseMove event and call the onMouseMove callback function if set.  
     580     * If the onMouseMove function is not set, or if it returns true (propagate), call the parent's mouseMove function.*/  
     581    void mouseMove(Vec2i amount) { 
    531582        mouseMoved = true; 
    532583        bool propagate = true; 
    533584        if(onMouseMove) 
    534             propagate = onMouseMove(this, buttons, amount); 
     585            propagate = onMouseMove(this, amount); 
    535586        if(parent && propagate)  
    536             parent.mouseMove(buttons, amount); 
    537     } 
    538  
    539     /** 
    540      * Trigger a mouseOver event and call the onMouseOver callback function if set. */  
    541     void mouseOver(byte buttons, Vec2i coordinates) { 
     587            parent.mouseMove(amount); 
     588    } 
     589 
     590    /** 
     591     * Trigger a mouseOver event and call the onMouseOver callback function if set.  
     592     * If the onMouseOver function is not set, or if it returns true (propagate), call the parent's mouseOver function.*/  
     593    void mouseOver() { 
    542594        if(!mouseIn) 
    543         {   bool propagate = true; 
    544                  
     595        {   bool propagate = true;               
    545596            mouseIn = true; 
    546597            if(onMouseOver)  
    547                 propagate = onMouseOver(this, buttons, coordinates); 
     598                propagate = onMouseOver(this); 
    548599            if(parent && propagate)  
    549                 parent.mouseOver(buttons, coordinates);      
    550         } 
    551     } 
    552  
    553     /** 
    554      * Trigger a mouseOut event and call the onMouseOut callback function if set */  
    555     void mouseOut(Surface next, byte buttons, Vec2i coordinates) 
     600                parent.mouseOver();      
     601        } 
     602    } 
     603 
     604    /** 
     605     * Trigger a mouseOut event and call the onMouseOut callback function if set  
     606     * If the onMouseOut function is not set, or if it returns true (propagate), call the parent's mouseOut function.*/   
     607    void mouseOut(Surface next) 
    556608    { 
    557609        if(mouseIn) 
    558         {   bool propagate = true; 
     610        {    
    559611            if(isChild(next)) 
    560                 return; 
    561             else 
    562             mouseIn = false; 
    563                if(onMouseOut) 
    564                    propagate = onMouseOut(this, buttons, coordinates);          
    565                 if(next !is parent && parent && propagate) 
    566                    parent.mouseOut(next, buttons, coordinates); 
    567             } 
     612                return; // Don't do anything if mouseOut occurs when going into a child. 
     613             
     614            mouseIn = false; 
     615            bool propagate = true; 
     616            if(onMouseOut) 
     617                propagate = onMouseOut(this);           
     618            if(next !is parent && parent && propagate) 
     619               parent.mouseOut(next); 
    568620        } 
    569621    } 
     
    608660    } 
    609661 
    610      
    611     /* 
    612      * Get a 4-sided polygon of the outline of this surface, after all styles and the transformation are applied. 
    613      * Coordinates are relative to the parent Surface. 
    614      * Params: 
    615      *     polygon = A pointer to a Vec2f[4] where the result will be stored. */ 
    616     protected Vec2f[] getPolygon(in Vec2f[] polygon=null) 
    617     {   if (polygon.length < 4) 
    618             polygon = new Vec2f[4]; 
    619         polygon[0] = Vec3f(0).transform(style.transform).vec2f + offset;            // top left 
    620         polygon[1] = Vec3f(size.x, 0, 0).transform(style.transform).vec2f + offset; // top right 
    621         polygon[2] = size.vec3f.transform(style.transform).vec2f + offset;          // bottom right 
    622         polygon[3] = Vec3f(0, size.y, 0).transform(style.transform).vec2f + offset; // bottom left 
    623         return polygon; 
    624     } 
    625      
    626      
    627662    // Set style dimensions from pixels. 
    628663    protected void top(float v)     
  • trunk/src/yage/gui/textblock.d

    r200 r201  
    4545/** 
    4646 * Render text and simple html with styles to an image. 
    47  * This class is used internally by the engine.  In most cases it shouldn't need to be called externally.. */ 
     47 * TextBlock has two modes of input:  setHtml() and the input() function that receives keypresses. 
     48 * This class is used internally by the engine.  In most cases it shouldn't need to be called externally. */ 
    4849struct TextBlock 
    4950{ 
     
    5455     
    5556    private char[] html; 
     57    private bool htmlDirty = true;      // html has changed, letters may be out of date 
     58    private bool lettersDirty = false;  // letters have chagned via input, html may be out of date 
    5659    package InlineStyle style; // base style of entire text block 
    5760    private Style.TextAlign alignment; 
     
    9194         * Get the cursor position from an xy pixel position. */ 
    9295        int xyToCursor(Vec2i xy) 
    93         {    
     96        {   if (!lines.length) 
     97                return 0; 
     98             
    9499            // Calculate line 
    95             int line; 
    96             for (; line<lines.length-1 && 0 <= xy.y; line++) 
    97                 xy.y -= lines[line].height; 
     100            int line, y; 
     101            while(true) 
     102            {   y += lines[line].height; 
     103                if (line>=lines.length-1 || y >= xy.y) 
     104                    break; 
     105                line++; 
     106            } 
    98107             
    99108            // Take alignment into account 
     
    105114            for (; position<lines[line].letters.length && 0 < xy.x; position++) 
    106115                xy.x -= lines[line].letters[position].advanceX; 
     116             
     117            /* 
     118            int position, x; 
     119            while(true) 
     120            {   y += lines[line].letters[position].advanceX; 
     121                if (position>=lines[line].letters.length-1 || x > xy.x) 
     122                    break; 
     123                position++; 
     124            } 
     125             */ 
    107126             
    108127            return lineToCursor(line, position); 
     
    144163    } 
    145164     
    146     /// 
    147     char[] getHtml() 
    148     {   return html; 
     165    /** 
     166     * Reverse the normal function of TextLayout and convert letters[] back to a string of html text. 
     167     * The text may use different tags than the original html, (since some information is lost)  
     168     * but will be functionally the same.  */ 
     169    public char[] getHtml() 
     170    { 
     171        if (!lettersDirty) 
     172            return html; 
     173         
     174        // If the user has typed text, regenerate html from scratch 
     175        html = "<span>"; 
     176        InlineStyle style = this.style;      
     177        InlineStyle currentStyle = style; // style of the previous letter 
     178         
     179        foreach(i, l; letters.data) 
     180        { 
     181            InlineStyle* newStyle = cast(InlineStyle*) l.extra; 
     182            if (currentStyle != *newStyle) // if style has changed since last letter 
     183            {    
     184                char[][] styleString;            
     185                 
     186                // TODO: Would it be better to create arrays to group sequential letters of the same style? 
     187                // then we could make a single xml node to contain those that have similar styles. 
     188                // If nothing else, the first version of this function could just return unformatted text. 
     189                // Should text be stored lazily so we don't have to recreate this all on every keypress? 
     190                 
     191                // Only print styles that don't match the Surface's style 
     192                if (style.fontFamily && style.fontFamily != newStyle.fontFamily) 
     193                    styleString ~= swritef(`font-family: url('%s')`, newStyle.fontFamily); 
     194                if (dword(style.fontSize) != dword(newStyle.fontSize)) 
     195                    styleString ~= swritef(`font-size: %spx`, newStyle.fontSize); 
     196                if (style.color != newStyle.color) 
     197                    styleString ~= swritef(`color: %spx`, newStyle.color); 
     198                if (style.fontWeight != newStyle.fontWeight) 
     199                    styleString ~= swritef(`font-weight: %s`, Style.enumToString(newStyle.fontWeight)); 
     200                if (style.fontStyle != newStyle.fontStyle) 
     201                    styleString ~= swritef(`font-style: %s`, Style.enumToString(newStyle.fontStyle)); 
     202                if (style.textDecoration != newStyle.textDecoration)  
     203                    styleString ~= swritef(`text-decoration: %s`, Style.enumToString(newStyle.textDecoration)); 
     204                if (dword(style.lineHeight) != dword(newStyle.lineHeight)) 
     205                    styleString ~= swritef(`lineHeight: %spx`, newStyle.lineHeight); 
     206                if (dword(style.letterSpacing) != dword(newStyle.letterSpacing))  
     207                    styleString ~= swritef(`letterSpacing: %spx`, newStyle.letterSpacing);               
     208                 
     209                html ~= swritef(`</span><span style="%s">`, join(styleString, "; ")); 
     210                currentStyle = *newStyle; 
     211            } 
     212            switch (l.letter) 
     213            {   case '>': html ~= "\&gt;"; break; 
     214                case '<': html ~= "\&lt;"; break; 
     215                case '&': html ~= "\&amp;"; break; 
     216                case '\n': html ~= "<br/>"; break; 
     217                case ' ':  
     218                    if (i>0 && letters[i-1].letter ==' ') 
     219                    {   html ~= "\&nbsp;"; // encode multiple spaces as " &nbsp;"  
     220                        break; 
     221                    } // fall through: 
     222                default: 
     223                    html ~= l.toString(); 
     224            } 
     225        } 
     226         html ~= "</span>"; 
     227        return html; 
    149228    } 
    150229     
     
    156235     *     unicode = Unicode value of the pressed key. 
    157236     *     cursor = The Surface's TextCursor. */ 
    158     void input(int key, int mod, dchar unicode, inout TextCursor cursor, Style computedStyle
     237    void input(int key, int mod, dchar unicode, inout TextCursor cursor
    159238    {    
    160         style = InlineStyle(computedStyle); 
    161         //Log.trace(cursor.position, " " , letters.length); 
     239         
     240        // If the html has changed we need to update the lines before continuing. 
     241        if (htmlDirty) 
     242        {   letters.length = 0;          
     243            styles.length = 0; 
     244            HtmlParser.htmlToLetters(html, style, letters, styles);  
     245            lines = lettersToLines(letters.data, width, lines); 
     246            htmlDirty = false; 
     247        } 
     248        lettersDirty = true; 
     249         
    162250        assert(cursor.position <= letters.length); 
    163251        static int xPosition; // save the cursor's x position when moving from one line to the next. 
     
    185273                    int x = xPosition = xPosition ? xPosition : cursorToXy(cursor.position).x; 
    186274                    x -= Line.getOffset(newLine.width, width, alignment); 
    187                     for (; i<newLine.letters.length && x >=0; i++) 
     275                    for (; i<newLine.letters.length && x>=0; i++) 
    188276                        x-= newLine.letters[i].advanceX; // find the cursor position for the previous x position 
    189                     cursor.position = lineToCursor(currentLine-1, i-1); 
     277                    cursor.position = lineToCursor(currentLine-1, max(i-1, 0)); 
    190278                } 
    191279                break; 
     
    196284                    int x = xPosition = xPosition ? xPosition : cursorToXy(cursor.position).x;   
    197285                    x -= Line.getOffset(newLine.width, width, alignment); 
    198                     for (; i<newLine.letters.length && x >=0; i++) 
     286                    for (; i<newLine.letters.length && x>=0; i++) 
    199287                        x-= newLine.letters[i].advanceX; 
    200                     cursor.position = lineToCursor(currentLine+1, i-1); 
     288                    //if (currentLine <lines.length-1) 
     289                    //  i--; // if not the last line, go back one before the character that causes the new line. 
     290                    cursor.position = lineToCursor(currentLine+1, max(i-1, 0)); 
    201291                } 
    202292                break; 
     
    204294                cursor.position -= linePosition; 
    205295                break; 
    206             case SDLK_END:  
    207                 auto letters = lines[currentLine].letters; 
    208                 int newPosition = (cast(int)letters.length) - linePosition; 
    209                 if (currentLine != lines.length-1)  
    210                     newPosition--; // if not the last line, go back one before the character that causes the new line. 
    211                 cursor.position += newPosition; 
     296            case SDLK_END: 
     297                if (lines.length) 
     298                {   auto letters = lines[currentLine].letters; 
     299                    int newPosition = (cast(int)letters.length) - linePosition; 
     300                    if (currentLine != lines.length-1)  
     301                        newPosition--; // if not the last line, go back one before the character that causes the new line. 
     302                    cursor.position += newPosition; 
     303                } 
    212304                break; 
    213305             
     
    230322                    if (unicode=='\r') 
    231323                        unicode='\n'; 
    232                     //Log.trace(cast(int)unicode); 
    233324                     
    234325                    // Get the style to use for a new letter 
     
    248339            break; 
    249340             
    250              
    251             // TODO: tabs, center cursor position, end crashes on centered textblock, click to position cursor, selection, ctrl+a, z, x, c, v 
     341            // TODO: tabs, click to position cursor, selection, ctrl+a, z, x, c, v 
    252342        } 
    253343         
    254344        lines = lettersToLines(letters.data, width, lines); 
    255         //Log.trace(lines.length); 
    256345    } 
    257346     
     
    312401                    result.overlayAndColor(letter.image, istyle.color, x+letter.left, baseline-letter.top); 
    313402                     
    314                     // Render underline, overline, and linethrough 
     403                    // Render underline, overline, and line-through 
    315404                    if (istyle.textDecoration == Style.TextDecoration.UNDERLINE) 
    316405                        for (int h=max(0, baseline); h<min(baseline+lineWidth, height); h++) 
     
    357446     
    358447    /** 
    359      * Reverse the normal function of TextLayout and convert letters[] back to a string of html text. 
    360      * The text may use different tags than the original html, (since some information is lost)  
    361      * but will be functionally the same.  */ 
    362     char[] toString() 
    363     { 
    364         char[] result = "<span>"; 
    365         InlineStyle style = this.style;      
    366         InlineStyle currentStyle = style; // style of the previous letter 
    367          
    368         foreach(Letter l; letters) 
    369         { 
    370             InlineStyle* newStyle = cast(InlineStyle*) l.extra; 
    371             if (currentStyle != *newStyle) // if style has changed since last letter 
    372             {    
    373                 char[][] styleString;            
    374                  
    375                 // TODO: Would it be better to create arrays to group sequential letters of the same style? 
    376                 // then we could make a single xml node to contain those that have similar styles. 
    377                 // If nothing else, the first version of this function could just return unformatted text. 
    378                 // Should text be stored lazily so we don't have to recreate this all on every keypress? 
    379                  
    380                 // Only print styles that don't match the Surface's style 
    381                 if (style.fontFamily && style.fontFamily != newStyle.fontFamily) 
    382                     styleString ~= swritef(`font-family: url('%s')`, newStyle.fontFamily); 
    383                 if (dword(style.fontSize) != dword(newStyle.fontSize)) 
    384                     styleString ~= swritef(`font-size: %spx`, newStyle.fontSize); 
    385                 if (style.color != newStyle.color) 
    386                     styleString ~= swritef(`color: %spx`, newStyle.color); 
    387                 if (style.fontWeight != newStyle.fontWeight) 
    388                     styleString ~= swritef(`font-weight: %s`, Style.enumToString(newStyle.fontWeight)); 
    389                 if (style.fontStyle != newStyle.fontStyle) 
    390                     styleString ~= swritef(`font-style: %s`, Style.enumToString(newStyle.fontStyle)); 
    391                 if (style.textDecoration != newStyle.textDecoration)  
    392                     styleString ~= swritef(`text-decoration: %s`, Style.enumToString(newStyle.textDecoration)); 
    393                 if (dword(style.lineHeight) != dword(newStyle.lineHeight)) 
    394                     styleString ~= swritef(`lineHeight: %spx`, newStyle.lineHeight); 
    395                 if (dword(style.letterSpacing) != dword(newStyle.letterSpacing))  
    396                     styleString ~= swritef(`letterSpacing: %spx`, newStyle.letterSpacing);               
    397                  
    398                 result ~= swritef(`</span><span style="%s">`, join(styleString, "; ")); 
    399                 currentStyle = *newStyle; 
    400             } 
    401             result ~= l.toString(); 
    402         } 
    403          
    404         return result ~ "</span>"; 
    405     } 
    406  
    407     /** 
    408      * Replace the text with new text, rebuilding the internal lines and letters data structures. 
    409      * This is unlike input(), which modifies the text based on a single keystroke. 
     448     * Set the contents of the Textblock with html. 
    410449     * Params: 
    411      *     text = String of utf-8 encoded html text to render. 
     450     *     html = String of utf-8 encoded html text to render. 
    412451     *       The following html tags are supported:<br>  
    413452     *          a, b, br, del, i, span, sub, sup, u <br> 
     
    415454     *         color, font-family, font-size[%|px], font-style[normal|italic|oblique], font-weight[normal|bold], 
    416455     *         letter-spacing[%|px], line-height[%|px],  
    417      *         text-align[left|center|right] text-decoration[none|underline|overline|line-through] 
     456     *         text-align[left|center|right] text-decoration[none|underline|overline|line-through] */ 
     457    void setHtml(char[] html) 
     458    {   this.html = html; 
     459        htmlDirty = true; 
     460        lettersDirty = false; 
     461    } 
     462 
     463    /** 
     464     * Replace the text with new text, rebuilding the internal lines and letters data structures. 
     465     * This is unlike input(), which modifies the text based on a single keystroke. 
     466     * Params: 
    418467     *     style = A style with fontSize and lineHeight in terms of pixels 
    419468     *     width = Available width for rendering text 
     
    421470     * Returns: True if the text will need to be re-rendered, false otherwise. 
    422471     * TODO: Should this be a constructor to maintain RAII? */ 
    423     bool update(char[] html, Style style, int width, int height) 
     472    bool update(Style style, int width, int height) 
    424473    { 
    425474        InlineStyle istyle = InlineStyle(style); 
    426475        alignment = style.textAlign; 
    427476         
    428         // If text has changed 
    429         //bool newLetters = text != (*this).text || istyle != (*this).style; 
    430         bool newLetters = html != this.html || istyle != this.style; 
     477        // If letters have changed 
     478        if (lettersDirty) // recreate html from letters. 
     479            html = getHtml(); 
     480         
     481         // Reparse the arrays of letters and styles from the html 
     482        bool newLetters = htmlDirty || istyle != this.style; 
    431483        if (newLetters) 
    432         {    
    433             // Update the arrays of letters and styles 
    434             letters.length = 0;          
     484        {   letters.length = 0;          
    435485            styles.length = 0; 
    436             HtmlParser.htmlToLetters(html, style, letters, styles); 
    437             
    438            this.html = html; 
    439            this.style = istyle; 
    440         } 
     486            HtmlParser.htmlToLetters(html, istyle, letters, styles);            
     487        } 
     488         
     489        this.style = istyle; 
     490        htmlDirty = false; 
    441491         
    442492        // If text or dimensions have changed 
    443493        if (newLetters || width != this.width || height != this.height) 
    444         {                
    445             this.width = width; 
     494        {   this.width = width; 
    446495            this.height = height;            
    447496            lines = lettersToLines(letters.data, width, lines); 
    448497            return true; 
    449498        } 
     499         
    450500        return false; 
    451501    } 
     
    631681     * Returns: 
    632682     */ 
    633     static void htmlToLetters(char[] htmlText, Style style, inout ArrayBuilder!(Letter) letters, inout ArrayBuilder!(InlineStyle) styles) 
     683    static void htmlToLetters(char[] htmlText, InlineStyle style, inout ArrayBuilder!(Letter) letters, inout ArrayBuilder!(InlineStyle) styles) 
    634684    { 
    635685        char[] lookaside = Memory.allocate!(char)(htmlText.length+13); // +13 for <span></span> that surrounds it 
     
    646696        } 
    647697 
    648         htmlNodeToLetters(doc.query.nodes[0], InlineStyle(style), letters, styles); 
     698        htmlNodeToLetters(doc.query.nodes[0], style, letters, styles); 
    649699    } 
    650700         
     
    687737        styles ~= style; 
    688738        if (input.value.length) 
    689         {   char[] text = htmlEntityDecode(input.value); 
     739        {   char[] text = entityDecode(input.value); 
    690740            foreach (dchar c; text) // dchar to iterate over each utf-8 char group 
    691741            {   if (style.fontFamily) 
     
    749799     * Note that this could avoid heap activity altogether with lookaside buffers. 
    750800     * See: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#XML_character_entity_references */  
    751     private static char[] htmlEntityDecode(char[] text) 
    752     {   text = text.substitute("&amp;", "&"); // TODO: fix garbage created by this function. 
     801    private static char[] entityDecode(char[] text) 
     802    {   // TODO: Perform this in one pass 
     803        text = text.substitute("&amp;", "&"); // TODO: fix garbage created by this function. 
    753804        text = text.substitute("&lt;", "<"); 
    754805        text = text.substitute("&gt;", ">"); 
     
    761812    {   char[] test = "<>Hello Goodbye&nbsp; &amp;&quot;&apos;&lt;&gt;"; 
    762813        char[] result="<>Hello Goodbye\&nbsp; &\"'<>"; 
    763         assert (htmlEntityDecode(test) == result); 
     814        assert (entityDecode(test) == result); 
    764815    } 
    765816} 
  • trunk/src/yage/system/graphics/render.d

    r188 r201  
    742742            // Bind surface properties   
    743743            glPushMatrix(); 
     744            glTranslatef(surface.left(), surface.top(), 0); 
    744745            glMultMatrixf(surface.style.transform.ptr); 
    745             glTranslatef(surface.left(), surface.top(), 0); 
    746746            surface.style.backfaceVisibility ? glDisable(GL_CULL_FACE) : glEnable(GL_CULL_FACE); 
    747747             
     748            // Render the surface 
    748749            geometry(surface.getGeometry()); 
    749750             
  • trunk/src/yage/system/input.d

    r200 r201  
    2323abstract class Input 
    2424{ 
     25    /// This is a mirror of SDLMod (SDL's modifier key struct) 
     26    enum ModifierKey 
     27    {   NONE  = 0x0000, /// Allowed values. 
     28        LSHIFT= 0x0001, /// ditto 
     29        RSHIFT= 0x0002, /// ditto 
     30        LCTRL = 0x0040, /// ditto 
     31        RCTRL = 0x0080, /// ditto 
     32        LALT  = 0x0100, /// ditto 
     33        RALT  = 0x0200, /// ditto 
     34        LMETA = 0x0400, /// ditto 
     35        RMETA = 0x0800, /// ditto 
     36        NUM   = 0x1000, /// ditto 
     37        CAPS  = 0x2000, /// ditto 
     38        MODE  = 0x4000, /// ditto 
     39        RESERVED = 0x8000, /// ditto 
     40        CTRL  = LCTRL | RCTRL, /// ditto 
     41        SHIFT = LSHIFT | RSHIFT, /// ditto 
     42        ALT   = LALT | RALT, /// ditto 
     43        META  = LMETA | RMETA /// ditto 
     44    };   
     45     
     46    /// 
     47    enum MouseButton 
     48    {   LEFT=1, /// Allowed values. 
     49        CENTER, /// ditto 
     50        RIGHT, /// ditto 
     51        WHEEL_UP, /// ditto 
     52        WHEEL_DOWN, /// ditto 
     53        FOUR, /// ditto 
     54        FIVE, /// ditto 
     55        SIX, /// ditto 
     56        SEVEN, /// ditto 
     57        EIGHT /// ditto 
     58    } 
     59     
    2560    public static int KEY_DELAY = 500; /// milliseconds before repeating a call to keyPress after holding a key down. 
    2661    public static int KEY_REPEAT = 30; /// milliseconds before subsequent calls to keyPress after KEY_DELAY occurs. 
    2762     
    28     protected static Vec2i mouse; // The current pixel location of the mouse cursor; (0, 0) is top left. 
     63    /// Position of the mouse and state of each button. 
     64    struct Mouse 
     65    {   Vec2i position; /// 
     66        bool left; /// ditto 
     67        bool center; /// ditto 
     68        bool right; /// ditto 
     69        bool four; /// ditto 
     70        bool five; /// ditto 
     71        bool six; /// ditto 
     72        bool seven; /// ditto 
     73        bool eight; /// ditto 
     74 
     75        /// 
     76        char[] toString() 
     77        {   return position.toString() ~ (left?"L":"") ~ (center?"C":"") ~ (right?"R":"") ~ (four?"4":"") ~  
     78                (five?"5":"") ~ (six?"6":"") ~ (seven?"7":"") ~ (eight?"8":""); 
     79        } 
     80         
     81        // used internally 
     82        private void fromMouseButton(ushort mouseButton, bool state) 
     83        {   switch (mouseButton) 
     84            {   case MouseButton.LEFT: left=state; break; 
     85                case MouseButton.CENTER: center=state; break; 
     86                case MouseButton.RIGHT: right=state; break; 
     87                case MouseButton.FOUR: four=state; break; 
     88                case MouseButton.FIVE: five=state; break; 
     89                case MouseButton.SIX: six=state; break; 
     90                case MouseButton.SEVEN: seven=state; break; 
     91                case MouseButton.EIGHT: eight=state; break; 
     92                default: break; 
     93        }   } 
     94         
     95    } 
     96    static Mouse mouse; /// Stores the current state of the mouse. 
     97     
     98    //protected static ubyte mouseButtons; /// A bitmask of MouseButton for the current state of the mouse buttons. 
     99     
     100    //protected static Vec2i mouse; // The current pixel location of the mouse cursor; (0, 0) is top left. 
    29101    protected static Surface currentSurface;    // Surface that the mouse is currently over 
    30102 
     
    37109    static void processAndSendTo(Surface surface) 
    38110    { 
     111        assert(surface); 
    39112         
    40113        SDL_EnableUNICODE(1); 
     
    48121                // Keyboard 
    49122                case SDL_KEYDOWN: 
    50                     if(focus) // keysym.sym gets all keys on the keyboard, including separate keys for numpad, keysym.unicde should be reserved for text. 
    51                     {   focus.keyDown(event.key.keysym.sym, event.key.keysym.mod); 
    52123                     
    53                         // Kepress will be called with the key repeat settings. 
    54                         focus.keyPress(event.key.keysym.sym, event.key.keysym.mod, event.key.keysym.unicode); 
    55                         lastKeyDown = event.key.keysym; 
    56                         lastKeyDownTime = clock()*1000 / CLOCKS_PER_SEC; 
    57                     }   
     124                    // keysym.sym gets all keys on the keyboard, including separate keys for numpad, keysym.unicde should be reserved for text. 
     125                    focus.keyDown(event.key.keysym.sym, event.key.keysym.mod); 
     126                 
     127                    // Kepress will be called with the key repeat settings. 
     128                    focus.keyPress(event.key.keysym.sym, event.key.keysym.mod, event.key.keysym.unicode); 
     129                    lastKeyDown = event.key.keysym; 
     130                    lastKeyDownTime = clock()*1000 / CLOCKS_PER_SEC; 
     131                       
    58132                    break; 
    59133                case SDL_KEYUP:                  
    60                     if(focus) 
    61                         focus.keyUp(event.key.keysym.sym, event.key.keysym.mod); 
     134                    focus.keyUp(event.key.keysym.sym, event.key.keysym.mod); 
    62135                    if (event.key.keysym.sym==lastKeyDown.sym) // if the same key we're repeating 
    63136                        lastKeyDownTime = uint.max; // stop repeating 
     
    66139                // Mouse 
    67140                case SDL_MOUSEBUTTONDOWN: 
     141                    mouse.fromMouseButton(event.button.button, true); // set                     
    68142                    auto over = getMouseSurface(surface); 
    69143                    if(over)  
    70                         over.mouseDown(event.button.button, mouse); 
     144                    {    
     145                        Vec2i localMouse = currentSurface.parentToLocal(mouse.position.toFloat()).toInt(); 
     146                        over.mouseDown(cast(MouseButton)event.button.button, localMouse); 
     147                    } 
    71148     
    72149                    break; 
    73150                case SDL_MOUSEBUTTONUP: 
     151                    mouse.fromMouseButton(event.button.button, false); // clear 
    74152                    auto over = getMouseSurface(surface); 
    75153                    if(over) 
    76                         over.mouseUp(event.button.button, mouse); 
     154                    {   Vec2i localMouse = currentSurface.globalToLocal(mouse.position.toFloat()).toInt(); 
     155                        over.mouseUp(cast(MouseButton)event.button.button, localMouse); 
     156                    } 
    77157     
    78158                    break; 
    79159                case SDL_MOUSEMOTION:            
    80                     mouse.x = event.motion.x; 
    81                     mouse.y = event.motion.y; 
     160                    mouse.position.x = event.motion.x; 
     161                    mouse.position.y = event.motion.y; 
    82162                     
    83163                    // Doing this before getMouseSurface() fixes the mouse leaving the surface while dragging. 
    84164                    if(currentSurface) 
    85                         currentSurface.mouseMove(event.button.button, Vec2i(event.motion.xrel, event.motion.yrel)); 
     165                    {   if (currentSurface !is Surface.getGrabbedSurface()) 
     166                        {   Vec2i localMouse = currentSurface.globalToLocal(mouse.position.toFloat()).toInt(); 
     167                            currentSurface.mouseX = localMouse.x;                        
     168                            currentSurface.mouseY = localMouse.y; 
     169                        } 
     170                        currentSurface.mouseMove(Vec2i(event.motion.xrel, event.motion.yrel)); 
     171                    } 
    86172     
    87173                    // If the surface that the mouse is in has changed 
     
    90176                    {    
    91177                        if(currentSurface) //Tell it that the mouse left 
    92                             currentSurface.mouseOut(over, event.button.button, mouse); 
    93                         if(over) //Tell it that the mouse entered 
    94                             over.mouseOver(event.button.button, mouse);                            
     178                            currentSurface.mouseOut(over); 
     179                        if(over) // Tell it that the mouse entered 
     180                            over.mouseOver();                          
    95181                         
    96182                        currentSurface = over; //The new current surface 
     
    116202        // Key repeat 
    117203        if (focus) 
    118         {        
    119             uint now = clock()*1000/CLOCKS_PER_SEC; // time in milliseconds 
     204        {   uint now = clock()*1000/CLOCKS_PER_SEC; // time in milliseconds 
    120205            if (now - KEY_DELAY > lastKeyDownTime) 
    121206            {   focus.keyPress(lastKeyDown.sym, lastKeyDown.mod, lastKeyDown.unicode); 
     
    130215        if(Surface.getGrabbedSurface())  
    131216            return Surface.getGrabbedSurface(); 
    132         return surface.findSurface(mouse.x, mouse.y); 
     217        return surface.findSurface(mouse.position); 
    133218    } 
    134219