changeset 100:0ea4a3e651ae

There is now a position marker for text editing. Changed the way fonts are configured. Actually, not much of the new way exists yet.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 15 Nov 2008 17:39:14 +0000
parents 5de5810e3516
children 71f0f1f83620
files data/conf/fonts.mtt data/conf/gui.mtt data/conf/options.mtt mde/font/FontTexture.d mde/font/font.d mde/gui/content/Content.d mde/gui/renderer/IRenderer.d mde/gui/renderer/SimpleRenderer.d mde/gui/widget/textContent.d
diffstat 9 files changed, 104 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/data/conf/fonts.mtt	Fri Nov 14 12:44:32 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-{MT01}
-<char[]|fallback="default">
-!{Lists available fonts. This data may be moved to options for more generic handling.}
-{default}
-<char[]|path="/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf">
-<int|size=16>
--- a/data/conf/gui.mtt	Fri Nov 14 12:44:32 2008 +0000
+++ b/data/conf/gui.mtt	Sat Nov 15 17:39:14 2008 +0000
@@ -16,8 +16,5 @@
 <WidgetData|optSep={0:[0x21, 0xff],1:["="]}>
 <WidgetData|floating={0:[0x8200,13,6,14],1:["text","button","blank"]}>
 <WidgetData|text={0:[0x4032]}>
-<WDims|content=[736,221,272,79]>
-<WDims|root=[6,736,50,6,580,6]>
-<WDims|floating=[25,0,103,27,572,26,74,74,242,128,168,93]>
 {Basic}
 <WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}>
--- a/data/conf/options.mtt	Fri Nov 14 12:44:32 2008 +0000
+++ b/data/conf/options.mtt	Sat Nov 15 17:39:14 2008 +0000
@@ -10,6 +10,9 @@
 {FontOptions}
 <int|lcdFilter=2>
 <int|renderMode=0x30000>
+<char[]|defaultFont="/usr/share/fonts/X11/Type1/n019003l.pfb">
+!<char[]|defaultFont="/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf">
+<int|defaultSize=16>
 
 {VideoOptions}
 <bool|noFrame=false>
--- a/mde/font/FontTexture.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/font/FontTexture.d	Sat Nov 15 17:39:14 2008 +0000
@@ -60,7 +60,6 @@
         ++cacheVer;
     }
     
-    
     /** Cache informatation for rendering a block of text.
      *
      * Recognises '\r', '\n' and "\r\n" as end-of-line markers. */
@@ -73,6 +72,7 @@
             return;
         
         cache.cacheVer = cacheVer;
+	cache.w = cache.h = 0;		// reset
         
         /* Convert the string to an array of character codes (which is equivalent to decoding UTF8
         * to UTF32 since no character code is ever > dchar.max). */
@@ -150,8 +150,9 @@
      *  cache =	Cache used to speed up CPU-side rendering code
      *  x =	Smaller x-coordinate of position
      *  y =	Smaller y-coordinate of position
-     *  col =	Text colour (note: currently limited to black or white) */
-    void drawCache (FT_Face face, char[] str, ref TextBlock cache, int x, int y, Colour col ) {
+     *  col =	Text colour (note: currently limited to black or white)
+     *  index =	Index of edit position, or size_t.max if no idet position. */
+    void drawCache (FT_Face face, char[] str, ref TextBlock cache, int x, int y, Colour col, size_t index) {
         updateCache (face, str, cache);	// update if necessary
         debug scope (failure)
                 logger.error ("drawTextCache failed");
@@ -159,7 +160,7 @@
         // opaque (GL_DECAL would be equivalent)
         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
         
-        drawCacheImpl (cache, x, y, col);
+        drawCacheImpl (face, cache, x, y, col, index);
     }
     /** A variation of drawCache, for transparent text.
      *
@@ -173,7 +174,7 @@
      * ---
      *
      * The overhead of the transparency is minimal. */
-    void drawCacheA (FT_Face face, char[] str, ref TextBlock cache, int x, int y, Colour col/+ = Colour.WHITE+/) {
+    void drawCacheA (FT_Face face, char[] str, ref TextBlock cache, int x, int y, Colour col, size_t index) {
         updateCache (face, str, cache);	// update if necessary
         debug scope (failure)
                 logger.error ("drawTextCache failed");
@@ -182,10 +183,10 @@
         // alpha is current colour, per component
         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
         
-        drawCacheImpl (cache, x, y, col);
+	drawCacheImpl (face, cache, x, y, col, index);
     }
-   
-    private void drawCacheImpl (ref TextBlock cache, int x, int y, Colour col) {
+    
+    private void drawCacheImpl (FT_Face face, ref TextBlock cache, int x, int y, Colour col, size_t index) {
         if (DerelictGL.availableVersion() >= GLVersion.Version14) {
             glBlendFunc (GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
             glBlendColor(col.r, col.g, col.b, 1f);	// text colour
@@ -216,9 +217,20 @@
             glTexCoord2f (tx1, ty2);	glVertex2i (x1, y2);
             glEnd ();
         }
-        
-        glDisable(GL_TEXTURE_2D);
+	glDisable(GL_TEXTURE_2D);
         glDisable(GL_BLEND);
+	
+	if (index <= cache.chars.length) {	// draw edit position
+	    int x1;
+	    if (index == 0)
+		x1 = x + 1;
+	    else {
+		auto c = cache.chars[index-1];
+		x1 = x + c.xPos + c.ga.advanceX;
+	    }
+	    glColor3f(col.r, col.g, col.b);
+	    glRecti(x1 - 1, y, x1, y + (face.size.metrics.height >> 6));
+	}
     }
     
     void addGlyph (FT_Face face, dchar chr) {
@@ -446,7 +458,10 @@
      *
      * lcdFilter should come from enum FT_LcdFilter:
      * FT_LCD_FILTER_NONE (0), FT_LCD_FILTER_DEFAULT (1), FT_LCD_FILTER_LIGHT (2) */
-    mixin (impl!("int renderMode, lcdFilter;"));
+    mixin (impl!("int renderMode, lcdFilter, defaultSize; char[] defaultFont;"));
+    
+    void validate() {
+    }
     
     static this() {
         fontOpts = new FontOptions;
@@ -469,11 +484,11 @@
  *
  * Struct should be stored externally and updated via references. */
 struct TextBlock {
-    CharCache[] chars;	// All chars. They hold x & y pos. info, so don't need to know about lines.
-    int cacheVer = -1;	// this is checked on access, and must equal for cache to be valid.
-    int w, h;		/// Size of the block. Likely the only fields of use outside the library.
+    CharCache[] chars;	/// All chars. They hold x & y pos. info, so don't need to know about lines.
+    int cacheVer = -1;	/// Checked on access; must equalFontTexture.cacheVer or the cache is rebuilt.
+    int w, h;		/// Size of the block.
 }
 struct CharCache {
-    GlyphAttribs* ga;	// character
-    int xPos, yPos;	// x,y position
+    GlyphAttribs* ga;	/// The character
+    int xPos, yPos;	/// Character's x,y position
 }
--- a/mde/font/font.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/font/font.d	Sat Nov 15 17:39:14 2008 +0000
@@ -21,14 +21,10 @@
 import mde.font.FontTexture;
 import mde.font.exception;
 
-import mde.file.mergetag.Reader;
-import mde.file.mergetag.DataSet;
-import mde.setup.paths;
 import mde.setup.exception;     // InitStage stuff
 
 import derelict.freetype.ft;
 
-import mde.file.deserialize;
 import tango.stdc.stringz;
 import Util = tango.text.Util;
 import tango.util.log.Log : Log, Logger;
@@ -46,7 +42,7 @@
  * Particular to a font and size, and any other effects like bold/italic if ever implemented.
  * 
  * Note: it is not currently intended to be thread-safe. */
-class FontStyle : IDataSection
+class FontStyle
 {
     //BEGIN Static: manager
     static {
@@ -55,8 +51,7 @@
                 fontTex.drawTexture;
         }
         
-        /** Load the freetype library with settings from the file fileName. */
-        private const fileName = "fonts";
+        /** Load the freetype library with settings from options. */
         StageState initialize () {
             if (FT_Init_FreeType (&library))
                 throw new fontException ("error initialising the FreeType library");
@@ -66,7 +61,7 @@
             FT_Library_Version (library, &maj, &min, &patch);
             if (maj != 2 || min != 3) {
                 logger.warn ("Using an untested FreeType version: {}.{}.{}", maj, min, patch);
-                logger.info ("The only tested version of freetype is 2.3.5");
+                logger.info ("The only tested version of freetype is 2.3");
             }
             
             // Set LCD filtering method if LCD rendering is enabled.
@@ -86,51 +81,19 @@
                 fontOpts.renderMode = FT_LOAD_TARGET_NORMAL;
             }
             
-            /* Load font settings
-             *
-             * Each mergetag section corresponds to a font; each is loaded whether used or not
-             * (however the actual font files are only loaded on use). A fallback id must be
-             * provided in the header which must match a loaded font name; if a non-existant font
-             * is requested a warning will be logged and this font returned. */
-            char[] fallbackName;
-            try {
-                IReader reader;
-                reader = confDir.makeMTReader (fileName, PRIORITY.LOW_HIGH, null, true);
-                reader.dataSecCreator = delegate IDataSection(ID id) {
-                    auto f = new FontStyle;
-                    fonts[id] = f;
-                    return f;
-                };
-                reader.read;
-                
-                // get fallback name
-                char[]* p = "fallback" in reader.dataset.header._charA;
-                if (p is null)
-                    throw new fontException ("No fallback font style specified");
-                fallbackName = *p;
-            } catch (NoFileException) {
-                throw new fontException ("No font settings file (fonts.[mtt|mtb])");
-            } catch (Exception e) {
-                throw new fontException ("Reading font settings failed: "~e.msg);
-            }
-            
-            // Find the fallback
-            FontStyle* p = fallbackName in fonts;
-            if (p is null)
-                throw new fontException ("Fallback font style specified is not found");
-            fallback = *p;
-            // Load the fallback now, to ensure it's available.
-            // Also note that get() doesn't make sure the fallback is loaded before returning it.
-            fallback.load;
-            
+            // Load the fallback font; if it throws let exception abort program
+	    fallback = new FontStyle (fontOpts.defaultFont(), fontOpts.defaultSize());
+	    
             return StageState.ACTIVE;
         }
         
         /** Cleanup: delete all fonts. */
         StageState cleanup () {
             // Clear loaded fonts (each has an FT_Face object needing to be freed):
-            foreach (fs; fonts)
+            /* FIXME
+	    foreach (fs; fonts)
                 fs.freeFace;
+	    */
             
             FT_Done_FreeType (library); // free the library
             
@@ -144,55 +107,25 @@
           * Uses fallback font-style if the desired style isn't known about or fails to load, so
           * this function should never fail or throw, in theory (unless out of memory). The
           * fallback should already be loaded. */
-        FontStyle get(char[] name) {
-            FontStyle* p = name in fonts;
-            if (p is null) {
-                logger.warn ("Font style "~name~" requested but not found; reverting to the fallback style.");
-                fonts[name] = fallback;	// set to prevent another warning getting logged
-                return fallback;
-            }
-            // Got it, but we need to make sure it's loaded:
-            try {
-                p.load;
-            } catch (Exception e) {
-                logger.warn ("Font style "~name~" failed to load; reverting to the fallback style.");
-                return fallback;
-            }
-            return *p;
+        FontStyle getDefault () {
+	    //FIXME: Ddoc, new purpose; rename
+            return fallback;
         }
         
     private:
         FT_Library	library;
         FontTexture	fontTex;
-        FontStyle[ID]	fonts;		// all font styles known about; not necessarily loaded
-        FontStyle	fallback;	// used when requested font isn't in fonts
+        //FontStyle[char[]]	fonts;	// loaded fonts, by file name	 FIXME: use hash of struct { char[] path; int size; }?
+        FontStyle	fallback;	// default & used when requested font can't be loaded
     }
     //END Static
     
-    this() {}
-    
-    //BEGIN Mergetag code
-    //NOTE: would it be better not to use a new mergetag file for this?
-    //FIXME: revise when gui can set options
-    void addTag (char[] tp, ID id, char[] dt) {
-        if (tp == "char[]") {
-            if (id == "path")
-                path = parseTo!(char[]) (dt);
-        }
-        else if (tp == "int") {
-            if (id == "size")
-                size = parseTo!(int) (dt);
-        }
-    }
-    void writeAll (ItemDelg) {}		// no writing the config for now
-    //END Mergetag code
-    
     /** Load the font file.
      *
      * Even if the same font is used at multiple sizes, multiple copies of FT_Face are used.
      * Sharing an FT_Face would require calling FT_Set_Pixel_Sizes each time a glyph is rendered or
      * swapping the size information (face.size)? */
-    void load ()
+    this (char[] path, int size)
     in {
         assert (library !is null, "font: library is null");
     } body {
@@ -206,12 +139,18 @@
          *	Use of face.size.metrics.height property.
          */
         
-        if (FT_Set_Pixel_Sizes (face, 0,size))
+        if (FT_Set_Pixel_Sizes (face, 0, size))
             throw new fontLoadException ("Unable to set pixel size");
         
         // Create if necessary:
         if (fontTex is null)
             fontTex = new FontTexture;
+	
+	return this;
+    }
+    
+    this (FontStyle font, int size) {
+	//FIXME: copy font's face and set new size?
     }
     
     /** Update a TextBlock cache, as used by the textBlock function.
@@ -234,8 +173,16 @@
     
     /** Draw a block of text (may inlcude new-lines).
      *
+     * Params:
+     *	x =	Top left x-coordinate of text block
+     *	y =	Top left y-coordinate of text block
+     *	str =	Text to render
+     *	cache =	An (optional) TextBlock used for rendering this text, to save some CPU work. See updateBlock
+     *	col =	Colour to render the text; see mde.types.Colour
+     *	index =	Either the index of the character to draw with an edit cursor or size_t.max. Not
+     *		the index within str, but the number of characters, excluding new-lines.
      * The text block is drawn with top-left corner at x,y. To put the text's baseline at a given
-     * y coordinate would require some changes. Line height is fixed based on largest glyph.
+     * y coordinate would require some changes. Line height is fixed (see getLineSeparation).
      * Due to hinter, glyphs are not guaranteed to lie within the "bounding box" defined by cache.
      * Can be changed to test size of each glyph if necessary.
      *
@@ -247,7 +194,7 @@
      * ---------------------------------
      * char[] str;
      * TextBlock strCache;
-     * textBlock (x, y, str, strCache);
+     * textBlock (x, y, str, strCache, Colour.WHITE);
      * ---------------------------------
      * The TextBlock cache will be updated as necessary. Besides the initial update, this will only
      * be if the font changes, or it is manually invalidated. This can be done by setting the
@@ -257,18 +204,18 @@
      * than this cache only serves as a small optimisation. However, the only way to get the size
      * of a text block is to use a TextBlock cache and update it, either with this function or with
      * the updateBlock function. */
-    void textBlock (int x, int y, char[] str, ref TextBlock cache, Colour col)
+    void textBlock (int x, int y, char[] str, ref TextBlock cache, Colour col, size_t index = size_t.max)
     in {
         assert (face, "FontStyle: face is null");
     } body {
         try {
-            fontTex.drawCache (face, str, cache, x, y, col);
+            fontTex.drawCache (face, str, cache, x, y, col, index);
         } catch (Exception e) {
             logger.warn ("Exception while drawing text: "~e.msg);
         }
     }
     /** ditto */
-    void textBlock (int x, int y, char[] str, Colour col)
+    void textBlock (int x, int y, char[] str, Colour col, size_t index = size_t.max)
     in {
         assert (face, "FontStyle: face is null");
     } body {
@@ -277,7 +224,7 @@
             // isn't really recommended anyway (and maintaining two versions of fontTex.drawText
             // would be horrible).
             TextBlock cache;
-            fontTex.drawCache (face, str, cache, x, y, col);
+            fontTex.drawCache (face, str, cache, x, y, col, index);
         } catch (Exception e) {
             logger.warn ("Exception while drawing text: "~e.msg);
         }
@@ -287,12 +234,12 @@
      *
      * Set the alpha by calling glColor*() first. See FontTexture.drawCacheA()'s documentation for
      * details. */
-    void textBlockA (int x, int y, char[] str, ref TextBlock cache, Colour col) 
+    void textBlockA (int x, int y, char[] str, ref TextBlock cache, Colour col, size_t index = size_t.max) 
     in {
         assert (face, "FontStyle: face is null");
     } body {
         try {
-            fontTex.drawCacheA (face, str, cache, x, y, col);
+	    fontTex.drawCacheA (face, str, cache, x, y, col, index);
         } catch (Exception e) {
             logger.warn ("Exception while drawing text: "~e.msg);
         }
@@ -312,16 +259,5 @@
     }
     
 private:
-    char[]	path;	// path to font file
-    int		size;	// font size
-    
     FT_Face	face;
-    
-    debug(mdeUnitTest) unittest {
-        // Don't do a unittest since font relies on loading the freetype library dynamically,
-        // normally done by Init. Also font is mostly visual and many problems will be obvious.
-    }
 }
-
-/+class OptionsFont : Options {
-    alias store!(+/
\ No newline at end of file
--- a/mde/gui/content/Content.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/gui/content/Content.d	Sat Nov 15 17:39:14 2008 +0000
@@ -225,6 +225,14 @@
 	return v;
     }
     
+    size_t getEditIndex () {
+	size_t i = 0;
+	for (size_t p = 0; p < pos; ++p)
+	    if (!(v[p] & 0x80) || v[p] & 0x40)
+		++i;
+	return i;
+    }
+    
     /// Gives all callbacks the modified value
     void endEdit () {
 	foreach (cb; cngCb)
--- a/mde/gui/renderer/IRenderer.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/gui/renderer/IRenderer.d	Sat Nov 15 17:39:14 2008 +0000
@@ -95,6 +95,10 @@
 	void setColour (int col = 0xFFFFFF) {
 	    colour = Colour (col);
 	}
+	
+	void setIndex (size_t index = size_t.max) {
+	    this.index = index;
+	}
         
         void getDimensions (out wdsize w, out wdsize h) {
             font.updateBlock (content, textCache);
@@ -103,11 +107,12 @@
         }
         
         void draw (wdabs x, wdabs y) {
-            font.textBlock (x,y, content, textCache, colour);
+            font.textBlock (x,y, content, textCache, colour, index);
         }
         
         char[] content;
         TextBlock textCache;
+	size_t index;
         Colour colour;
         FontStyle font;
     }
--- a/mde/gui/renderer/SimpleRenderer.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/gui/renderer/SimpleRenderer.d	Sat Nov 15 17:39:14 2008 +0000
@@ -31,7 +31,7 @@
 class SimpleRenderer : IRenderer
 {
     this () {
-        defaultFont = FontStyle.get("default");
+        defaultFont = FontStyle.getDefault;
     }
     
     alias Border.BTYPE BTYPE;
@@ -135,7 +135,8 @@
         a.font = defaultFont;
         a.content = text;
 	a.colour = Colour (col);
-        return a;
+	a.index = size_t.max;
+	return a;
     }
     
 protected:
--- a/mde/gui/widget/textContent.d	Fri Nov 14 12:44:32 2008 +0000
+++ b/mde/gui/widget/textContent.d	Sat Nov 15 17:39:14 2008 +0000
@@ -23,6 +23,14 @@
 
 import mde.gui.content.Content;
 
+debug {
+    import tango.util.log.Log : Log, Logger;
+    private Logger logger;
+    static this () {
+	logger = Log.getLogger ("mde.gui.widget.textContent");
+    }
+}
+
 
 class TextContentWidget : ATextWidget
 {
@@ -37,16 +45,21 @@
     
     /** On click, request keyboard input. */
     int clickEvent (wdabs, wdabs, ubyte, bool state) {
+	adapter.setIndex = content.getEditIndex;
+	mgr.requestRedraw;
 	return 1;	// get keyboard input via keyEvent
     }
     
     void keyEvent (ushort s, char[] i) {
-	adapter.setText (content.keyStroke (s, i));
-	adapter.getDimensions (mw, mh);	// FIXME: only passively change size: next resize will see new minimal size
+	adapter.setText = content.keyStroke (s, i);
+	adapter.setIndex = content.getEditIndex;
+	adapter.getDimensions (mw, mh);	// NOTE: only passively change size: next resize will see new minimal size
 	mgr.requestRedraw;
     }
     void keyFocusLost () {
+	adapter.setIndex;
 	content.endEdit;	// update other users of content relying on callbacks
+	mgr.requestRedraw;
     }
     
 protected: