0 registered members (),
1,209
guests, and 0
spiders. |
Key:
Admin,
Global Mod,
Mod
|
|
|
Colourful TEXT
#418880
03/02/13 18:18
03/02/13 18:18
|
Joined: Nov 2011
Posts: 274 de
lemming
OP
Member
|
OP
Member
Joined: Nov 2011
Posts: 274
de
|
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 TEXTaka CLTEXTThis 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:
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. CLTEXT.H
////////////////////////////////////////////////////////////////////////////////
// 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:
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);
}
}
[/b][b]
Last edited by lemming; 04/16/13 21:32.
|
|
|
Re: Colourful TEXT
[Re: lemming]
#418882
03/02/13 18:38
03/02/13 18:38
|
Joined: Jun 2009
Posts: 2,210 Bavaria, Germany
Kartoffel
Expert
|
Expert
Joined: Jun 2009
Posts: 2,210
Bavaria, Germany
|
Wow very cool! I'm sure this will be helpful for a lot of people, including me (I had to laugh about the chat dialogue... sounds familiar, lol)
Last edited by Kartoffel; 03/02/13 20:13.
POTATO-MAN saves the day! - Random
|
|
|
Re: Colourful TEXT
[Re: lemming]
#423258
05/26/13 21:56
05/26/13 21:56
|
Joined: Jun 2009
Posts: 2,210 Bavaria, Germany
Kartoffel
Expert
|
Expert
Joined: Jun 2009
Posts: 2,210
Bavaria, Germany
|
hey there, I was wondering why the colors looked kina wired and had a closer look at the source code. You made a little mistake in the function which reads the color from a string. It should be like this: (actually, it should be right without the / 2 in line 179 but somehow it is exactly the half...) ...I also created a couple of additional functions (f.e. changing the text of an CLTEXT) I can share them if anybody is interested.
Last edited by Kartoffel; 05/26/13 22:04.
POTATO-MAN saves the day! - Random
|
|
|
Re: Colourful TEXT
[Re: lemming]
#423300
05/27/13 18:18
05/27/13 18:18
|
Joined: May 2013
Posts: 23 Somewhere
Wiseguy
Newbie
|
Newbie
Joined: May 2013
Posts: 23
Somewhere
|
I haven't tested it, but this method here should do the trick and is much saner in the handling of the string:
var cltext_hextovar(STRING *hexs, var *output)
{
if(!hexs || !output)
return -1;
if(str_len(hexs) > 2)
return -2;
const char *hex = hexs->chars;
int acc = 0;
while(*hex)
{
char c = *(hex ++);
if(c >= '0' && c <= '9)
c -= '0';
else if(c >= 'A' && <= 'Z')
c -= 'A' - 10;
else if(c >= 'a' && <= 'z')
c -= 'a' - 10;
else
return -3;
acc = (acc * 16) + c;
}
*output = acc;
return 0;
}
Further more, it can be expanded to convert strings with any base, by simply replacing the 16 in line 27 with the required base (and by adding a sanity check wether c is inside the base). Also, Kartoffel, could you please not post screenshots of code? I would have loved to copy and paste it instead of having to write the whole body myself. (PS: Lemming: It's your code, you should be the one who maintains it in the git repo ;))
Last edited by Wiseguy; 05/27/13 23:32. Reason: Fixed two things
His words are wise, his face is beard.
|
|
|
|