Text Style.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. #define SKIP_SPACE 1 // if skip drawing space that was at the end of the line but didn't fit
  6. #define CC4_TXDS CC4('T','X','D','S')
  7. #define DefaultShade 230
  8. static const Color DefaultSelectionColor(51, 153, 255, 64);
  9. Memc<TextLineSplit8 > Tls8 ;
  10. Memc<TextLineSplit16> Tls16;
  11. DEFINE_CACHE(TextStyle, TextStyles, TextStylePtr, "Text Style");
  12. /******************************************************************************/
  13. static Int Length(CChar8 *text, AUTO_LINE_MODE auto_line, Int max_length)
  14. {
  15. Int length=-1;
  16. MAX (max_length, 1); // always allow drawing at least 1 character
  17. FREP(max_length+1) // iterate those characters (and 1 more) to check if within this range (or right after) we have special characters
  18. {
  19. Char8 c=text[i];
  20. if(c=='\0' || c=='\n')return i; // if we've encountered end or new line, then we have to stop there, return length exclusive because we won't draw this character
  21. if(auto_line!=AUTO_LINE_SPLIT)if(c==' ' )length=i; // if we want to avoid splitting words into multiple lines, then remember last space that's in this range
  22. }
  23. if(length>=0)length+=(length<max_length);else // to support drawing cursors and selections, draw the space too (increase length to include it) if it can fit in the 'max_length' range
  24. { // if nothing was found
  25. length=max_length; // draw all characters that fit in this space
  26. if(auto_line==AUTO_LINE_SPACE)for(; ; length++) // if we're not splitting words then we have to keep going forward outside of the range that fits in the space until we find first character that can split
  27. {
  28. Char8 c=text[length];
  29. if(c=='\0'
  30. || c=='\n'
  31. || c==' ' )break; // and find a first character that can split text, set length exclusive because we won't draw this character
  32. }
  33. }
  34. return length;
  35. }
  36. static Int Length(CChar *text, AUTO_LINE_MODE auto_line, Int max_length)
  37. {
  38. Int length=-1;
  39. MAX (max_length, 1); // always allow drawing at least 1 character
  40. FREP(max_length+1) // iterate those characters (and 1 more) to check if within this range (or right after) we have special characters
  41. {
  42. Char c=text[i];
  43. if(c=='\0' || c=='\n')return i; // if we've encountered end or new line, then we have to stop there, return length exclusive because we won't draw this character
  44. if(auto_line!=AUTO_LINE_SPLIT)if(c==' ' )length=i; // if we want to avoid splitting words into multiple lines, then remember last space that's in this range
  45. }
  46. if(length>=0)length+=(length<max_length);else // to support drawing cursors and selections, draw the space too (increase length to include it) if it can fit in the 'max_length' range
  47. { // if nothing was found
  48. length=max_length; // draw all characters that fit in this space
  49. if(auto_line==AUTO_LINE_SPACE)for(; ; length++) // if we're not splitting words then we have to keep going forward outside of the range that fits in the space until we find first character that can split
  50. {
  51. Char c=text[length];
  52. if(c=='\0'
  53. || c=='\n'
  54. || c==' ' )break; // and find a first character that can split text, set length exclusive because we won't draw this character
  55. }
  56. }
  57. return length;
  58. }
  59. /******************************************************************************/
  60. void Set(MemPtr<TextLineSplit8> tls, CChar8 *text, C TextStyleParams &text_style, Flt width, AUTO_LINE_MODE auto_line) // have to set at least one line to support drawing cursor when text is empty
  61. {
  62. tls.clear();
  63. if(CChar8 *t=text)switch(auto_line)
  64. {
  65. default: // AUTO_LINE_NONE
  66. {
  67. tls.New().set(t, -1, 0);
  68. for(Int length=0; t[0]; t++)
  69. {
  70. if(t[0]!='\n')length++;else
  71. {
  72. tls.last().length=length; length=0;
  73. tls.New ().set(t+1, -1, (t+1)-text);
  74. }
  75. }
  76. }break;
  77. case AUTO_LINE_SPACE:
  78. case AUTO_LINE_SPACE_SPLIT:
  79. case AUTO_LINE_SPLIT:
  80. {
  81. for(width+=EPS; ; ) // needed when drawing text in 'width' calculated from 'textWidth'
  82. {
  83. Int length=Length(t, auto_line, text_style.textPos(t, width, false)); // check how many characters can we fit in this space
  84. tls.New().set(t, length, t-text);
  85. t+=length;
  86. if(*t=='\0')break;
  87. if(*t=='\n'/* || !length*/ // if next character is a new line, or we didn't advance ('length' doesn't need to be checked because it can be 0 only for '\0' or '\n' which are already checked)
  88. || SKIP_SPACE && *t==' ' && text_style.spacing!=SPACING_CONST
  89. )t++; // then skip this character
  90. }
  91. }break;
  92. }else tls.New().set(null, 0, 0); // have to set at least one line
  93. }
  94. void Set(MemPtr<TextLineSplit16> tls, CChar *text, C TextStyleParams &text_style, Flt width, AUTO_LINE_MODE auto_line) // have to set at least one line to support drawing cursor when text is empty
  95. {
  96. tls.clear();
  97. if(CChar *t=text)switch(auto_line)
  98. {
  99. default: // AUTO_LINE_NONE
  100. {
  101. tls.New().set(t, -1, 0);
  102. for(Int length=0; t[0]; t++)
  103. {
  104. if(t[0]!='\n')length++;else
  105. {
  106. tls.last().length=length; length=0;
  107. tls.New ().set(t+1, -1, (t+1)-text);
  108. }
  109. }
  110. }break;
  111. case AUTO_LINE_SPACE:
  112. case AUTO_LINE_SPACE_SPLIT:
  113. case AUTO_LINE_SPLIT:
  114. {
  115. for(width+=EPS; ; ) // needed when drawing text in 'width' calculated from 'textWidth'
  116. {
  117. Int length=Length(t, auto_line, text_style.textPos(t, width, false)); // check how many characters can we fit in this space
  118. tls.New().set(t, length, t-text);
  119. t+=length;
  120. if(*t=='\0')break;
  121. if(*t=='\n'/* || !length*/ // if next character is a new line, or we didn't advance ('length' doesn't need to be checked because it can be 0 only for '\0' or '\n' which are already checked)
  122. || SKIP_SPACE && *t==' ' && text_style.spacing!=SPACING_CONST
  123. )t++; // then skip this character
  124. }
  125. }break;
  126. }else tls.New().set(null, 0, 0); // have to set at least one line
  127. }
  128. static Bool SetLine(TextLineSplit16 &tls, CChar *text, C TextStyleParams &text_style, Flt width, AUTO_LINE_MODE auto_line, Int line)
  129. {
  130. if(CChar *t=text)if(line>=0)for(width+=EPS; ; ) // needed when drawing text in 'width' calculated from 'textWidth'
  131. {
  132. Int length=Length(t, auto_line, (auto_line==AUTO_LINE_NONE) ? INT_MAX-1 : text_style.textPos(t, width, false)); // check how many characters can we fit in this space, use "INT_MAX-1" so "FREP(max_length+1)" inside 'Length' can finish
  133. if(!line){tls.set(t, length, t-text); return true;}
  134. line--;
  135. t+=length;
  136. if(*t=='\0')break;
  137. if(*t=='\n'/* || !length*/ // if next character is a new line, or we didn't advance ('length' doesn't need to be checked because it can be 0 only for '\0' or '\n' which are already checked)
  138. || SKIP_SPACE && *t==' ' && text_style.spacing!=SPACING_CONST && auto_line!=AUTO_LINE_NONE
  139. )t++; // then skip this character
  140. }
  141. return false;
  142. }
  143. Vec2 TextStyleParams::textIndex(CChar *text, Int index, Flt width, AUTO_LINE_MODE auto_line)C
  144. {
  145. if(index<=0 || !text)return 0;
  146. CChar *t=text; Int line=0; for(width+=EPS; ; line++) // needed when drawing text in 'width' calculated from 'textWidth'
  147. {
  148. Int length=Length(t, auto_line, (auto_line==AUTO_LINE_NONE) ? INT_MAX-1 : textPos(t, width, false)); // check how many characters can we fit in this space, use "INT_MAX-1" so "FREP(max_length+1)" inside 'Length' can finish
  149. Int offset=t-text;
  150. t+=length;
  151. #if 1 // this check will set the cursor for the next line for split lines
  152. if(*t!='\n')length--;
  153. #endif
  154. if(index<=offset+length || *t=='\0')
  155. return Vec2(textWidth(text+offset, index-offset), line*lineHeight());
  156. if(*t=='\n'/* || !length*/ // if next character is a new line, or we didn't advance ('length' doesn't need to be checked because it can be 0 only for '\0' or '\n' which are already checked)
  157. || SKIP_SPACE && *t==' ' && spacing!=SPACING_CONST && auto_line!=AUTO_LINE_NONE
  158. )t++; // then skip this character
  159. }
  160. }
  161. /******************************************************************************/
  162. Font* TextStyleParams::getFont()C
  163. {
  164. if(_font)return _font;
  165. if(GuiSkin *skin=Gui.skin())return skin->font(); // copy to temp 'skin' in case of multi-thread issues
  166. return null;
  167. }
  168. Flt TextStyleParams::posY(Flt y)C
  169. {
  170. if(C Font *font=getFont())
  171. {
  172. Flt ysize =size .y/font->height(), // height of 1 font texel
  173. y_align_mul=align.y*0.5f+0.5f;
  174. y+=size.y*y_align_mul+ysize*font->paddingT();
  175. }
  176. return y;
  177. }
  178. void TextStyleParams::posY(Flt y, Vec2 &range)C
  179. {
  180. if(C Font *font=getFont())
  181. {
  182. Flt ysize =size .y/font->height(), // height of 1 font texel
  183. y_align_mul=align.y*0.5f+0.5f;
  184. y+=size.y*y_align_mul;
  185. range.x=y-size.y-ysize*font->paddingB(); // min
  186. range.y=y +ysize*font->paddingT(); // max
  187. }
  188. }
  189. void TextStyleParams::posYI(Flt y, Vec2 &range)C
  190. {
  191. if(C Font *font=getFont())
  192. {
  193. Flt ysize =size .y/font->height(), // height of 1 font texel
  194. y_align_mul=align.y*0.5f+0.5f;
  195. y-=size.y*y_align_mul;
  196. range.x=y -ysize*font->paddingT(); // min
  197. range.y=y+size.y+ysize*font->paddingB(); // max
  198. }
  199. }
  200. /******************************************************************************/
  201. void TextStyleParams::setPerPixelSize()
  202. {
  203. if(C Font *font=getFont())size=D.pixelToScreenSize(font->height());
  204. }
  205. Flt TextStyleParams::textWidth(C Str &str, Int max_length)C
  206. {
  207. if(Int length=str.length())
  208. if(C Font *font=getFont())
  209. {
  210. if(max_length>=0 && max_length<length){if(!max_length)return 0; length=max_length;}
  211. Flt xsize=size.x/font->height(),
  212. space=size.x*T.space.x;
  213. Int base_chars, width=font->textWidth(base_chars, spacing, str, max_length);
  214. return width*xsize + space*(base_chars-(spacing!=SPACING_CONST)); // calculate spacing only between base characters (ignoring combining), we're including spacing between the characters, so we need to set 1 less (except the case for SPACING_CONST where we need to have spacing for all characters)
  215. }
  216. return 0;
  217. }
  218. Flt TextStyleParams::textWidth(C Str8 &str, Int max_length)C
  219. {
  220. if(Int length=str.length())
  221. if(C Font *font=getFont())
  222. {
  223. if(max_length>=0 && max_length<length){if(!max_length)return 0; length=max_length;}
  224. Flt xsize=size.x/font->height(),
  225. space=size.x*T.space.x;
  226. Int base_chars, width=font->textWidth(base_chars, spacing, str, max_length);
  227. return width*xsize + space*(base_chars-(spacing!=SPACING_CONST)); // calculate spacing only between base characters (ignoring combining), we're including spacing between the characters, so we need to set 1 less (except the case for SPACING_CONST where we need to have spacing for all characters)
  228. }
  229. return 0;
  230. }
  231. Flt TextStyleParams::textWidth(CChar *text, Int max_length)C
  232. {
  233. if(Int length=Length(text))
  234. if(C Font *font=getFont())
  235. {
  236. if(max_length>=0 && max_length<length){if(!max_length)return 0; length=max_length;}
  237. Flt xsize=size.x/font->height(),
  238. space=size.x*T.space.x;
  239. Int base_chars, width=font->textWidth(base_chars, spacing, text, max_length);
  240. return width*xsize + space*(base_chars-(spacing!=SPACING_CONST)); // calculate spacing only between base characters (ignoring combining), we're including spacing between the characters, so we need to set 1 less (except the case for SPACING_CONST where we need to have spacing for all characters)
  241. }
  242. return 0;
  243. }
  244. Flt TextStyleParams::textWidth(CChar8 *text, Int max_length)C
  245. {
  246. if(Int length=Length(text))
  247. if(C Font *font=getFont())
  248. {
  249. if(max_length>=0 && max_length<length){if(!max_length)return 0; length=max_length;}
  250. Flt xsize=size.x/font->height(),
  251. space=size.x*T.space.x;
  252. Int base_chars, width=font->textWidth(base_chars, spacing, text, max_length);
  253. return width*xsize + space*(base_chars-(spacing!=SPACING_CONST)); // calculate spacing only between base characters (ignoring combining), we're including spacing between the characters, so we need to set 1 less (except the case for SPACING_CONST where we need to have spacing for all characters)
  254. }
  255. return 0;
  256. }
  257. /******************************************************************************/
  258. Int TextStyleParams::textPos(CChar8 *text, Flt x, Bool round)C
  259. {
  260. Int pos=0;
  261. if(Is(text))
  262. if(C Font *font=getFont())
  263. {
  264. Flt space=size.x*T.space.x;
  265. if(spacing==SPACING_CONST)
  266. {
  267. x/=space; if(round)x+=0.5f;
  268. pos=Trunc(x);
  269. #if 0 // fast
  270. Clamp(pos, 0, Length(text));
  271. #else // CHARF_COMBINING
  272. CChar8 *start=text; for(Int base_chars=0; ; base_chars++)
  273. {
  274. Char8 c=*text; if(!c || base_chars>=pos){pos=text-start; break;}
  275. skip:
  276. Char8 next=*++text;
  277. if(CharFlagFast(next)&CHARF_COMBINING)goto skip;
  278. }
  279. #endif
  280. }else
  281. for(Flt xsize=size.x/font->height(); ; )
  282. {
  283. Char8 c=*text; if(!c)break;
  284. CChar8 *start=text;
  285. skip1:
  286. Char8 next=*++text;
  287. if(CharFlagFast(next)&CHARF_COMBINING)goto skip1;
  288. Flt w=font->charWidth(c, next, spacing)*xsize;
  289. if(x<=(round ? w*0.5f : w))break;
  290. x-=w+space;
  291. pos+=text-start; // advance by how many characters were processed
  292. }
  293. }
  294. return pos;
  295. }
  296. Int TextStyleParams::textPos(CChar *text, Flt x, Bool round)C
  297. {
  298. Int pos=0;
  299. if(Is(text))
  300. if(C Font *font=getFont())
  301. {
  302. Flt space=size.x*T.space.x;
  303. if(spacing==SPACING_CONST)
  304. {
  305. x/=space; if(round)x+=0.5f;
  306. pos=Trunc(x);
  307. #if 0 // fast
  308. Clamp(pos, 0, Length(text));
  309. #else // CHARF_COMBINING
  310. CChar *start=text; for(Int base_chars=0; ; base_chars++)
  311. {
  312. Char c=*text; if(!c || base_chars>=pos){pos=text-start; break;}
  313. skip:
  314. Char next=*++text;
  315. if(CharFlagFast(next)&CHARF_COMBINING)goto skip;
  316. }
  317. #endif
  318. }else
  319. for(Flt xsize=size.x/font->height(); ; )
  320. {
  321. Char c=*text; if(!c)break;
  322. CChar *start=text;
  323. skip1:
  324. Char next=*++text;
  325. if(CharFlagFast(next)&CHARF_COMBINING)goto skip1;
  326. Flt w=font->charWidth(c, next, spacing)*xsize;
  327. if(x<=(round ? w*0.5f : w))break;
  328. x-=w+space;
  329. pos+=text-start; // advance by how many characters were processed
  330. }
  331. }
  332. return pos;
  333. }
  334. Int TextStyleParams::textPos(CChar *text, Flt x, Flt y, Bool round, Flt width, AUTO_LINE_MODE auto_line, Bool &eol)C
  335. {
  336. Int line=Trunc(y/lineHeight()); if(line<0){eol=false; return 0;}
  337. TextLineSplit16 tls; if(!SetLine(tls, text, T, width, auto_line, line)){eol=true; return Length(text);}
  338. Int pos=textPos(text+tls.offset, x, round);
  339. if(eol=(pos>=tls.length))pos=tls.length; // yes this must check ">=" and not ">" because we need to set "eol=(pos>=tls.length)" because we need it for correct double-clicking word selection
  340. return tls.offset+pos;
  341. }
  342. /******************************************************************************/
  343. Int TextStyleParams::textLines(CChar8 *text, Flt width, AUTO_LINE_MODE auto_line, Flt *actual_width)C
  344. {
  345. Memt<TextLineSplit8> tls; Set(tls, text, T, width, auto_line);
  346. if(actual_width)
  347. {
  348. *actual_width=0; REPA(tls){TextLineSplit8 &t=tls[i]; MAX(*actual_width, textWidth(t.text, t.length));}
  349. }
  350. return tls.elms();
  351. }
  352. Int TextStyleParams::textLines(CChar *text, Flt width, AUTO_LINE_MODE auto_line, Flt *actual_width)C
  353. {
  354. Memt<TextLineSplit16> tls; Set(tls, text, T, width, auto_line);
  355. if(actual_width)
  356. {
  357. *actual_width=0; REPA(tls){TextLineSplit16 &t=tls[i]; MAX(*actual_width, textWidth(t.text, t.length));}
  358. }
  359. return tls.elms();
  360. }
  361. /******************************************************************************/
  362. TextStyleParams& TextStyleParams::resetColors(Bool gui)
  363. {
  364. // use 'Gui.skin.text.text_style', 'Gui.skin.text_style' if available
  365. if(GuiSkin *skin=Gui.skin())
  366. if(TextStyle *text_style=(gui ? skin->text.text_style() : skin->text_style()))
  367. {
  368. shadow =text_style->shadow;
  369. shade =text_style->shade;
  370. color =text_style->color;
  371. selection=text_style->selection;
  372. return T;
  373. }
  374. // otherwise set defaults
  375. shadow =255;
  376. shade =DefaultShade;
  377. color =WHITE;
  378. selection=DefaultSelectionColor;
  379. return T;
  380. }
  381. TextStyleParams& TextStyleParams::reset(Bool gui)
  382. {
  383. // use 'Gui.skin.text.text_style', 'Gui.skin.text_style' if available
  384. if(GuiSkin *skin=Gui.skin())
  385. if(TextStyle *text_style=(gui ? skin->text.text_style() : skin->text_style()))
  386. {T=*text_style; return T;}
  387. // otherwise set defaults
  388. pixel_align=true;
  389. align.set(0 , 0 );
  390. size .set(0.08f, 0.08f);
  391. space.set(0.06f, 1 );
  392. spacing =SPACING_NICE;
  393. shadow =255;
  394. shade =DefaultShade;
  395. color =WHITE;
  396. selection=DefaultSelectionColor;
  397. _font =null; // keep as null to always use the current value of 'Gui.skin.font'
  398. edit =null;
  399. return T;
  400. }
  401. /******************************************************************************/
  402. TextStyle& TextStyle::font (C FontPtr &font) {_font=font; super::font(_font()); return T;}
  403. TextStyle& TextStyle::resetColors( Bool gui ) {super::resetColors(gui); return T;}
  404. TextStyle& TextStyle::reset ( Bool gui ) {super::reset (gui); if(GuiSkin *skin=Gui.skin())if(TextStyle *text_style=(gui ? skin->text.text_style() : skin->text_style())){font(text_style->font()); return T;} font(null); return T;} // copy to temp 'skin' and 'text_style' in case of multi-thread issues, call 'font' to always make sure that '_font' from 'TextStyle' and 'TextStyleParams' is synchronized
  405. TextStyle::TextStyle ( ) { if(GuiSkin *skin=Gui.skin())if(TextStyle *text_style= skin->text_style() ){font(text_style->font()); return ;} font(null); } // copy to temp 'skin' and 'text_style' in case of multi-thread issues, call 'font' to always make sure that '_font' from 'TextStyle' and 'TextStyleParams' is synchronized
  406. /******************************************************************************/
  407. // DRAW
  408. /******************************************************************************/
  409. struct TextInput
  410. {
  411. CChar8 *t8 ;
  412. CChar *t16;
  413. Bool is()C {return Is(t8) || Is(t16);}
  414. Char c ()C {return t8 ? Char8To16Fast(*t8) : t16 ? *t16 : 0;} // we can assume that Str was already initialized
  415. CPtr p ()C {return t8 ? CPtr(t8) : CPtr(t16);}
  416. void operator++(int) {if(t8)t8++; if(t16)t16++;}
  417. TextInput(CChar8 *t) {t8 =t; t16=null;}
  418. TextInput(CChar *t) {t16=t; t8 =null;}
  419. };
  420. static Int CompareCode(C TextCodeData &code, C CPtr &pos) {return ComparePtr(code.pos, pos);}
  421. static Int FindCode(C TextCodeData *code, Int codes, CPtr cur_pos)
  422. {
  423. Int index;
  424. Bool found=BinarySearch(code, codes, cur_pos, index, CompareCode);
  425. if( found)for(; index>0 && code[index-1].pos==cur_pos; )index--; // in case if many codes would point to the same place
  426. return found ? index : index-1; // if we haven't found at exact position, then we need to grab the one before selected position like this: "<code>some tex|t here" when starting drawing of | we need to use the <code>
  427. }
  428. static void SetCode(C TextCodeData *code, C TextStyleParams &text_style, Bool sub_pixel)
  429. {
  430. VI.flush();
  431. if(sub_pixel){ Color c=((code && code-> color_mode!=TextCodeData::DEFAULT) ? code->color : text_style.color ) ; D.alphaFactor(c); c.r=c.g=c.b=c.a; VI.color(c);}
  432. else {VI.color (((code && code-> color_mode!=TextCodeData::DEFAULT) ? code->color : text_style.color ) );
  433. Sh.h_FontShadow->set(((code && code->shadow_mode!=TextCodeData::DEFAULT) ? code->shadow : text_style.shadow)/255.0f);}
  434. }
  435. void DrawKeyboardCursor(C Vec2 &pos, Flt height)
  436. {
  437. ALPHA_MODE alpha=D.alpha(ALPHA_INVERT); Rect_U(pos, height/11.0f, height).draw(WHITE);
  438. D.alpha(alpha );
  439. }
  440. void DrawKeyboardCursorOverwrite(C Vec2 &pos, Flt height, C TextStyleParams &text_style, Char chr)
  441. {
  442. if(C Font *font=text_style.getFont())
  443. {
  444. Flt w=((text_style.spacing==SPACING_CONST) ? text_style.colWidth() : chr ? text_style.size.x/font->height()*font->charWidth(chr) : height*0.5f),
  445. min_w=height/5.0f; // use min width because some characters are just too thin
  446. ALPHA_MODE alpha=D.alpha(ALPHA_INVERT); Rect_LU(pos, w, height).extendX(Max((min_w-w)*0.5f, 0)).draw(WHITE); // extend both left and right
  447. D.alpha(alpha );
  448. }
  449. }
  450. void TextStyleParams::drawMain(Flt x, Flt y, TextInput ti, Int max_length, C TextCodeData *code, Int codes, Int offset)C
  451. {
  452. Int cur=SIGN_BIT, sel=SIGN_BIT; ASSERT(Int(SIGN_BIT)<0); // these must be negative
  453. if(edit && App.active()){if(edit->cur>=0)cur=edit->cur-offset; if(edit->sel>=0)sel=edit->sel-offset;} // set only if valid (so we keep SIGN_BIT otherwise)
  454. if(ti.is() || cur>=0 || sel>=0) // we have some text to draw, or we may encounter cursor or selection ahead
  455. if(C Font *font=getFont())
  456. {
  457. Int pos=0, cur_chr=-1;
  458. Flt cur_x, sel_x;
  459. #if DEBUG
  460. cur_x=sel_x=0; // to prevent run-time check exceptions on debug mode
  461. #endif
  462. // set
  463. Flt xsize =size.x/font->height(), // width of 1 font texel
  464. ysize =size.y/font->height(), // height of 1 font texel
  465. space =size.x*T.space.x,
  466. x_align_mul =align.x*0.5f-0.5f,
  467. y_align_mul =align.y*0.5f+0.5f,
  468. total_width =(Equal(x_align_mul, 0) ? 0 : ti.t8 ? textWidth(ti.t8, max_length) : textWidth(ti.t16, max_length)), // don't calculate text width when not needed
  469. total_height=size.y;
  470. x +=total_width *x_align_mul - xsize*font->paddingL();
  471. y +=total_height*y_align_mul + ysize*font->paddingT();
  472. if(spacing==SPACING_CONST)x+=space*0.5f; // put the cursor at the center where characters will be drawn, later this will be repositioned by half char width
  473. Vec2 p(x, y); if(pixel_align)D.alignScreenToPixel(p);
  474. // draw
  475. if(ti.is())
  476. {
  477. Flt xsize_2=xsize*0.5f;
  478. // texture bias
  479. {
  480. #if DX9
  481. U32 bias=(U32&)D._font_sharpness; bias^=SIGN_BIT; D3D->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, bias);
  482. #elif DX11
  483. // bias is set using 'SamplerFont' which is accessed in 'Font' shader
  484. #elif GL
  485. // bias is set using Font.setGLFont and Image.setGLFont
  486. #endif
  487. }
  488. // sub-pixel rendering
  489. ALPHA_MODE alpha;
  490. Bool sub_pixel=font->_sub_pixel;
  491. if(sub_pixel){alpha=D.alpha(Renderer.inside() ? ALPHA_FONT_DEC : ALPHA_FONT); VI.color2(TRANSPARENT);}else // if drawing text while rendering, then decrease the alpha channel (glow)
  492. if(Renderer.inside())D.alpha(ALPHA_BLEND_DEC); // if drawing text while rendering, then decrease the alpha channel (glow), but don't bother to restore it, as in Rendering, alpha blending is always set for each call
  493. Int cur_code_index=FindCode(code, codes, ti.p());
  494. C TextCodeData * cur_code =(InRange(cur_code_index , codes) ? &code[cur_code_index ] : null),
  495. *next_code =(InRange(cur_code_index+1, codes) ? &code[cur_code_index+1] : null);
  496. // font params
  497. Flt contrast;
  498. Byte lum=color.lum(); if(!lum)contrast=1;else
  499. {
  500. Flt pixels =Renderer.screenToPixelSize(size).min();
  501. if( pixels>=32)contrast=1;else
  502. {
  503. contrast=32/pixels;
  504. contrast=Sqrt(contrast); // or alternative: contrast=Log2(contrast+1);
  505. contrast=Max(1, contrast);
  506. contrast=Lerp(1.0f, contrast, lum/255.0f);
  507. }
  508. }
  509. Sh.h_FontContrast->set(contrast );
  510. Sh.h_FontShade ->set(shade/255.0f);
  511. SetCode(cur_code, T, sub_pixel);
  512. // font depth
  513. if(D._text_depth) // apply new state
  514. {
  515. D .depthLock (true );
  516. D .depthWrite(false); Renderer.needDepthTest(); // !! 'needDepthTest' after 'depthWrite' !!
  517. VI.shader (Sh.h_FontD);
  518. }
  519. for(Char c=ti.c(); c; pos++)
  520. {
  521. if(!max_length--)break;
  522. // check if encountered next code
  523. for(; next_code && ti.p()>=next_code->pos; )
  524. {
  525. cur_code_index++;
  526. cur_code=next_code;
  527. next_code=(InRange(cur_code_index+1, codes) ? &code[cur_code_index+1] : null);
  528. SetCode(cur_code, T, sub_pixel);
  529. }
  530. // next character
  531. Char n=(ti.t8 ? Char8To16Fast(*++ti.t8) : *++ti.t16);
  532. // draw character
  533. if(c!=' ') // don't draw space sign, potentially we could check it below using "fc.height" instead, however this is faster
  534. {
  535. UInt index=font->_wide_to_font[U16(c)]; if(InRange(index, font->_chrs))
  536. {
  537. C Font::Chr &fc=font->_chrs[index];
  538. //if(fc.height) // potentially we could check for empty characters (full width space, half width space, nbsp, however in most cases we will have something to draw, so this check will slow down things
  539. {
  540. Vec2 c_pos=p;
  541. if(spacing==SPACING_CONST)c_pos.x-=xsize_2*fc.width ; // move back by half of the character width
  542. c_pos.y-=ysize *fc.offset;
  543. if(pixel_align)D.alignScreenXToPixel(c_pos.x);
  544. VI.image(&font->_images[fc.image]);
  545. Rect_LU rect(c_pos, xsize*fc.width_padd, ysize*fc.height_padd);
  546. if(sub_pixel)VI.imagePart(rect, fc.tex);
  547. else VI.font (rect, fc.tex);
  548. // combining
  549. UInt flag=CharFlagFast(n); if(flag&CHARF_COMBINING)
  550. {
  551. c_pos.x+=xsize_2*fc.width; // after 'c_pos' was pixel aligned, move by half of the character width to put it at centerX of 'c' character
  552. c_pos.y =p.y; // reset Y pos
  553. if(flag&CHARF_STACK)c_pos.y+=ysize*font->paddingB(); // skip shadow padding at the bottom, because next character is drawn after base character and on top, so its bottom shadow must not overlap the base
  554. again:
  555. UInt index=font->_wide_to_font[U16(n)]; if(InRange(index, font->_chrs))
  556. {
  557. if(!max_length--)break;
  558. // update positions
  559. if(cur==pos){cur_x=p.x; cur_chr=c;}
  560. if(sel==pos){sel_x=p.x;}
  561. pos++;
  562. C Font::Chr &fc=font->_chrs[index];
  563. Vec2 n_pos=c_pos;
  564. n_pos.x-=xsize_2*fc.width; // move back by half of the character width
  565. n_pos.y-=ysize *fc.offset;
  566. if(pixel_align)D.alignScreenXToPixel(n_pos.x);
  567. VI.image(&font->_images[fc.image]);
  568. Rect_LU rect(n_pos, xsize*fc.width_padd, ysize*fc.height_padd);
  569. if(sub_pixel)VI.imagePart(rect, fc.tex);
  570. else VI.font (rect, fc.tex);
  571. n=(ti.t8 ? Char8To16Fast(*++ti.t8) : *++ti.t16);
  572. flag=CharFlagFast(n); if(flag&CHARF_COMBINING) // if next character is combining too
  573. {
  574. if(flag&CHARF_STACK)c_pos.y+=ysize*fc.height_padd; // move position higher, to stack combining characters on top of each other (needed for THAI)
  575. goto again;
  576. }
  577. }
  578. }
  579. }
  580. }
  581. }
  582. // update positions
  583. if(cur==pos){cur_x=p.x; cur_chr=c;}
  584. if(sel==pos){sel_x=p.x;}
  585. p.x+=space + xsize*font->charWidth(c, n, spacing);
  586. c=n;
  587. }
  588. VI.end();
  589. // texture bias
  590. {
  591. #if DX9
  592. D3D->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, 0);
  593. #elif DX11
  594. // bias is set using 'SamplerFont' which is accessed in 'Font' shader
  595. #elif GL
  596. // bias is set using 'Font.setGLFont' and 'Image.setGLFont'
  597. #endif
  598. }
  599. // sub-pixel
  600. if(sub_pixel)D.alpha(alpha);
  601. // font depth
  602. if(D._text_depth) // reset old state
  603. {
  604. D.depthUnlock();
  605. D.depthWrite (true);
  606. }
  607. }
  608. // selection
  609. if(sel!=SIGN_BIT && cur!=SIGN_BIT) // have some selection
  610. {
  611. Int min, max; Rect rect;
  612. if(sel<cur)
  613. {
  614. min=sel; rect.min.x=sel_x;
  615. max=cur; rect.max.x=cur_x;
  616. }else
  617. {
  618. min=cur; rect.min.x=cur_x;
  619. max=sel; rect.max.x=sel_x;
  620. }
  621. if(max>=0 && min<pos) // if selection intersects with this text
  622. {
  623. if(min<=0 )rect.min.x= x; // if minimum is before the start of this text, then use starting position
  624. if(max>=pos)rect.max.x=p.x; // if maximum is after the end of this text, then use current position
  625. if(spacing==SPACING_CONST){rect.min.x-=space*0.5f; rect.max.x-=space*0.5f;} // revert what we've applied
  626. Flt h=lineHeight();
  627. rect. setY(y-h, p.y); D.alignScreenYToPixel(rect.min.y); // use "y-h" instead of "p.y-h" to get exact value of the next line
  628. rect.moveY((h-size.y)/2); // adjust rectangle so text is at selection center
  629. rect.draw(selection);
  630. }
  631. }
  632. // cursor
  633. if(cur==pos){Char c=ti.c(); if(!c || c=='\n' || (SKIP_SPACE && c==' ' && spacing!=SPACING_CONST /*&& auto_line!=AUTO_LINE_NONE*/)){cur_x=p.x; cur_chr=0;}} // allow drawing cursor at the end only if it's followed by new line or null (this prevents drawing cursors 2 times for split lines - at the end of 1st line and at the beginning of 2nd line)
  634. if(cur_chr>=0 && !Kb._hidden)
  635. {
  636. if(spacing==SPACING_CONST)cur_x-=space*0.5f; // revert what we've applied
  637. if(!edit->overwrite)DrawKeyboardCursor (Vec2(cur_x, p.y), total_height);
  638. else DrawKeyboardCursorOverwrite(Vec2(cur_x, p.y), total_height, T, Char(cur_chr));
  639. }
  640. }
  641. }
  642. struct FontDraw
  643. {
  644. struct Rect2
  645. {
  646. Rect src, dest;
  647. };
  648. Rect2 rect[65536/SIZE(Rect2)];
  649. Int rects, mip_map;
  650. Flt contrast, shadow;
  651. Vec4 color;
  652. RectI clip;
  653. Bool sub_pixel;
  654. C Image *src;
  655. Image &dest;
  656. FontDraw(Image &dest, Int mip_map, Bool sub_pixel) : dest(dest), mip_map(Max(0, mip_map)), sub_pixel(sub_pixel) {rects=0; clip.set(0, 0, dest.w(), dest.h()); clear();}
  657. ~FontDraw() {unlock();}
  658. void setCode(C TextCodeData *code, C TextStyleParams &text_style)
  659. {
  660. flush();
  661. //if(sub_pixel){Color c=((code && code-> color_mode!=TextCodeData::DEFAULT) ? code->color : text_style.color ) ; D.alphaFactor(c); c.r=c.g=c.b=c.a; VI.color(c);}else
  662. {color =((code && code-> color_mode!=TextCodeData::DEFAULT) ? code->color : text_style.color ).asVec4();
  663. shadow=((code && code->shadow_mode!=TextCodeData::DEFAULT) ? code->shadow : text_style.shadow)/255.0f ;}
  664. }
  665. void flush()
  666. {
  667. FREP(rects)
  668. {
  669. C Rect2 &r=rect[i];
  670. RectI dest;
  671. dest.set(Trunc(r.dest.min.x), Trunc(r.dest.max.y), Ceil(r.dest.max.x), Ceil(r.dest.min.y));
  672. dest&=clip;
  673. Vec2 mul_add_x((r.src.max.x-r.src.min.x) / (r.dest.max.x - r.dest.min.x) * src->lw(),
  674. (r.src.min.x + (r.src.max.x-r.src.min.x) * (0.5f - r.dest.min.x) / (r.dest.max.x - r.dest.min.x)) * src->lw() - 0.5f);
  675. Vec2 mul_add_y((r.src.min.y-r.src.max.y) / (r.dest.max.y - r.dest.min.y) * src->lh(),
  676. (r.src.max.y + (r.src.min.y-r.src.max.y) * (0.5f - r.dest.min.y) / (r.dest.max.y - r.dest.min.y)) * src->lh() - 0.5f);
  677. for(Int y=dest.min.y; y<dest.max.y; y++)
  678. {
  679. //Flt fy=LerpR(r.dest.min.y, r.dest.max.y, y+0.5f); // fy=(y+0.5f - r.dest.min.y)/(r.dest.max.y - r.dest.min.y)
  680. //Flt ty=Lerp (r.src .max.y, r.src .min.y, fy)*src->lh()-0.5f; // ty=(r.src.max.y + (r.src.min.y-r.src.max.y) * fy)*src->lh()-0.5f
  681. // ty=(r.src.max.y + (r.src.min.y-r.src.max.y) * (y+0.5f - r.dest.min.y)/(r.dest.max.y - r.dest.min.y))*src->lh()-0.5f
  682. // ty=y * (r.src.min.y-r.src.max.y) / (r.dest.max.y - r.dest.min.y) * src->lh()
  683. // + (r.src.max.y + (r.src.min.y-r.src.max.y) * (0.5f - r.dest.min.y) / (r.dest.max.y - r.dest.min.y)) * src->lh() - 0.5f
  684. Flt ty=y*mul_add_y.x+mul_add_y.y;
  685. for(Int x=dest.min.x; x<dest.max.x; x++)
  686. {
  687. //Flt fx=LerpR(r.dest.min.x, r.dest.max.x, x+0.5f);
  688. //Flt tx=Lerp (r.src .min.x, r.src .max.x, fx)*src->lw()-0.5f;
  689. Flt tx=x*mul_add_x.x+mul_add_x.y;
  690. Vec4 c=src->colorFLinear(tx, ty);
  691. Flt a=Min(c.y*contrast, 1), // font opacity, scale up by 'contrast' to improve quality when font is very small
  692. s= c.w*shadow ; // font shadow
  693. // Flt final_alpha=1-(1-s)*(1-a);
  694. // 1-(1-s)*(1-a)
  695. // 1-(1-a-s+sa)
  696. // 1-1+a+s-sa
  697. // a + s - s*a
  698. Flt final_alpha=a+s-s*a;
  699. // ALPHA_MERGE:
  700. Vec4 out;
  701. out.xyz=color.xyz*(a*color.w);
  702. out.w =color.w*final_alpha;
  703. T.dest.merge(x, y, out);
  704. }
  705. }
  706. }
  707. rects=0;
  708. }
  709. void clear () {src=null;}
  710. void unlock() {if(src){flush(); src->unlock();}}
  711. Bool draw(C Image &image, C Rect &rect_src, C Rect &rect_dest)
  712. {
  713. if(src!=&image)
  714. {
  715. unlock();
  716. if(image.lockRead(Min(mip_map, image.mipMaps()-1)))
  717. {
  718. src=&image;
  719. }else
  720. {
  721. clear(); return false;
  722. }
  723. }
  724. if(!InRange(rects, rect))flush();
  725. Rect2 &r=rect[rects++];
  726. r.src =rect_src;
  727. r.dest=rect_dest;
  728. return true;
  729. }
  730. };
  731. void TextStyleParams::drawMainSoft(Image &image, Flt x, Flt y, TextInput ti, Int max_length, C TextCodeData *code, Int codes, Int offset)C
  732. {
  733. if(ti.is()) // we have some text to draw
  734. if(C Font *font=getFont())
  735. {
  736. // set
  737. Flt xsize = size.x/font->height(), // width of 1 font texel
  738. ysize =-size.y/font->height(), // height of 1 font texel
  739. space = size.x*T.space.x,
  740. x_align_mul =align.x*0.5f-0.5f,
  741. y_align_mul =align.y*0.5f+0.5f,
  742. total_width =(Equal(x_align_mul, 0) ? 0 : ti.t8 ? textWidth(ti.t8, max_length) : textWidth(ti.t16, max_length)), // don't calculate text width when not needed
  743. total_height=-size.y;
  744. x +=total_width *x_align_mul - xsize*font->paddingL();
  745. y +=total_height*y_align_mul + ysize*font->paddingT();
  746. if(spacing==SPACING_CONST)x+=space*0.5f; // put the cursor at the center where characters will be drawn, later this will be repositioned by half char width
  747. Vec2 p(x, y); if(pixel_align)p=Round(p);
  748. // draw
  749. //if(ti.is())
  750. {
  751. Flt xsize_2=xsize*0.5f;
  752. // sub-pixel rendering
  753. Flt max_size=Max(Abs(xsize), Abs(ysize)), mip_map=0.5f*Log2(Sqr(1/max_size));
  754. FontDraw draw(image, Round(mip_map), font->_sub_pixel);
  755. Int cur_code_index=FindCode(code, codes, ti.p());
  756. C TextCodeData * cur_code =(InRange(cur_code_index , codes) ? &code[cur_code_index ] : null),
  757. *next_code =(InRange(cur_code_index+1, codes) ? &code[cur_code_index+1] : null);
  758. // font params
  759. Byte lum=color.lum(); if(!lum)draw.contrast=1;else
  760. {
  761. Flt pixels =size.min();
  762. if( pixels>=32)draw.contrast=1;else
  763. {
  764. draw.contrast=32/pixels;
  765. draw.contrast=Sqrt(draw.contrast); // or alternative: draw.contrast=Log2(draw.contrast+1);
  766. draw.contrast=Max(1, draw.contrast);
  767. draw.contrast=Lerp(1.0f, draw.contrast, lum/255.0f);
  768. }
  769. }
  770. draw.setCode(cur_code, T);
  771. for(Char c=ti.c(); c; )
  772. {
  773. if(!max_length--)break;
  774. // check if encountered next code
  775. for(; next_code && ti.p()>=next_code->pos; )
  776. {
  777. cur_code_index++;
  778. cur_code=next_code;
  779. next_code=(InRange(cur_code_index+1, codes) ? &code[cur_code_index+1] : null);
  780. draw.setCode(cur_code, T);
  781. }
  782. // next character
  783. Char n=(ti.t8 ? Char8To16Fast(*++ti.t8) : *++ti.t16);
  784. // draw character
  785. if(c!=' ') // don't draw space sign, potentially we could check it below using "fc.height" instead, however this is faster
  786. {
  787. UInt index=font->_wide_to_font[U16(c)]; if(InRange(index, font->_chrs))
  788. {
  789. C Font::Chr &fc=font->_chrs[index];
  790. //if(fc.height) // potentially we could check for empty characters (full width space, half width space, nbsp, however in most cases we will have something to draw, so this check will slow down things
  791. {
  792. Vec2 c_pos=p;
  793. if(spacing==SPACING_CONST)c_pos.x-=xsize_2*fc.width ; // move back by half of the character width
  794. c_pos.y-=ysize *fc.offset;
  795. if(pixel_align)c_pos.x=Round(c_pos.x);
  796. draw.draw(font->_images[fc.image], fc.tex, Rect_LU(c_pos, xsize*fc.width_padd, ysize*fc.height_padd));
  797. // combining
  798. UInt flag=CharFlagFast(n); if(flag&CHARF_COMBINING)
  799. {
  800. c_pos.x+=xsize_2*fc.width; // after 'c_pos' was pixel aligned, move by half of the character width to put it at centerX of 'c' character
  801. c_pos.y =p.y; // reset Y pos
  802. if(flag&CHARF_STACK)c_pos.y+=ysize*font->paddingB(); // skip shadow padding at the bottom, because next character is drawn after base character and on top, so its bottom shadow must not overlap the base
  803. again:
  804. UInt index=font->_wide_to_font[U16(n)]; if(InRange(index, font->_chrs))
  805. {
  806. if(!max_length--)break;
  807. C Font::Chr &fc=font->_chrs[index];
  808. Vec2 n_pos=c_pos;
  809. n_pos.x-=xsize_2*fc.width; // move back by half of the character width
  810. n_pos.y-=ysize *fc.offset;
  811. if(pixel_align)n_pos.x=Round(n_pos.x);
  812. draw.draw(font->_images[fc.image], fc.tex, Rect_LU(n_pos, xsize*fc.width_padd, ysize*fc.height_padd));
  813. n=(ti.t8 ? Char8To16Fast(*++ti.t8) : *++ti.t16);
  814. flag=CharFlagFast(n); if(flag&CHARF_COMBINING) // if next character is combining too
  815. {
  816. if(flag&CHARF_STACK)c_pos.y+=ysize*fc.height_padd; // move position higher, to stack combining characters on top of each other (needed for THAI)
  817. goto again;
  818. }
  819. }
  820. }
  821. }
  822. }
  823. }
  824. // update positions
  825. p.x+=space + xsize*font->charWidth(c, n, spacing);
  826. c=n;
  827. }
  828. }
  829. }
  830. }
  831. /******************************************************************************/
  832. void TextStyleParams::drawSplit(C Rect &rect, Memc<TextLineSplit8> &tls, C TextCodeData *code, Int codes)C
  833. {
  834. Flt h=lineHeight();
  835. Vec2 p(rect.lerpX(align.x*-0.5f+0.5f),
  836. Lerp(rect.min.y+(tls.elms()-1)*h, rect.max.y, align.y*-0.5f+0.5f));
  837. if(pixel_align)D.alignScreenToPixel(p); // align here to prevent jittering between lines when moving the whole text
  838. C Rect &clip=D._clip ? D._clip_rect : D.viewRect();
  839. Vec2 range; posY(p.y, range);
  840. Int start=Max( 0, Floor((range.y-clip.max.y)/h)),
  841. end =Min(tls.elms()-1, Ceil ((range.x-clip.min.y)/h));
  842. #if DEBUG && 0
  843. D.clip(null);
  844. D.lineX(RED, range.y , -D.w(), D.w()); // max
  845. D.lineX(RED, range.x-(tls.elms()-1)*h, -D.w(), D.w()); // min
  846. D.text(0, D.h()*0.9f, S+start+' '+end);
  847. #endif
  848. for(; start<=end; start++){auto &t=tls[start]; drawMain(p.x, p.y-start*h, t.text, t.length, code, codes, t.offset);}
  849. }
  850. void TextStyleParams::drawSplit(C Rect &rect, Memc<TextLineSplit16> &tls, C TextCodeData *code, Int codes)C
  851. {
  852. Flt h=lineHeight();
  853. Vec2 p(rect.lerpX(align.x*-0.5f+0.5f),
  854. Lerp(rect.min.y+(tls.elms()-1)*h, rect.max.y, align.y*-0.5f+0.5f));
  855. if(pixel_align)D.alignScreenToPixel(p); // align here to prevent jittering between lines when moving the whole text
  856. C Rect &clip=D._clip ? D._clip_rect : D.viewRect();
  857. Vec2 range; posY(p.y, range);
  858. Int start=Max( 0, Floor((range.y-clip.max.y)/h)),
  859. end =Min(tls.elms()-1, Ceil ((range.x-clip.min.y)/h));
  860. #if DEBUG && 0
  861. D.clip(null);
  862. D.lineX(RED, range.y , -D.w(), D.w()); // max
  863. D.lineX(RED, range.x-(tls.elms()-1)*h, -D.w(), D.w()); // min
  864. D.text(0, D.h()*0.9f, S+start+' '+end);
  865. #endif
  866. for(; start<=end; start++){auto &t=tls[start]; drawMain(p.x, p.y-start*h, t.text, t.length, code, codes, t.offset);}
  867. }
  868. /******************************************************************************/
  869. void TextStyleParams::drawSplitSoft(Image &image, C Rect &rect, Memt<TextLineSplit8> &tls, C TextCodeData *code, Int codes)C
  870. {
  871. Flt h=lineHeight();
  872. Vec2 p(rect.lerpX(align.x*-0.5f+0.5f),
  873. Lerp(rect.max.y-(tls.elms()-1)*h, rect.min.y, align.y*-0.5f+0.5f));
  874. if(pixel_align)p=Round(p); // align here to prevent jittering between lines when moving the whole text
  875. Rect clip(0, image.size());
  876. Vec2 range; posYI(p.y, range);
  877. Int start=Max( 0, Floor((clip.min.y-range.x)/h)),
  878. end =Min(tls.elms()-1, Ceil ((clip.max.y-range.y)/h));
  879. for(; start<=end; start++){auto &t=tls[start]; drawMainSoft(image, p.x, p.y+start*h, t.text, t.length, code, codes, t.offset);}
  880. }
  881. void TextStyleParams::drawSplitSoft(Image &image, C Rect &rect, Memt<TextLineSplit16> &tls, C TextCodeData *code, Int codes)C
  882. {
  883. Flt h=lineHeight();
  884. Vec2 p(rect.lerpX(align.x*-0.5f+0.5f),
  885. Lerp(rect.max.y-(tls.elms()-1)*h, rect.min.y, align.y*-0.5f+0.5f));
  886. if(pixel_align)p=Round(p); // align here to prevent jittering between lines when moving the whole text
  887. Rect clip(0, image.size());
  888. Vec2 range; posYI(p.y, range);
  889. Int start=Max( 0, Floor((clip.min.y-range.x)/h)),
  890. end =Min(tls.elms()-1, Ceil ((clip.max.y-range.y)/h));
  891. for(; start<=end; start++){auto &t=tls[start]; drawMainSoft(image, p.x, p.y+start*h, t.text, t.length, code, codes, t.offset);}
  892. }
  893. /******************************************************************************/
  894. void TextStyleParams::drawCode(C Rect &rect, CChar8 *t, AUTO_LINE_MODE auto_line, C TextCodeData *code, Int codes)C {Set(Tls8 , t, T, rect.w(), auto_line); drawSplit(rect, Tls8 , code, codes);}
  895. void TextStyleParams::drawCode(C Rect &rect, CChar *t, AUTO_LINE_MODE auto_line, C TextCodeData *code, Int codes)C {Set(Tls16, t, T, rect.w(), auto_line); drawSplit(rect, Tls16, code, codes);}
  896. /******************************************************************************/
  897. void DisplayDraw::text(C TextStyleParams &text_style, Flt x, Flt y, CChar8 *t) {text_style.drawMain(x, y, t);}
  898. void DisplayDraw::text(C TextStyleParams &text_style, Flt x, Flt y, CChar *t) {text_style.drawMain(x, y, t);}
  899. void DisplayDraw::text( Flt x, Flt y, CChar8 *t) {if(Gui.skin && Gui.skin->text_style)Gui.skin->text_style->drawMain(x, y, t);}
  900. void DisplayDraw::text( Flt x, Flt y, CChar *t) {if(Gui.skin && Gui.skin->text_style)Gui.skin->text_style->drawMain(x, y, t);}
  901. void DisplayDraw::text(C TextStyleParams &text_style, C Rect &rect, CChar8 *t, AUTO_LINE_MODE auto_line) {text_style.drawCode(rect, t, auto_line);}
  902. void DisplayDraw::text(C TextStyleParams &text_style, C Rect &rect, CChar *t, AUTO_LINE_MODE auto_line) {text_style.drawCode(rect, t, auto_line);}
  903. void DisplayDraw::text( C Rect &rect, CChar8 *t, AUTO_LINE_MODE auto_line) {if(Gui.skin && Gui.skin->text_style)Gui.skin->text_style->drawCode(rect, t, auto_line);}
  904. void DisplayDraw::text( C Rect &rect, CChar *t, AUTO_LINE_MODE auto_line) {if(Gui.skin && Gui.skin->text_style)Gui.skin->text_style->drawCode(rect, t, auto_line);}
  905. /******************************************************************************/
  906. void TextStyleParams::drawSoft(Image &image, Flt x, Flt y, CChar8 *t)C {drawMainSoft(image, x, y, t);}
  907. void TextStyleParams::drawSoft(Image &image, Flt x, Flt y, CChar *t)C {drawMainSoft(image, x, y, t);}
  908. void TextStyleParams::drawSoft(Image &image, C Rect &rect, CChar8 *t, AUTO_LINE_MODE auto_line)C {Memt<TextLineSplit8 > tls8 ; Set(tls8 , t, T, rect.w(), auto_line); drawSplitSoft(image, rect, tls8 );} // use temp because this can be called outside of Draw
  909. void TextStyleParams::drawSoft(Image &image, C Rect &rect, CChar *t, AUTO_LINE_MODE auto_line)C {Memt<TextLineSplit16> tls16; Set(tls16, t, T, rect.w(), auto_line); drawSplitSoft(image, rect, tls16);} // use temp because this can be called outside of Draw
  910. /******************************************************************************/
  911. // IO
  912. /******************************************************************************/
  913. #pragma pack(push, 1)
  914. struct TextStyleDesc
  915. {
  916. SPACING_MODE spacing;
  917. Byte shadow, shade;
  918. Color color, selection;
  919. Vec2 align, size, space;
  920. };
  921. #pragma pack(pop)
  922. Bool TextStyle::save(File &f, CChar *path)C
  923. {
  924. f.putMulti(UInt(CC4_TXDS), Byte(4)); // version
  925. TextStyleDesc desc;
  926. Unaligned(desc.spacing , spacing);
  927. Unaligned(desc.shadow , shadow);
  928. Unaligned(desc.shade , shade);
  929. Unaligned(desc.color , color);
  930. Unaligned(desc.selection, selection);
  931. Unaligned(desc.align , align);
  932. Unaligned(desc.size , size);
  933. Unaligned(desc.space , space);
  934. f<<desc;
  935. f._putAsset(font().name(path));
  936. return f.ok();
  937. }
  938. Bool TextStyle::load(File &f, CChar *path)
  939. {
  940. pixel_align=true;
  941. edit=null;
  942. if(f.getUInt()==CC4_TXDS)switch(f.decUIntV()) // version
  943. {
  944. case 4:
  945. {
  946. TextStyleDesc desc; if(f.getFast(desc))
  947. {
  948. Unaligned(spacing , desc.spacing);
  949. Unaligned(shadow , desc.shadow);
  950. Unaligned(shade , desc.shade);
  951. Unaligned(color , desc.color);
  952. Unaligned(selection, desc.selection);
  953. Unaligned(align , desc.align);
  954. Unaligned(size , desc.size);
  955. Unaligned(space , desc.space);
  956. _font.require(f._getAsset(), path); super::font(_font());
  957. if(f.ok())return true;
  958. }
  959. }break;
  960. case 3:
  961. {
  962. #pragma pack(push, 1)
  963. struct TextStyleDesc
  964. {
  965. SPACING_MODE spacing;
  966. Byte shadow, shade;
  967. Color color;
  968. Vec2 align, size, space;
  969. };
  970. #pragma pack(pop)
  971. TextStyleDesc desc; if(f.get(desc))
  972. {
  973. Unaligned(spacing, desc.spacing);
  974. Unaligned(shadow , desc.shadow);
  975. Unaligned(shade , desc.shade);
  976. Unaligned(color , desc.color);
  977. Unaligned(align , desc.align);
  978. Unaligned(size , desc.size);
  979. Unaligned(space , desc.space);
  980. _font.require(f._getStr(), path); super::font(_font());
  981. selection=DefaultSelectionColor;
  982. if(f.ok())return true;
  983. }
  984. }break;
  985. case 2:
  986. {
  987. #pragma pack(push, 4)
  988. struct TextStyleDesc
  989. {
  990. SPACING_MODE spacing;
  991. Byte shadow, shade;
  992. Color color;
  993. Vec2 align, size, space;
  994. };
  995. #pragma pack(pop)
  996. TextStyleDesc desc; if(f.get(desc))
  997. {
  998. Unaligned(spacing, desc.spacing);
  999. Unaligned(shadow , desc.shadow);
  1000. Unaligned(shade , desc.shade);
  1001. Unaligned(color , desc.color);
  1002. Unaligned(align , desc.align);
  1003. Unaligned(size , desc.size);
  1004. Unaligned(space , desc.space);
  1005. _font.require(f._getStr(), path); super::font(_font());
  1006. selection=DefaultSelectionColor;
  1007. if(f.ok())return true;
  1008. }
  1009. }break;
  1010. case 1:
  1011. {
  1012. #pragma pack(push, 4)
  1013. struct TextStyleDesc
  1014. {
  1015. SPACING_MODE spacing;
  1016. Byte shadow, shade;
  1017. VecB4 color;
  1018. Vec2 align, size, space;
  1019. };
  1020. #pragma pack(pop)
  1021. TextStyleDesc desc; if(f.get(desc))
  1022. {
  1023. Unaligned(spacing, desc.spacing);
  1024. Unaligned(shadow , desc.shadow);
  1025. Unaligned(shade , desc.shade);
  1026. color .set(Unaligned(desc.color.z), Unaligned(desc.color.y), Unaligned(desc.color.x), Unaligned(desc.color.w));
  1027. Unaligned(align , desc.align);
  1028. Unaligned(size , desc.size);
  1029. Unaligned(space , desc.space);
  1030. _font.require(f._getStr(), path); super::font(_font());
  1031. selection=DefaultSelectionColor;
  1032. if(f.ok())return true;
  1033. }
  1034. }break;
  1035. case 0:
  1036. {
  1037. #pragma pack(push, 4)
  1038. struct TextStyleDesc
  1039. {
  1040. SPACING_MODE spacing;
  1041. Byte shadow, shade;
  1042. VecB4 color;
  1043. Vec2 align, size, space;
  1044. };
  1045. #pragma pack(pop)
  1046. TextStyleDesc desc; if(f.get(desc))
  1047. {
  1048. Unaligned(spacing, desc.spacing);
  1049. Unaligned(shadow , desc.shadow);
  1050. shade =DefaultShade;
  1051. color .set(Unaligned(desc.color.z), Unaligned(desc.color.y), Unaligned(desc.color.x), Unaligned(desc.color.w));
  1052. Unaligned(align , desc.align);
  1053. Unaligned(size , desc.size);
  1054. Unaligned(space , desc.space);
  1055. _font.require(f._getStr8(), path); super::font(_font());
  1056. selection=DefaultSelectionColor;
  1057. if(f.ok())return true;
  1058. }
  1059. }break;
  1060. }
  1061. reset(); return false;
  1062. }
  1063. Bool TextStyle::save(C Str &name)C
  1064. {
  1065. File f; if(f.writeTry(name)){if(save(f, _GetPath(name)) && f.flush())return true; f.del(); FDelFile(name);}
  1066. return false;
  1067. }
  1068. Bool TextStyle::load(C Str &name)
  1069. {
  1070. File f; if(f.readTry(name))return load(f, _GetPath(name));
  1071. reset(); return false;
  1072. }
  1073. void TextStyle::operator=(C Str &name)
  1074. {
  1075. if(!load(name))Exit(MLT(S+"Can't load Text Style \"" +name+"\"",
  1076. PL,S+u"Nie można wczytać stylu tekstu \""+name+"\""));
  1077. }
  1078. /******************************************************************************/
  1079. // TEXT CODE
  1080. /******************************************************************************/
  1081. static TextCodeData& NewCode(MemPtr<TextCodeData> codes, Str &text)
  1082. {
  1083. TextCodeData &code=codes.New(); code.pos=(CPtr)text.length();
  1084. if(codes.elms()>=2)
  1085. {
  1086. TextCodeData &last=codes[codes.elms()-2];
  1087. code.shadow_mode=((last.shadow_mode==TextCodeData::DEFAULT) ? TextCodeData::DEFAULT : TextCodeData::PREV); code.shadow=last.shadow;
  1088. code. color_mode=((last. color_mode==TextCodeData::DEFAULT) ? TextCodeData::DEFAULT : TextCodeData::PREV); code.color =last.color ;
  1089. code.nocode_mode=((last.nocode_mode==TextCodeData::DEFAULT) ? TextCodeData::DEFAULT : TextCodeData::PREV);
  1090. }else
  1091. {
  1092. code.shadow_mode=code.color_mode=code.nocode_mode=TextCodeData::DEFAULT;
  1093. code.shadow=255;
  1094. code.color =WHITE;
  1095. }
  1096. return code;
  1097. }
  1098. static Bool GetDig(Byte &dig, CChar* &text)
  1099. {
  1100. Int i=CharInt(*text);
  1101. if( i>=0){dig=i; text++; return true;}
  1102. return false;
  1103. }
  1104. static Color GetColor(CChar *text)
  1105. {
  1106. text=_SkipWhiteChars(text);
  1107. Byte dig[8], digs=0; FREP(8)if(GetDig(dig[i], text))digs++;else break;
  1108. switch(digs)
  1109. {
  1110. case 3: return Color( dig[0]*255/0xF , dig[1]*255/0xF , dig[2]*255/0xF ); // RGB
  1111. case 4: return Color( dig[0]*255/0xF , dig[1]*255/0xF , dig[2]*255/0xF , dig[3]*255/0xF ); // RGBA
  1112. case 6: return Color((dig[0]<<4)|dig[1], (dig[2]<<4)|dig[3], (dig[4]<<4)|dig[5] ); // RRGGBB
  1113. case 8: return Color((dig[0]<<4)|dig[1], (dig[2]<<4)|dig[3], (dig[4]<<4)|dig[5], (dig[6]<<4)|dig[7]); // RRGGBBAA
  1114. default: return WHITE;
  1115. }
  1116. }
  1117. static Byte GetByte(CChar *text)
  1118. {
  1119. text=_SkipWhiteChars(text);
  1120. Byte dig[2], digs=0; FREP(2)if(GetDig(dig[i], text))digs++;else break;
  1121. switch(digs)
  1122. {
  1123. case 1: return dig[0]*255/0xF;
  1124. case 2: return (dig[0]<<4)|dig[1];
  1125. default: return 255;
  1126. }
  1127. }
  1128. void SetTextCode(C Str &code, Str &text, MemPtr<TextCodeData> codes)
  1129. {
  1130. // 'codes' initially uses 'pos' as Int
  1131. text .clear();
  1132. codes.clear();
  1133. Bool nocode=false;
  1134. MemtN<Color, 1024> colors ;
  1135. MemtN<Byte , 1024> shadows;
  1136. for(CChar *cc=code(); cc; )
  1137. {
  1138. Char c=*cc++; if(!c)break;
  1139. if(c=='[') // code sign
  1140. {
  1141. if(!nocode && (Starts(cc, "col=") || Starts(cc, "color=")))
  1142. {
  1143. if(cc=TextPos(cc, '='))
  1144. {
  1145. Color col =GetColor(cc+1);
  1146. TextCodeData &code=NewCode(codes, text);
  1147. code.color_mode=TextCodeData::NEW;
  1148. code.color =col; colors.New()=col;
  1149. }
  1150. if(cc=TextPos(cc, ']'))cc++;
  1151. }else
  1152. if(!nocode && (Starts(cc, "shd=") || Starts(cc, "shadow=")))
  1153. {
  1154. if(cc=TextPos(cc, '='))
  1155. {
  1156. Byte shd =GetByte(cc+1);
  1157. TextCodeData &code=NewCode(codes, text);
  1158. code.shadow_mode=TextCodeData::NEW;
  1159. code.shadow =shd; shadows.New()=shd;
  1160. }
  1161. if(cc=TextPos(cc, ']'))cc++;
  1162. }else
  1163. if(!nocode && Starts(cc, "/col"))
  1164. {
  1165. TextCodeData &code=NewCode(codes, text);
  1166. colors.removeLast();
  1167. if(colors.elms())
  1168. {
  1169. code.color_mode=TextCodeData::OLD;
  1170. code.color =colors.last();
  1171. }else
  1172. {
  1173. code.color_mode=TextCodeData::DEFAULT;
  1174. code.color =WHITE;
  1175. }
  1176. if(cc=TextPos(cc, ']'))cc++;
  1177. }else
  1178. if(!nocode && Starts(cc, "/sh"))
  1179. {
  1180. TextCodeData &code=NewCode(codes, text);
  1181. shadows.removeLast();
  1182. if(shadows.elms())
  1183. {
  1184. code.shadow_mode=TextCodeData::OLD;
  1185. code.shadow =shadows.last();
  1186. }else
  1187. {
  1188. code.shadow_mode=TextCodeData::DEFAULT;
  1189. code.shadow =255;
  1190. }
  1191. if(cc=TextPos(cc, ']'))cc++;
  1192. }else
  1193. if(Starts(cc, "nocode"))
  1194. {
  1195. nocode=true;
  1196. TextCodeData &code=NewCode(codes, text);
  1197. code.nocode_mode=TextCodeData::NEW;
  1198. if(cc=TextPos(cc, ']'))cc++;
  1199. }else
  1200. if(Starts(cc, "/nocode"))
  1201. {
  1202. nocode=false;
  1203. TextCodeData &code=NewCode(codes, text);
  1204. code.nocode_mode=TextCodeData::DEFAULT;
  1205. if(cc=TextPos(cc, ']'))cc++;
  1206. }else
  1207. {
  1208. text+='[';
  1209. }
  1210. }else
  1211. {
  1212. text+=c;
  1213. }
  1214. }
  1215. // update codes
  1216. REPA(codes)codes[i].pos=CPtr(text()+UIntPtr(codes[i].pos)); // convert Int to Ptr
  1217. }
  1218. Str GetTextCode(C Str &text, C TextCodeData *code, Int codes)
  1219. {
  1220. Str out;
  1221. Int cur_code=0, colors=0, shadows=0, nocodes=0;
  1222. for(Int i=0; ; i++)
  1223. {
  1224. for(; InRange(cur_code, codes) && text()+i>=code[cur_code].pos; )
  1225. {
  1226. C TextCodeData &c=code[cur_code++];
  1227. switch(c.color_mode)
  1228. {
  1229. case TextCodeData::NEW: out+="[color="; out+=c.color.asHex(); out+=']'; colors++; break;
  1230. case TextCodeData::DEFAULT: if(!colors)break; // on purpose without break
  1231. case TextCodeData::OLD : colors--; out+="[/color]"; break;
  1232. }
  1233. switch(c.shadow_mode)
  1234. {
  1235. case TextCodeData::NEW: out+="[shadow="; out+=TextHex(U32(c.shadow), 2); out+=']'; shadows++; break;
  1236. case TextCodeData::DEFAULT: if(!shadows)break; // on purpose without break
  1237. case TextCodeData::OLD : shadows--; out+="[/shadow]"; break;
  1238. }
  1239. switch(c.nocode_mode)
  1240. {
  1241. case TextCodeData::NEW: out+="[nocode]"; nocodes++; break;
  1242. case TextCodeData::DEFAULT: if(!nocodes)break; // on purpose without break
  1243. case TextCodeData::OLD : nocodes--; out+="[/nocode]"; break;
  1244. }
  1245. }
  1246. if(i>=text.length())break;
  1247. out+=text[i];
  1248. }
  1249. return out;
  1250. }
  1251. /******************************************************************************/
  1252. TextCode& TextCode::clear( ) { T._text.clear(); _codes.clear(); return T;}
  1253. TextCode& TextCode::set (C Str &text) { T._text=text ; _codes.clear(); return T;}
  1254. TextCode& TextCode::code (C Str &code) { SetTextCode(code, _text , _codes ); return T;}
  1255. Str TextCode::code ( )C {return GetTextCode(T(), _codes.data(), _codes.elms());}
  1256. void TextCode::draw(C TextStyleParams &text_style, C Rect &rect, AUTO_LINE_MODE auto_line)C
  1257. {
  1258. text_style.drawCode(rect, T(), auto_line, _codes.data(), _codes.elms());
  1259. }
  1260. void TextCode::draw(C Rect &rect, AUTO_LINE_MODE auto_line)C
  1261. {
  1262. if(Gui.skin && Gui.skin->text_style)Gui.skin->text_style->drawCode(rect, T(), auto_line, _codes.data(), _codes.elms());
  1263. }
  1264. /******************************************************************************/
  1265. }
  1266. /******************************************************************************/