# HG changeset patch # User Diggory Hardy # Date 1226770754 0 # Node ID 0ea4a3e651ae8d06e5f08531462729946f16800b # Parent 5de5810e35163d6ab7c50e8717b9fb1c81140567 There is now a position marker for text editing. Changed the way fonts are configured. Actually, not much of the new way exists yet. diff -r 5de5810e3516 -r 0ea4a3e651ae data/conf/fonts.mtt --- 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} - -!{Lists available fonts. This data may be moved to options for more generic handling.} -{default} - - diff -r 5de5810e3516 -r 0ea4a3e651ae data/conf/gui.mtt --- 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 @@ - - - {Basic} diff -r 5de5810e3516 -r 0ea4a3e651ae data/conf/options.mtt --- 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} + +! + {VideoOptions} diff -r 5de5810e3516 -r 0ea4a3e651ae mde/font/FontTexture.d --- 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 } diff -r 5de5810e3516 -r 0ea4a3e651ae mde/font/font.d --- 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 diff -r 5de5810e3516 -r 0ea4a3e651ae mde/gui/content/Content.d --- 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) diff -r 5de5810e3516 -r 0ea4a3e651ae mde/gui/renderer/IRenderer.d --- 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; } diff -r 5de5810e3516 -r 0ea4a3e651ae mde/gui/renderer/SimpleRenderer.d --- 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: diff -r 5de5810e3516 -r 0ea4a3e651ae mde/gui/widget/textContent.d --- 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: