Changeset 192
- Timestamp:
- 07/04/10 14:32:17 (2 years ago)
- Files:
-
- trunk/build/buildyage.d (modified) (1 diff)
- trunk/src/yage/gui/all.d (modified) (1 diff)
- trunk/src/yage/gui/style.d (modified) (1 diff)
- trunk/src/yage/gui/surface.d (modified) (14 diffs)
- trunk/src/yage/gui/textblock.d (moved) (moved from trunk/src/yage/gui/textlayout.d) (7 diffs)
- trunk/src/yage/resource/manager.d (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/build/buildyage.d
r189 r192 10 10 */ 11 11 12 const char[] app = "demo 1"; // set which program to build against yage.12 const char[] app = "demo2"; // set which program to build against yage. 13 13 //const char[] app = "tests/integration/main.d"; 14 14 trunk/src/yage/gui/all.d
r153 r192 13 13 import yage.gui.style; 14 14 import yage.gui.surface; 15 import yage.gui.textlayout; 15 import yage.gui.surfacegeometry; 16 import yage.gui.textblock; 16 17 } trunk/src/yage/gui/style.d
r191 r192 411 411 412 412 default: 413 throw new CSSException("Unsupported CSS Property: ' {}'", property);413 throw new CSSException("Unsupported CSS Property: '%s'", property); 414 414 } 415 415 } trunk/src/yage/gui/surface.d
r191 r192 7 7 module yage.gui.surface; 8 8 9 import tango.io.Stdout;10 9 import tango.math.IEEE; 11 10 import tango.math.Math; … … 22 21 import yage.resource.material; 23 22 import yage.gui.style; 24 import yage.gui.text layout;23 import yage.gui.textblock; 25 24 import yage.gui.surfacegeometry; 26 25 … … 35 34 Style style; 36 35 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 46 42 /// Callback functions 47 43 bool delegate(Surface self) onBlur; /// … … 56 52 * Unlike onKeyDown and onKeyUp, key is the unicode value of the key press, instead of the sdl key code. */ 57 53 bool delegate(Surface self, dchar key, int modifier) onKeyPress; 58 59 60 54 bool delegate(Surface self, byte buttons, Vec2i coordinates, char[] href) onMouseDown; /// 61 55 bool delegate(Surface self, byte buttons, Vec2i coordinates, char[] href) onMouseUp; /// … … 85 79 META = LMETA | RMETA /// ditto 86 80 }; 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. 90 87 91 88 public Vec2f offsetAbsolute; // pixel distance of top left from the window's top left at 0, 0, an absolute offset 92 89 93 90 protected bool mouseIn; // used to track mouseover/mouseout 94 protected bool grabbed; 95 protected bool resize_dirty = true; 91 protected bool resizeDirty = true; 96 92 97 93 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() 100 97 protected static Surface grabbedSurface; // surface that has captured the mouse 101 98 protected static Surface focusSurface; // surface that has focus for receiving input … … 194 191 195 192 /** 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 /** 196 237 * Get the geometry data used for rendering this Surface. */ 197 238 SurfaceGeometry getGeometry() … … 312 353 //alias calculatedStyle cs; 313 354 updateDimensions(cs); 314 if (resize _dirty)355 if (resizeDirty) 315 356 { 316 357 Vec4f border; … … 324 365 325 366 // Text 326 if (text.length && (text != oldText || resize _dirty))367 if (text.length && (text != oldText || resizeDirty)) 327 368 { 328 369 int width = cast(int)width(); … … 356 397 child.update(); 357 398 358 resize _dirty = false;399 resizeDirty = false; 359 400 } 360 401 … … 415 456 { if (editable) 416 457 { 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); 421 459 } 422 460 if(parent) … … 497 535 } 498 536 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; 504 552 } 505 553 … … 511 559 else return focusSurface; 512 560 } 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 513 568 514 569 /* … … 555 610 } 556 611 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 style577 Style pcs = parent ? parent.getCalculatedStyle() : getDefaultStyle();578 579 // Font and text properties580 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 pixels590 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 left604 int bottomRight = xy==0 ? 1 : 2; // bottom or right605 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 615 612 /* 616 613 * Update the internally stored x, y, width, and height based on the style. … … 661 658 // If resized 662 659 if (size != old_size) 663 { resize _dirty = true;660 { resizeDirty = true; 664 661 resize(size-old_size); // trigger resize event. 665 662 foreach (c; children) trunk/src/yage/gui/textblock.d
r191 r192 6 6 * This module is dedicated to my loving wife, Brittany Poggel. 7 7 */ 8 module yage.gui.text layout;8 module yage.gui.textblock; 9 9 10 10 import tango.core.Exception; 11 import tango.io.Stdout;12 11 import tango.math.Math; 13 12 import tango.math.IEEE; … … 36 35 import yage.system.log; 37 36 37 /// 38 struct TextCursor 39 { uint position; /// 40 uint selectionStart; /// 41 uint selectionEnd; /// 42 } 43 38 44 /** 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.. */ 47 struct TextBlock 41 48 { 42 49 private static const char[] whitespace = " \t\r\n"; 43 50 private static const char[] breaks = " *()-+=/\\,.;:|()[]{}<>\t\r\n"; // breaking characters 44 51 45 private ubyte[] imageLookaside; // TODO: Use Memory.allocate instead?52 private ubyte[] imageLookaside; // TODO: Have the lookaside passed into Render 46 53 47 54 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 51 60 int cursorPosition; 52 61 int selectionStart; … … 60 69 int width; 61 70 int height; 62 int cursorPosition;63 int selectionStart;64 int selectionEnd;65 71 } 66 72 Previous previous; 67 73 */ 68 74 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; 84 76 private ArrayBuilder!(Letter) letters; 85 private ArrayBuilder!(InlineStyle) styles; 77 private ArrayBuilder!(InlineStyle) styles; // A style for each letter 86 78 87 79 /** 88 80 * Update lines and letters data structures from keyboard input 89 81 * Params: 90 * key = 82 * key = SDL key code constant 91 83 * mod = modifier key. 92 * unicode = 84 * unicode = Unicode value of the pressed key. 85 * cursor 93 86 * Returns: new html text. 94 87 */ 95 char[] input(int key, int mod, dchar unicode )88 char[] input(int key, int mod, dchar unicode, inout TextCursor cursor) 96 89 { 90 Log.trace("%s %s", cursor.position, letters.length); 91 assert(cursor.position <= letters.length); 92 93 // Position cursor 97 94 switch(key) 98 95 { 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; 101 98 case SDLK_UP: break; 102 99 case SDLK_DOWN: break; 103 100 case SDLK_HOME: break; 104 101 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; 107 113 case SDLK_BACKSPACE: break; 108 114 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. 115 118 letters ~= l; 116 } 117 // TODO: rebuild lines from letters. 119 } 120 121 lines = lettersToLines(letters.data, width, lines); 118 122 119 123 return toString(); … … 151 155 totalHeight += line.height; 152 156 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. 154 158 height = min(totalHeight, height); 155 159 … … 161 165 result = new Image(4, width, height, imageLookaside); 162 166 imageLookaside = result.getData(); 163 foreach (i, line; lines )167 foreach (i, line; lines.data) 164 168 { 165 169 if (style.textAlign == Style.TextAlign.RIGHT) … … 295 299 this.width = width; 296 300 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; 357 349 } 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 } 362 379 } 363 380 … … 414 431 } 415 432 433 /* 434 * Store a line of rendered letters. */ 435 private 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 } 416 445 417 446 private struct HtmlParser trunk/src/yage/resource/manager.d
r187 r192 42 42 Texture.Format format; 43 43 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 } 44 59 } 45 60
