Posted By: lemming
Colourful TEXT - 03/02/13 18:18
Hello everyone!
If you are like my annoyed that using different colours in TEXT objects is not possible, then I probably have exactly what you need:
COLOURFUL TEXT
aka CLTEXT
This lib wraps some TEXTs and arranges them in a way they look like a single TEXT with different colours.
(text from this pic)
Update 1.1:
CLTEXT is now part of the TUST-Project and will only be maintained there.
Changelog:
What it can beside beeing colourful:
- word wrap over different lines
- indent after the first line
What it can not (but will hopefully in the future):
- lf/cr as special character
A description of the functions is in the commentblock of the headerfile.
I coded this while having a cold, so there might be some small bugs or quirks. Please tell me them.
CLTEXT.H
The code for creating the scene in the picture is the following:
[/b][b]
If you are like my annoyed that using different colours in TEXT objects is not possible, then I probably have exactly what you need:
COLOURFUL TEXT
aka CLTEXT
This lib wraps some TEXTs and arranges them in a way they look like a single TEXT with different colours.
(text from this pic)
Update 1.1:
CLTEXT is now part of the TUST-Project and will only be maintained there.
Changelog:
Code:
1.1: * modified for TUST (mainly doxygen commenting and splitting file) + cltext_align added
What it can beside beeing colourful:
- word wrap over different lines
- indent after the first line
What it can not (but will hopefully in the future):
- lf/cr as special character
A description of the functions is in the commentblock of the headerfile.
I coded this while having a cold, so there might be some small bugs or quirks. Please tell me them.
Click to reveal..
CLTEXT.H
Code:
//////////////////////////////////////////////////////////////////////////////// // COLOURFUL TEXT ///////////////// // // author: lemming // version: 1.0 // version history: // 1.0: initial release // // Free to use for every 3DGS user, also for commercial projects. As long as it // does not violate the 3DGS terms of use. // If you extend the script file, I'd appreciate if you commit it to the // community. // //////////////////////////////////////////////////////////////////////////////// // // This library lets you create a CLTEXT-object, that holds several TEXT- // objects with different colours and arranges them so they look as a single // TEXT. This is done by parsing a tagged string. // example: "[clff0000]red [clffff00]yellow [cl177935] dark green" // The RGB-values are as hexcodes inside the text. The text following the tags // will be coloured. // // Features: // - word warapping // - indentation after word wrapping // // Functions: // // var cltext_hextovar(STRING* hexs, var* i) // // Converts a 2 digit hex to a var // return: 0 success, -1 no string, -2 too long string, -3 invalid chars // // // var str_width_hack(STRING* str, FONT* font) // // This is a "hacked" version of the str_width function. The original does // not take spaces at the end in account, this version does by adding a dot // at the end and then removing the width of the dot again. // return: The full length with trailing spaces taken in account. // // // var cltext_removetags(STRING* str) // // Removes tags from a string, for example when using it in a chat system // with user defined inputs. // return: how many tags has been removed // // // CLTEXT* cltext_create(STRING* text, FONT* font, var indent, var sizey, // var flags, COLOR* defaultcolor) // // Creates a CLTEXT object and parses the string. // // text: colourtagged text to display as a string // font: display text with this font // indent: after a linefeed use this indent (not in the first line) // sizey: max width for the text. Will word wrap at reaching it. // flags: not used, but written to the struct and can then be read out // dfltclr:colour to use when text has no colour tags // // return: CLTEXT pointer or NULL if failed to create. This can happen if // the max width is smaller than the indent. // // // var cltext_getheight(CLTEXT* cltext) // // Returns the height of a CLTEXT. This needs a font to be set. // return: > 0 for height or 0 if no font is set. // // // void* cltext_remove(CLTEXT* cltext); // // Removes a CLTEXT object from memory. // return: NULL // // // void cltext_show(CLTEXT* cltext, var posx, var posy, var alpha, // var layer); // // Shows, hides or moves the text. // // cltext: the CLTEXT object to handle // posx/y: screen coordinate to move to // alpha: alpha blending // 0 disables the SHOW tag // 100 disables the TRANSLUCENT flag // layer: layer to move the text to // // //////////////////////////////////////////////////////////////////////////////// #ifndef __CLTEXT_H #define __CLTEXT_H #include <strio.c> ////////////////////////// // Structs and Prototypes typedef struct CLLIST { TEXT* element; var linex; var liney; struct CLLIST* next; } CLLIST; typedef struct CLTEXT { STRING* label; CLLIST* texts; FONT* font; var indent; var sizey; var flags; var layer; var posx; var posy; } CLTEXT; // for internal use: var cltext_ValidClTag(STRING* text, var pos); var cltext_SearchTag(STRING* text, var from); var cltext_SearchSpaceBackw(STRING* str, var from); // for external use: var cltext_hextovar(STRING* hexs, var* i); var str_width_hack(STRING* str, FONT* font); var cltext_removetags(STRING* str); CLTEXT* cltext_create(STRING* text, FONT* font, var indent, var sizey, var flags, COLOR* defaultcolor); var cltext_getheight(CLTEXT* cltext); void* cltext_remove(CLTEXT* cltext); void cltext_show(CLTEXT* cltext, var posx, var posy, var alpha, var layer); ////////////////// // Implementation var cltext_hextovar(STRING* hexs, var* i) // turns hex-strings into var i // return: 0 success, -1 no string, -2 too long string, -3 invalid char { var hexl = str_len(hexs); if (hexl <= -1) return -1; // no string if (hexl > 2) return -2; // too long string var c; char hex[2]; for (c=0;c<hexl;c++) hex[c] = (hexs.chars)[c]; var factor=1; var h = 0; for (c=hexl-1;c>=0;c--) { if ((hex[c] >= 48) && (hex[c] <= 57)) // 0 - 9 h += (hex[c] - 48) * factor; else if ((hex[c] >= 65) && (hex[c] <= 70)) // A - F h += (hex[c] - 65 + 10) * factor; else if ((hex[c] >= 97) && (hex[c] <= 102)) // a - f h += (hex[c] - 97 + 10) * factor; else return -3; // invalid char factor *= 16; } *i = h; return 0; // success } var cltext_ValidClTag(STRING* text, var pos) // checks if a valid colour tag is at a given position // Does not check if the hex code is valid // return: 0 yes, != 0 no { if (pos > (str_len(text)-10)) return 5; var res = 0; if ((text.chars)[pos+0] != '[') res += 1; if ((text.chars)[pos+1] != 'c') res += 1; if ((text.chars)[pos+2] != 'l') res += 1; if ((text.chars)[pos+9] != ']') res += 1; return res; } var cltext_SearchTag(STRING* text, var from) // searches for a colour tag // return: position or -1 { var i; for (i = from; i < (str_len(text)-10); i++) { if (cltext_ValidClTag(text, i) == 0) return i; } return -1; } var str_width_hack(STRING* str, FONT* font) // returns the width of a string in pixels with trailing spaces // return: width { STRING* wrk = str_create(str); str_cat(wrk,"."); var strwidth = str_width(wrk,font) - str_width(".",font); ptr_remove(wrk); return strwidth; } var cltext_SearchSpaceBackw(STRING* str, var from) // searches spaces in a STRING backwards beginning at from // return: position or 0 { var i; for (i=from-1; i > 0; i--) { if (str_getchr(str,i) == 32) return i; } return 0; } var cltext_removetags(STRING* str) // removes colour tags from a string // return: count of tags removed { STRING* work = str_create(""); STRING* newstr = str_create(""); var i=0, count = 0; do { i = cltext_SearchTag(str, i); if (i > -1) { if (i > 0) str_cut(newstr, str, 0, i); else str_cpy(newstr, ""); str_cut(work, str, i+11, 0); str_cat(newstr,work); str_cpy(str,newstr); count += 1; } } while (i > -1); ptr_remove(newstr); ptr_remove(work); return count; } CLTEXT* cltext_create(STRING* text, FONT* font, var indent, var sizey, var flags, COLOR* defaultcolor) // creates a CLTEXT object // text: colourtagged text to display as a string // font: display text with this font // indent: after a linefeed use this indent (not in the first line) // sizey: max width for the text. Will word wrap at reaching it. // flags: not used, but written to the struct and can then be read out // dfltclr: colour to use when text has no colour tags { if (sizey < (indent+1)) return NULL; CLTEXT* cltext = sys_malloc(sizeof(CLTEXT)); cltext.label = str_create(text); cltext.texts = NULL; cltext.font = font; cltext.indent = indent; cltext.sizey = sizey; cltext.flags = flags; // First element CLLIST* current = sys_malloc(sizeof(CLLIST)); current.element = NULL; current.next = NULL; cltext.texts = current; STRING* newline = str_create(""); STRING* curline = str_create(""); STRING* worktext = str_create(""); var found = -1, cutstart = 0, cutend = -1, text_len = str_len(text); while (cutstart < text_len) { current.element = txt_create(1,0); found = cltext_SearchTag(text, cutstart); if (found == cutstart) { // Tagged Text cutstart = found + 11; cutend = cltext_SearchTag(text, cutstart); if (cutend < cutstart) cutend = text_len; str_cut(worktext, text, found+4, found+5); cltext_hextovar(worktext, current.element.red); str_cut(worktext, text, found+6, found+7); cltext_hextovar(worktext, current.element.green); str_cut(worktext, text, found+8, found+9); cltext_hextovar(worktext, current.element.blue); } else if (found > cutstart) { // Untagged text cutend = found; vec_set(current.element.blue, defaultcolor); } else { // No Tags found cutend = text_len; } str_cut((current.element.pstring)[0], text, cutstart, cutend); // If the text in the line is too long, it has to be wrapped while ( (current.linex + str_width((current.element.pstring)[0], font)) > sizey ) { var current_len = str_len((current.element.pstring)[0]); var validspace = 0, possiblespace = 0; var lastspace = current_len; while ((lastspace > 0) && (validspace == 0)) { lastspace = cltext_SearchSpaceBackw((current.element.pstring)[0], lastspace); if (lastspace > 0) { possiblespace = lastspace; str_cut(worktext, (current.element.pstring)[0], 0, possiblespace); if (str_width(worktext,font)+current.linex <= sizey) { validspace = lastspace; } } } // we now know if there is a valid space and where it is if (validspace > 0) { // great, we have a space to lf str_cut(curline, (current.element.pstring)[0], 0, validspace); str_cut(newline, (current.element.pstring)[0], validspace+1, current_len); } else { // too bad, there is no valid space if ( ((current.linex == indent) && (current.liney > 0)) || ((current.linex == 0) && (current.liney == 0)) ) { // text at the beginning of the line if (possiblespace > 0) { // just use the next space possible str_cut(curline, (current.element.pstring)[0], 0, possiblespace); str_cut(newline, (current.element.pstring)[0], possiblespace+1, current_len); } else { // no chance for a lf str_cpy(curline, (current.element.pstring)[0]); str_cpy(newline, ""); } } else { // text at the end of the line, so just lf it. str_cpy(curline, ""); str_cpy(newline, (current.element.pstring)[0]); } } // so time for a new line current.next = sys_malloc(sizeof(CLLIST)); current.next.next = NULL; current.next.element = txt_create(1,0); current.next.linex = indent; current.next.liney = current.liney + font.dy; vec_set(current.next.element.blue, current.element.blue); str_cpy((current.element.pstring)[0], curline); str_cpy((current.next.element.pstring)[0], newline); current = current.next; } // Create new entry for next cycle current.next = sys_malloc(sizeof(CLLIST)); current.next.linex = current.linex + str_width_hack((current.element.pstring)[0], font); current.next.liney = current.liney; current.next.element = NULL; current.next.next = NULL; current = current.next; cutstart = cutend; } ptr_remove(worktext); ptr_remove(curline); ptr_remove(newline); return cltext; } var cltext_getheight(CLTEXT* cltext) // returns the height in pixels of a CLTEXT. A font has to be set. { CLLIST* current = cltext.texts; var maxliney = 0; if (cltext) { while (current != NULL) { maxliney = maxv(maxliney, current.liney); current = current.next; } if (cltext.font) maxliney += cltext.font.dy; else maxliney = 0; } return maxliney; } void* cltext_remove(CLTEXT* cltext) // removes a CLTEXT // return: NULL { CLLIST* temp; CLLIST* current = cltext.texts; while (current != NULL) { if (current.element != NULL) { ptr_remove((current.element.pstring)[0]); ptr_remove(current.element); } temp = current; current = current.next; sys_free(temp); } ptr_remove(cltext.label); sys_free(cltext); return NULL; } void cltext_show(CLTEXT* cltext, var posx, var posy, var alpha, var layer) // shows and moves a CLTEXT object { CLLIST* current = cltext.texts; cltext.posx = posx; cltext.posy = posy; do { current.element.pos_x = posx + current.linex; current.element.pos_y = posy + current.liney; current.element.font = cltext.font; layer_sort(current.element,layer); current.element.alpha = clamp(alpha,0,100); if (alpha == 0) { reset(current.element, SHOW | LIGHT | TRANSLUCENT); } else if (alpha == 100) { reset(current.element, TRANSLUCENT); set(current.element, LIGHT | SHOW); } else { set(current.element, LIGHT | SHOW | TRANSLUCENT); } if (current.next != NULL) current = current.next; } while (current.next != NULL); } #endif /////////////////////////////////////////////////////////////////////// E O F //
The code for creating the scene in the picture is the following:
Code:
void main(void) { level_load(NULL); wait(3); ENTITY* guard_mdl = ent_create("guard.mdl", _vec(30,0,-32),NULL); guard_mdl.pan = 150; PANEL* backpan = pan_create(NULL,0); backpan.size_x = 600; backpan.size_y = 100; backpan.pos_x = 60; backpan.pos_y = 340; backpan.alpha = 50; vec_set(backpan.blue, nullvector); set(backpan, LIGHT | SHOW | TRANSLUCENT); STRING** chatstr; CLTEXT** chattxt; chatstr = sys_malloc(sizeof(STRING*)*6); chattxt = sys_malloc(sizeof(CLTEXT*)*6); STRING* guardstr = str_create("[clff0000]Guard: [clffffff]I used to be an [cl2233ff]adventurer [clffffff]like you. Until I was offered this job as [clf2f222]city guard[clffffff]. It's a much more stable wage and less dangerous plus I get to spend more time with my [clff00ff]family[clffffff]."); chatstr[0] = str_create("[clff0000][CyberGirl] [clffdd11][pwns] [cl0000ff][Witch]"); chatstr[1] = str_create("[cl0000ff][Witch] [clffffff] [all]: lol, hax0r"); chatstr[2] = str_create("[clff0000][CyberGirl] [clffffff][all]: wtf? u sux, stfu n00b"); chatstr[3] = str_create("[clff0000][Warlock] [clffffff] [all]: My dear fellows, I barely understand a word of what you say. Why won't you speak in a clear language?"); chatstr[4] = str_create("[clff0000][CyberGirl] [clffffff][all]: stfu"); chatstr[5] = str_create("[cl0000ff][Witch] [clffffff] [all]: stfu"); FONT* myfont = font_create("Arial#18b"); FONT* chatfont = font_create("Courier New#14b"); CLTEXT* clguard = cltext_create(guardstr, myfont, 0, 540, 0, nullvector); cltext_show(clguard, 80, 360, 100, 1); var i; for (i = 0; i < 6; i++) { chattxt[i] = cltext_create(chatstr[i], chatfont, str_width_hack(_str(" "), chatfont), 400, 0, nullvector); if (i > 0) cltext_show(chattxt[i], 40, cltext_getheight(chattxt[i-1]) + chattxt[i-1].posy, 100, 1); else cltext_show(chattxt[i], 40, 40, 100, 1); } }