Changeset 192

Show
Ignore:
Timestamp:
07/04/10 14:32:17 (2 years ago)
Author:
JoeCoder
Message:

Fixed a bug in ResourceManager?.texture that prevented textures from being cached (There was a missing opCmp for the hash.)
Renamed gui/textlayout.d to gui/textblock.d

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/build/buildyage.d

    r189 r192  
    1010 */ 
    1111 
    12 const char[] app = "demo1"; // set which program to build against yage. 
     12const char[] app = "demo2"; // set which program to build against yage. 
    1313//const char[] app = "tests/integration/main.d"; 
    1414 
  • trunk/src/yage/gui/all.d

    r153 r192  
    1313    import yage.gui.style; 
    1414    import yage.gui.surface; 
    15     import yage.gui.textlayout; 
     15    import yage.gui.surfacegeometry; 
     16    import yage.gui.textblock; 
    1617}  
  • trunk/src/yage/gui/style.d

    r191 r192  
    411411                 
    412412                default: 
    413                     throw new CSSException("Unsupported CSS Property: '{}'", property); 
     413                    throw new CSSException("Unsupported CSS Property: '%s'", property); 
    414414            } 
    415415        } 
  • trunk/src/yage/gui/surface.d

    r191 r192  
    77module yage.gui.surface; 
    88 
    9 import tango.io.Stdout; 
    109import tango.math.IEEE; 
    1110import tango.math.Math; 
     
    2221import yage.resource.material; 
    2322import yage.gui.style; 
    24 import yage.gui.textlayout
     23import yage.gui.textblock
    2524import yage.gui.surfacegeometry; 
    2625 
     
    3534    Style style; 
    3635     
    37     char[] text; 
    38     protected char[] oldText; 
    39     protected Texture textTexture;   
    40      
    41     bool editable = true; 
    42     bool mouseChildren = true; 
    43      
    44     TextLayout textLayout; 
    45  
     36    char[] text; /// This html text will be rendered inside the surface. 
     37     
     38    bool editable = true; /// The text of this surface is editable. 
     39    bool mouseChildren = true; /// Allow the mouse to interact with this Surface's children. 
     40    TextCursor textCursor; /// 
     41     
    4642    /// Callback functions 
    4743    bool delegate(Surface self) onBlur; /// 
     
    5652     * Unlike onKeyDown and onKeyUp, key is the unicode value of the key press, instead of the sdl key code. */ 
    5753    bool delegate(Surface self, dchar key, int modifier) onKeyPress;  
    58      
    59      
    6054    bool delegate(Surface self, byte buttons, Vec2i coordinates, char[] href) onMouseDown; /// 
    6155    bool delegate(Surface self, byte buttons, Vec2i coordinates, char[] href) onMouseUp; /// 
     
    8579        META  = LMETA | RMETA /// ditto 
    8680    };   
    87      
    88     protected Vec2f offset;     // pixel distance of the topleft corner from parent's top left, a relative offset 
    89     protected Vec2f size;       // pixel outer width/height, which includes borders and padding. 
     81 
     82    protected char[] oldText;       // Used for comparison to see if text has changed.  setHtml() would be more performant. 
     83    protected Texture textTexture;  // texture that constains rendered text image. 
     84     
     85    protected Vec2f offset;         // pixel distance of the topleft corner from parent's top left, a relative offset 
     86    protected Vec2f size;           // pixel outer width/height, which includes borders and padding. 
    9087     
    9188    public Vec2f offsetAbsolute;    // pixel distance of top left from the window's top left at 0, 0, an absolute offset 
    9289     
    9390    protected bool mouseIn;         // used to track mouseover/mouseout 
    94     protected bool grabbed;  
    95     protected bool resize_dirty = true; 
     91    protected bool resizeDirty = true; 
    9692     
    9793    protected SurfaceGeometry geometry; // geometry used to render this surface 
    98      
    99     protected static Style defaultStyle; // Used by getDefaultStyle()    
     94    protected TextBlock textLayout; 
     95     
     96    protected static Style defaultStyle; // Used as a cache by getDefaultStyle()     
    10097    protected static Surface grabbedSurface; // surface that has captured the mouse 
    10198    protected static Surface focusSurface; // surface that has focus for receiving input 
     
    194191     
    195192    /** 
     193     * Get a style with all auto/null/inherit/% values replaced with absolute values. */ 
     194    Style getCalculatedStyle() 
     195    {    
     196        Style cs = style;  // calculated style 
     197        Style pcs = parent ? parent.getCalculatedStyle() : getDefaultStyle(); 
     198         
     199        // Font and text properties 
     200        cs.fontFamily = style.fontFamily is null ? pcs.fontFamily : style.fontFamily; 
     201        cs.fontSize = style.fontSize == CSSValue.AUTO ? pcs.fontSize : style.fontSize; 
     202        cs.fontStyle = style.fontStyle == Style.FontStyle.AUTO ? pcs.fontStyle : style.fontStyle; 
     203        cs.fontWeight = style.fontWeight == Style.FontWeight.AUTO ? pcs.fontWeight : style.fontWeight; 
     204        cs.textAlign = style.textAlign == Style.TextAlign.AUTO ? pcs.textAlign : style.textAlign; 
     205        cs.textDecoration = style.textDecoration == Style.TextDecoration.AUTO ? pcs.textDecoration : style.textDecoration; 
     206         
     207        // Dimensional properties: 
     208         
     209        // Convert all sizes to pixels 
     210        Vec2f parent_size = Vec2f(parentWidth(), parentHeight()); 
     211        cs.width = style.width.toPx(parent_size.x); 
     212        cs.height = style.height.toPx(parent_size.y);        
     213        for (int i=0; i<4; i++) 
     214        {   ubyte xy = i%2; 
     215            float scale_by = xy==0 ? parent_size.y : parent_size.x;          
     216            cs.padding[i] = style.padding[i].toPx(scale_by, false); 
     217            cs.borderWidth[i] = style.borderWidth[i].toPx(scale_by, false);          
     218            cs.dimension[i].value = style.dimension[i].toPx(parent_size[xy]); 
     219        } 
     220         
     221        // Ensure at least 4 of the 6 of top/right/bottom/left/width/height are set. 
     222        for (int xy=0; xy<2; xy++) 
     223        {   int topLeft= xy==0 ? 3 : 0; // top or left 
     224            int bottomRight = xy==0 ? 1 : 2; // bottom or right 
     225            if (isNaN(cs.dimension[topLeft].value)) 
     226            {   if (isNaN(cs.dimension[bottomRight].value)) 
     227                    cs.dimension[topLeft] = 0; 
     228                if (isNaN(cs.size[xy].value)) 
     229                    cs.size[xy] = parent_size[xy];           
     230            } 
     231        } 
     232        return cs; 
     233    } 
     234 
     235     
     236    /** 
    196237     * Get the geometry data used for rendering this Surface. */ 
    197238    SurfaceGeometry getGeometry() 
     
    312353        //alias calculatedStyle cs; 
    313354        updateDimensions(cs); 
    314         if (resize_dirty) 
     355        if (resizeDirty) 
    315356        {    
    316357            Vec4f border; 
     
    324365         
    325366        // Text 
    326         if (text.length && (text != oldText || resize_dirty)) 
     367        if (text.length && (text != oldText || resizeDirty)) 
    327368        { 
    328369            int width = cast(int)width(); 
     
    356397            child.update(); 
    357398         
    358         resize_dirty = false; 
     399        resizeDirty = false; 
    359400    } 
    360401 
     
    415456        {   if (editable) 
    416457            {    
    417                 // Log.trace(cast(int)key, " ", unicode); 
    418                 //text ~= unicode; // TODO: send calculatedStyle to surface so 
    419                 // it can have the correct font for adding letters. 
    420                 //text = textLayout.input(key, mod, unicode); 
     458                text = textLayout.input(key, mod, unicode, textCursor); 
    421459            } 
    422460            if(parent)  
     
    497535    } 
    498536 
    499      
    500     /** 
    501      * Return the Surface that is currently grabbing mouse input, or null if no Surfaces are. */ 
    502     static Surface getGrabbedSurface() 
    503     {   return grabbedSurface;       
     537    /** 
     538     * Get the default style for Surface. 
     539     * None of these styles will be set to AUTO. */ 
     540    static Style getDefaultStyle() 
     541    { 
     542        if (!defaultStyle.fontFamily) 
     543        {   defaultStyle.fontFamily = ResourceManager.getDefaultFont(); 
     544            defaultStyle.fontSize = 12; 
     545            defaultStyle.fontStyle = Style.FontStyle.NORMAL; 
     546            defaultStyle.fontWeight = Style.FontWeight.NORMAL; 
     547            defaultStyle.color = "black"; 
     548            defaultStyle.textAlign = Style.TextAlign.LEFT; 
     549            defaultStyle.textDecoration = Style.TextDecoration.NONE;             
     550        } 
     551        return defaultStyle; 
    504552    } 
    505553     
     
    511559        else return focusSurface;        
    512560    } 
     561     
     562    /** 
     563     * Return the Surface that is currently grabbing mouse input, or null if no Surfaces are. */ 
     564    static Surface getGrabbedSurface() 
     565    {   return grabbedSurface;       
     566    } 
     567 
    513568     
    514569    /* 
     
    555610    } 
    556611     
    557  
    558     static Style getDefaultStyle() 
    559     { 
    560         if (!defaultStyle.fontFamily) 
    561         {   defaultStyle.fontFamily = ResourceManager.getDefaultFont(); 
    562             defaultStyle.fontSize = 12; 
    563             defaultStyle.fontStyle = Style.FontStyle.NORMAL; 
    564             defaultStyle.fontWeight = Style.FontWeight.NORMAL; 
    565             defaultStyle.color = "black"; 
    566             defaultStyle.textAlign = Style.TextAlign.LEFT; 
    567             defaultStyle.textDecoration = Style.TextDecoration.NONE;             
    568         } 
    569         return defaultStyle; 
    570     } 
    571      
    572     /** 
    573      * Get a style with all auto/null/inherit/% values replaced with absolute values. */ 
    574     Style getCalculatedStyle() 
    575     {    
    576         Style cs = style;  // calculated style 
    577         Style pcs = parent ? parent.getCalculatedStyle() : getDefaultStyle(); 
    578          
    579         // Font and text properties 
    580         cs.fontFamily = style.fontFamily is null ? pcs.fontFamily : style.fontFamily; 
    581         cs.fontSize = style.fontSize == CSSValue.AUTO ? pcs.fontSize : style.fontSize; 
    582         cs.fontStyle = style.fontStyle == Style.FontStyle.AUTO ? pcs.fontStyle : style.fontStyle; 
    583         cs.fontWeight = style.fontWeight == Style.FontWeight.AUTO ? pcs.fontWeight : style.fontWeight; 
    584         cs.textAlign = style.textAlign == Style.TextAlign.AUTO ? pcs.textAlign : style.textAlign; 
    585         cs.textDecoration = style.textDecoration == Style.TextDecoration.AUTO ? pcs.textDecoration : style.textDecoration; 
    586          
    587         // Dimensional properties: 
    588          
    589         // Convert all sizes to pixels 
    590         Vec2f parent_size = Vec2f(parentWidth(), parentHeight()); 
    591         cs.width = style.width.toPx(parent_size.x); 
    592         cs.height = style.height.toPx(parent_size.y);        
    593         for (int i=0; i<4; i++) 
    594         {   ubyte xy = i%2; 
    595             float scale_by = xy==0 ? parent_size.y : parent_size.x;          
    596             cs.padding[i] = style.padding[i].toPx(scale_by, false); 
    597             cs.borderWidth[i] = style.borderWidth[i].toPx(scale_by, false);          
    598             cs.dimension[i].value = style.dimension[i].toPx(parent_size[xy]); 
    599         } 
    600          
    601         // Ensure at least 4 of the 6 of top/right/bottom/left/width/height are set. 
    602         for (int xy=0; xy<2; xy++) 
    603         {   int topLeft= xy==0 ? 3 : 0; // top or left 
    604             int bottomRight = xy==0 ? 1 : 2; // bottom or right 
    605             if (isNaN(cs.dimension[topLeft].value)) 
    606             {   if (isNaN(cs.dimension[bottomRight].value)) 
    607                     cs.dimension[topLeft] = 0; 
    608                 if (isNaN(cs.size[xy].value)) 
    609                     cs.size[xy] = parent_size[xy];           
    610             } 
    611         } 
    612         return cs; 
    613     } 
    614  
    615612    /* 
    616613     * Update the internally stored x, y, width, and height based on the style. 
     
    661658        // If resized 
    662659        if (size != old_size) 
    663         {   resize_dirty = true; 
     660        {   resizeDirty = true; 
    664661            resize(size-old_size); // trigger resize event. 
    665662            foreach (c; children) 
  • trunk/src/yage/gui/textblock.d

    r191 r192  
    66 * This module is dedicated to my loving wife, Brittany Poggel. 
    77 */ 
    8 module yage.gui.textlayout
     8module yage.gui.textblock
    99 
    1010import tango.core.Exception; 
    11 import tango.io.Stdout; 
    1211import tango.math.Math; 
    1312import tango.math.IEEE; 
     
    3635import yage.system.log; 
    3736 
     37/// 
     38struct TextCursor 
     39{   uint position; /// 
     40    uint selectionStart; /// 
     41    uint selectionEnd; /// 
     42} 
     43 
    3844/** 
    39  * Render text and simple html with styles to an image. */ 
    40 struct TextLayout 
     45 * Render text and simple html with styles to an image. 
     46 * This class is used internally by the engine.  In most cases it shouldn't need to be called externally.. */ 
     47struct TextBlock 
    4148{ 
    4249    private static const char[] whitespace = " \t\r\n"; 
    4350    private static const char[] breaks = " *()-+=/\\,.;:|()[]{}<>\t\r\n"; // breaking characters 
    4451     
    45     private ubyte[] imageLookaside; // TODO: Use Memory.allocate instead? 
     52    private ubyte[] imageLookaside; // TODO: Have the lookaside passed into Render 
    4653     
    4754    private char[] text; 
    48     InlineStyle style; 
    49     int width; 
    50     int height; 
     55    private InlineStyle style; // base style of entire text block 
     56    private int width; 
     57    private int height; 
     58     
     59    // Deprecated in favor of TextCursor 
    5160    int cursorPosition; 
    5261    int selectionStart; 
     
    6069        int width; 
    6170        int height; 
    62         int cursorPosition; 
    63         int selectionStart; 
    64         int selectionEnd; 
    6571    } 
    6672    Previous previous; 
    6773    */ 
    6874     
    69      
    70     /* 
    71      * Store a line of rendered letters. */ 
    72     private struct Line 
    73     {   Letter[] letters; 
    74         int height;// height of the line, based on either a css line-height or the tallest characters 
    75         int width; // width of the line 
    76          
    77         static Line opCall() 
    78         {   Line result; 
    79             return result; 
    80         } 
    81     } 
    82      
    83     private Line[] lines; 
     75    private ArrayBuilder!(Line) lines; 
    8476    private ArrayBuilder!(Letter) letters; 
    85     private ArrayBuilder!(InlineStyle) styles;   
     77    private ArrayBuilder!(InlineStyle) styles;  // A style for each letter 
    8678     
    8779    /** 
    8880     * Update lines and letters data structures from keyboard input 
    8981     * Params: 
    90      *     key =  
     82     *     key = SDL key code constant 
    9183     *     mod = modifier key. 
    92      *     unicode =  
     84     *     unicode = Unicode value of the pressed key. 
     85     *     cursor 
    9386     * Returns: new html text. 
    9487     */ 
    95     char[] input(int key, int mod, dchar unicode
     88    char[] input(int key, int mod, dchar unicode, inout TextCursor cursor
    9689    { 
     90        Log.trace("%s %s", cursor.position, letters.length); 
     91        assert(cursor.position <= letters.length); 
     92         
     93        // Position cursor 
    9794        switch(key)  
    9895        { 
    99             case SDLK_LEFT: cursorPosition--; if (cursorPosition<0) cursorPosition=0; break; 
    100             case SDLK_RIGHT: cursorPosition++; if (cursorPosition>letters.length) cursorPosition=letters.length; break; 
     96            case SDLK_LEFT: if (cursorPosition>0) cursorPosition--; break; 
     97            case SDLK_RIGHT: if (cursorPosition<letters.length) cursorPosition++; break; 
    10198            case SDLK_UP: break; 
    10299            case SDLK_DOWN: break; 
    103100            case SDLK_HOME: break; 
    104101            case SDLK_END: break; 
    105             case SDLK_INSERT: break; 
    106              
     102             
     103            default: break;          
     104            // ctrl+a, z, x, c, v 
     105        } 
     106         
     107         
     108        if (unicode) 
     109        {   InlineStyle style; 
     110         
     111            /* 
     112            case SDLK_INSERT: break;             
    107113            case SDLK_BACKSPACE: break; 
    108114            case SDLK_DELETE:  break; 
    109             default: break; 
    110              
    111             // ctrl+a, z, x, c, v 
    112         } 
    113         if (unicode) 
    114         {   Letter l = style.fontFamily.getLetter(unicode, 10, 10); 
     115             */ 
     116             
     117            Letter l = style.fontFamily.getLetter(unicode, 10, 10); // style.fontFamily is null. 
    115118            letters ~= l; 
    116         }        
    117         // TODO: rebuild lines from letters. 
     119        } 
     120         
     121        lines = lettersToLines(letters.data, width, lines); 
    118122         
    119123        return toString(); 
     
    151155                totalHeight += line.height; 
    152156            if (lines.length) 
    153                 totalHeight += lines[$-1].height / 3; // add 1/3rd of the last line's height for letters w/ danglies. 
     157                totalHeight += lines.data[$-1].height / 3; // add 1/3rd of the last line's height for letters w/ danglies. 
    154158            height = min(totalHeight, height); 
    155159             
     
    161165                result = new Image(4, width, height, imageLookaside); 
    162166            imageLookaside = result.getData(); 
    163             foreach (i, line; lines
     167            foreach (i, line; lines.data
    164168            { 
    165169                if (style.textAlign == Style.TextAlign.RIGHT) 
     
    295299            this.width = width; 
    296300            this.height = height;            
    297             lines.length = 0; 
    298              
    299             // Build lines from letters 
    300             // TODO: Instead of having this here, create lettersToLines function (to Match HtmlParse.htmlToLetters()) 
    301             int i; 
    302             while (i<letters.length) 
    303             {   int start=i; 
    304                 int x=0, lineHeight=0; 
    305                 int last_break=i; 
    306                  
    307                 // Loop through as many as we can fit on this line 
    308                 while (x<width && i<letters.length) 
    309                 {   InlineStyle* letterStyle = (cast(InlineStyle*)letters[i].extra); 
    310                      
    311                     // Get line height (Defaults to fontSize*1.2 if not specified) 
    312                     int calculatedLineHeight = cast(int)(isNaN(letterStyle.lineHeight) ? letterStyle.fontSize : letterStyle.lineHeight); 
    313                     if (lineHeight < calculatedLineHeight) 
    314                         lineHeight = calculatedLineHeight; 
    315  
    316                     x+= letters[i].advanceX; 
    317                      
    318                     // Convert letter to utf-8 for comparison. TODO: This won't be necessary if we store breaks as utf-32. 
    319                     char[4] lookaside; 
    320                     char[] utf8 = letters[i].toString(lookaside); 
    321                      
    322                     if (containsPattern(breaks, utf8)) // store position of last breaking character 
    323                         last_break = i; 
    324                     if (x<width) 
    325                         i++; 
    326                     if (i==letters.length) // include the final characters. 
    327                         last_break = i; 
    328                      
    329                     if (utf8[0] == '\n') // break on line returns. 
    330                         break; 
    331                 } 
    332                  
    333                 // Add a new line 
    334                 Line line; 
    335                 if (start<last_break) // don't count spaces at the end of the line. 
    336                 {   i = last_break; 
    337                     if (i < letters.length && letters[i].letter=='\n') 
    338                         i++; // skip line returns 
    339                     assert(last_break <= letters.length); 
    340                     line.letters = letters.data[start..last_break]; // slice directly from the letters array to avoid copy allocation 
    341                 } 
    342                  
    343                 // trim line 
    344                 int firstChar, lastChar=line.letters.length-1; 
    345                 while (firstChar < line.letters.length && whitespace.contains(cast(char)line.letters[firstChar].letter)) 
    346                     firstChar++; 
    347                 while (lastChar>=0 && whitespace.contains(cast(char)line.letters[lastChar].letter)) 
    348                     lastChar--; 
    349                 line.letters = line.letters[firstChar..lastChar+1]; 
    350                      
    351                 // Calculate line width 
    352                 foreach (letter; line.letters) 
    353                     line.width += letter.advanceX; 
    354                  
    355                 line.height = lineHeight; 
    356                 lines ~= line; 
     301            lines = lettersToLines(letters.data, width, lines); 
     302            return true; 
     303        } 
     304        return false; 
     305    } 
     306     
     307    /* 
     308     *  
     309     * Params: 
     310     *     letters =  
     311     *     width =  
     312     *     lines = Provide an existing ArrayBuilder to fill--allows for fewer allocations */ 
     313    private static ArrayBuilder!(Line) lettersToLines(Letter[] letters, int width, ArrayBuilder!(Line) lines=ArrayBuilder!(Line)()) 
     314    { 
     315        lines.length = 0; 
     316         
     317        // Build lines from letters 
     318        // TODO: Instead of having this here, create lettersToLines function (to Match HtmlParse.htmlToLetters()) 
     319        int i; 
     320        while (i<letters.length) 
     321        {   int start=i; 
     322            int x=0, lineHeight=0; 
     323            int last_break=i; 
     324             
     325            // Loop through as many as we can fit on this line 
     326            while (x<width && i<letters.length) 
     327            {   InlineStyle* letterStyle = (cast(InlineStyle*)letters[i].extra); 
     328                 
     329                // Get line height (Defaults to fontSize*1.2 if not specified) 
     330                int calculatedLineHeight = cast(int)(isNaN(letterStyle.lineHeight) ? letterStyle.fontSize : letterStyle.lineHeight); 
     331                if (lineHeight < calculatedLineHeight) 
     332                    lineHeight = calculatedLineHeight; 
     333 
     334                x+= letters[i].advanceX; 
     335                 
     336                // Convert letter to utf-8 for comparison. TODO: This won't be necessary if we store breaks as utf-32. 
     337                char[4] lookaside; 
     338                char[] utf8 = letters[i].toString(lookaside); 
     339                 
     340                if (containsPattern(breaks, utf8)) // store position of last breaking character 
     341                    last_break = i; 
     342                if (x<width) 
     343                    i++; 
     344                if (i==letters.length) // include the final characters. 
     345                    last_break = i; 
     346                 
     347                if (utf8[0] == '\n') // break on line returns. 
     348                    break; 
    357349            } 
    358             return true; 
    359         } 
    360         return false; 
    361     }    
     350             
     351            // Add a new line 
     352            Line line; 
     353            if (start<last_break) // don't count spaces at the end of the line. 
     354            {   i = last_break; 
     355                if (i < letters.length && letters[i].letter=='\n') 
     356                    i++; // skip line returns 
     357                assert(last_break <= letters.length); 
     358                line.letters = letters[start..last_break]; // slice directly from the letters array to avoid copy allocation 
     359            } 
     360             
     361            // trim line 
     362            int firstChar, lastChar=line.letters.length-1; 
     363            while (firstChar < line.letters.length && whitespace.contains(cast(char)line.letters[firstChar].letter)) 
     364                firstChar++; 
     365            while (lastChar>=0 && whitespace.contains(cast(char)line.letters[lastChar].letter)) 
     366                lastChar--; 
     367            line.letters = line.letters[firstChar..lastChar+1]; 
     368                 
     369            // Calculate line width 
     370            foreach (letter; line.letters) 
     371                line.width += letter.advanceX; 
     372             
     373            line.height = lineHeight; 
     374            lines ~= line; 
     375        } 
     376 
     377        return lines; 
     378    } 
    362379} 
    363380 
     
    414431} 
    415432 
     433/* 
     434 * Store a line of rendered letters. */ 
     435private struct Line 
     436{   Letter[] letters; 
     437    int height;// height of the line, based on either a css line-height or the tallest characters 
     438    int width; // width of the line 
     439     
     440    static Line opCall() 
     441    {   Line result; 
     442        return result; 
     443    } 
     444} 
    416445 
    417446private struct HtmlParser 
  • trunk/src/yage/resource/manager.d

    r187 r192  
    4242        Texture.Format format; 
    4343        bool mipmap; 
     44         
     45        hash_t toHash() 
     46        {   return typeid(char[]).getHash(&source) ^ format ^ mipmap; 
     47        } 
     48        int opEquals(TextureKey* rhs) // seems to go unused 
     49        {   return source == rhs.source && format == rhs.format && mipmap == rhs.mipmap; 
     50        } 
     51        int opCmp(TextureKey* rhs) 
     52        {   // return positive if this is greater, negative if rhs is greater 
     53            if (source != rhs.source)  
     54                return source > rhs.source ? 1 : -1; 
     55            if (format != rhs.format) 
     56                return format = rhs.format; 
     57            return cast(byte)mipmap - cast(byte)rhs.mipmap; 
     58        } 
    4459    } 
    4560