|
[A5] New Line |
tarlkea
Member #13,014
July 2011
|
When I used al_draw_text, the text gives me squares or nothing when i use the \n character in the char * argument. Is there a way to get it to do line breaks without modifying the text string? |
Thomas Fjellstrom
Member #476
June 2000
|
Sadly no. It does not do wrapping or support newlines. You will have to parse out each line and display it, potentially doing additional word wrapping if needed. -- |
beoran
Member #12,636
March 2011
|
Since everyone and their dog is forced to implement this themselves with the current API, which is a waste of effort, I think an al_draw_multiline_text function would be a "nice to have" feature for 5.2 or later. Ideally this would also inclue a way to limit the width of the text and allow for text blocks. So I added this functionality as "nice to have" to the roadmap on the wiki http://wiki.allegro.cc/index.php?title=Allegro_roadmap. |
pkrcel
Member #14,001
February 2012
|
beoran said: Since everyone and their dog is forced to implement this themselves I do not have a dog and I am not programming a GUI It is unlikely that Google shares your distaste for capitalism. - Derezo |
Thomas Fjellstrom
Member #476
June 2000
|
pkrcel said: I do not have a dog and I am not programming a GUI You should think about getting a dog. -- |
pkrcel
Member #14,001
February 2012
|
AH! I know you guys were over your heads but really hoped we could save you. It is unlikely that Google shares your distaste for capitalism. - Derezo |
Arthur Kalliokoski
Second in Command
February 2005
|
pkrcel said: I know you guys were over your heads 1//note: if you reuse a particular wordwrap struct, you have to free frame->wrapbox.bufferarray to avoid a memory leak,
2//and strictly speaking to free it before program exit.
3static int _zz_wordwrap(char *src, ALLEGRO_FONT *font, int pixelwidth, ZZ_WRAPBOX *wrapbox)
4{
5 char *current_line = src; //allow zero based indices within current line
6 char *current_dst;
7 char *dst_resize;
8 char *wrapbuff; //temp store for current line
9 int dst_current_char = 0; //index of current line in dest
10 int src_prev_space = 0; //marks last space in source (if we have to ignore part of a word at end of line)
11 int dst_prev_space = 0; //marks last space in dest
12 int src_current_char = 0; //how far within the current string we are
13 int dst_lines_used = 0; //check that we don't overrun the destination buffer
14 int num_dst_lines; //how many lines in dst available (chars_width * num_dest_lines = number of bytes in dst buffer)
15 int num_src_bytes;
16 char c;
17
18 memset(wrapbox, 0, sizeof(ZZ_WRAPBOX));
19 wrapbox->textfont = font;
20 wrapbox->fontheight = al_get_font_line_height(font);
21 wrapbox->pixelwidth = pixelwidth;
22
23 wrapbox->chars_width = al_get_text_width(font, ten_narrowchars);
24 wrapbox->chars_width = (float)(10.0 / ((float)wrapbox->chars_width / (float)pixelwidth));
25
26 wrapbuff = (char *)malloc(wrapbox->chars_width + 1);
27 if(!wrapbuff)
28 {
29 return ZZ_WRAP_NOMEM;
30 }
31
32 //valgrind complains about conditional jump on uninitialized values if this isn't here, remove if you like
33 memset(wrapbuff,0,wrapbox->chars_width + 1);
34
35 //allocating src_bytes worth of dst plus a WAG of 20% for slop
36 num_src_bytes = strlen(src);
37 num_dst_lines = num_src_bytes / wrapbox->chars_width; //first approximation to how many lines
38 num_dst_lines = (float)num_dst_lines * 1.2; //WAG of 20% guessing most lines waste that much on the end
39 num_dst_lines += 10; //for those one-liners ;)
40 current_dst = (char *)malloc(num_dst_lines * wrapbox->chars_width);
41 if(!current_dst)
42 {
43 return ZZ_WRAP_NOMEM;
44 }
45
46 memset(current_dst, 0, num_dst_lines * wrapbox->chars_width); //zero them all out for asciiz terminators
47
48 while(1)
49 {
50 c = current_line[src_current_char]; //grab next char from source
51
52 if(c == 0) //null terminator? done
53 {
54 strcpy(¤t_dst[dst_lines_used * wrapbox->chars_width], wrapbuff); //copy temp to dest buffer
55 free(wrapbuff);
56 wrapbox->numdestlines = dst_lines_used+1; //tell caller how many lines of wrapped text there are
57
58 wrapbox->bufferarray = current_dst;
59 return 0;
60 }
61
62 if(c == '\n') //start a new line in dest buffer regardless of room remaining in this line
63 {
64 wrapbox->linetoolong++;
65 strcpy(¤t_dst[dst_lines_used * wrapbox->chars_width], wrapbuff); //copy temp to dest buffer
66 memset(wrapbuff, 0, wrapbox->chars_width + 1); //zero out temp for asciiz terminators
67 dst_lines_used++;
68 if(dst_lines_used == num_dst_lines) //need destination buffer enlarged?
69 {
70 /* trying out Peter Wang suggestion of doubling size every realloc()
71 static int lcount = 1;
72 printf("doubling line count %d times\n",lcount);
73 lcount++;
74 */
75 num_dst_lines *= 2; //+= 50; //add 50 more lines available
76 dst_resize = (char *)realloc(current_dst, num_dst_lines * wrapbox->chars_width);
77 if(!dst_resize)
78 {
79 free(wrapbuff);
80 free(current_dst);
81 return ZZ_WRAP_NOMEM;
82 }
83 current_dst = dst_resize;
84 }
85 current_line = ¤t_line[src_current_char + 1]; //+1 to get past the '\n'
86 src_current_char = -1;
87 src_prev_space = 0;
88 dst_prev_space = 0;
89 dst_current_char = 0;
90 }
91
92 if(c < ' ') //ignore backspaces, tabs, '\r's, control chars
93 {
94 src_current_char++; //get past the '\r' or whatever
95 continue; //since c is signed, all the "high" non-ASCII chars will be less than ' ' too
96 }
97
98 if(c == ' ') //keep track of possible line end (wrapbuff full part way through a word)
99 {
100 src_prev_space = src_current_char + 1; //+1 to ignore it next line
101 dst_prev_space = dst_current_char;
102 }
103
104 //fall through to storing in wrapbuff
105 if( (wrapbox->chars_width == (dst_current_char + 1)) || (wrapbox->pixelwidth < al_get_text_width(font, wrapbuff))) //run out of room in this line?
106 {
107 if(dst_prev_space == 0) //we found one single word that's too long for a line
108 {
109 free(wrapbuff);
110 free(current_dst);
111 return ZZ_WRAP_TOOLONG;
112 }
113
114 if(c != ' ') //part way through a word?
115 {
116 dst_current_char = dst_prev_space;
117 src_current_char = src_prev_space;
118 while(wrapbuff[dst_current_char] == ' ')
119 {
120 wrapbuff[dst_current_char] = 0;
121 dst_current_char--;
122 if(dst_current_char < 0)
123 {
124 break;
125 }
126 }
127 }
128
129 strcpy(¤t_dst[dst_lines_used * wrapbox->chars_width], wrapbuff); //copy temp to dest buffer
130 memset(wrapbuff, 0, wrapbox->chars_width + 1); //zero out temp for asciiz terminators
131 dst_lines_used++;
132 if(dst_lines_used == num_dst_lines) //need destination buffer enlarged?
133 {
134 num_dst_lines += 50; //add 50 more lines available
135 dst_resize = (char *)realloc(current_dst, num_dst_lines * wrapbox->chars_width);
136 if(!dst_resize)
137 {
138 free(wrapbuff);
139 free(current_dst);
140 return ZZ_WRAP_NOMEM;
141 }
142 current_dst = dst_resize;
143 }
144 current_line = ¤t_line[src_prev_space];
145 src_current_char = 0;
146 dst_prev_space = 0;
147 dst_current_char = 0;
148 continue;
149 }
150 wrapbuff[dst_current_char] = c;
151 src_current_char++;
152 dst_current_char++;
153 }
154 return 0;
155}
156
157static void _zz_init_frame(int left, int top, int right, int bottom,
158 char *titletext, ALLEGRO_FONT *titlefont,
159 ZZ_TEXTBOX *frame, ZZ_WIN *window)
160{
161 int height, width;
162 frame->framerect.left = left;
163 frame->framerect.top = top;
164 frame->framerect.right = right;
165
166 frame->textbox.left = frame->framerect.left + TEXTBOXMARGIN;
167 frame->textbox.top = frame->framerect.top + TEXTBOXMARGIN;
168 frame->textbox.right = frame->framerect.right - TEXTBOXMARGIN;
169
170 frame->titletext = titletext;
171 frame->titlefont = titlefont;
172 frame->window = window;
173
174 height = al_get_font_line_height(titlefont);
175 width = al_get_text_width(titlefont, titletext);
176
177 frame->titlebox.top = frame->framerect.top - (height >> 1);
178 frame->titlebox.bottom = frame->titlebox.top + height;
179 frame->titlebox.left = frame->framerect.left + 10;
180 frame->titlebox.right = frame->titlebox.left + width + 10;
181
182 //leave zero in the "bottom" parameter to have bottom automagically
183 //align with bottom of text
184 if(bottom != 0)
185 {
186 frame->framerect.bottom = bottom;
187 frame->textbox.bottom = frame->framerect.bottom - TEXTBOXMARGIN;
188 }
189 else
190 {
191 frame->textbox.bottom = frame->titlebox.bottom + (frame->wrapbox.numdestlines * frame->wrapbox.fontheight);
192 frame->framerect.bottom = frame->textbox.bottom + TEXTBOXMARGIN;
193 }
194}
195
196int zz_init_textbox(ZZ_TEXTBOX *textbox, //textbox struct location
197 ZZ_WIN *parentwin, //parent window struct location
198 ALLEGRO_FONT *titlefont, //font for title (may be 0 if no title desired)
199 ALLEGRO_FONT *bodyfont, //font for body of text (may be 0 if no body desired)
200 char *titletext, //pointer to text for title (may be 0 if no title desired)
201 char *body_text_src, //pointer to original cstring for body of text (may be 0 if no body desired)
202 int left, //left edge on parent window
203 int top, //top edge on parent window
204 int right, //right edge on parent window
205 int bottom, //bottom edge on parent window (may be 0 if you want bottom set to end of body of text)
206 int gray)
207{
208 int err;
209
210 err = _zz_wordwrap(body_text_src, bodyfont, right-left-TEXTBOXMARGIN*2, &textbox->wrapbox);
211 if(err)
212 {
213 return err;
214 }
215
216 _zz_init_frame(left, top, right, bottom, //was _zz_init_textbox(?)
217 titletext, titlefont,
218 textbox, parentwin);
219 textbox->gray = gray;
220 return 0;
221}
222
223void zz_draw_textbox(ZZ_TEXTBOX *frame)
224{
225 int x;
226 int y;
227 int width;
228 int height;
229
230 int row;
231 int limit;
232 int indx;
233 int i;
234 int gray;
235
236 if(frame->gray == 0) //force default
237 gray = UNSELECTED; //force washed out appearance
238 else
239 gray = frame->window->grayout;
240
241 x = frame->framerect.left + frame->window->rect.left;
242 y = frame->framerect.top + frame->window->rect.top;
243 width = frame->framerect.right - frame->framerect.left;
244 height = frame->framerect.bottom - frame->framerect.top;
245
246 //draw outline
247
248 //top line (tapered at the ends)
249 al_draw_line(x - 0.5,
250 y - 0.5,
251 x + width + 0.5,
252 y - 0.5,
253 zz_gui_colors[gray][DIMGRAY], 1.0);
254
255 al_draw_line(x + 0.5,
256 y + 0.5,
257 x + width - 0.5,
258 y + 0.5,
259 zz_gui_colors[gray][BRIGHTGRAY], 1.0);
260
261 //bottom line
262 al_draw_line(x + 0.5,
263 height + y - 0.5,
264 x + width - 0.5,
265 height + y - 0.5,
266 zz_gui_colors[gray][DIMGRAY], 1.0);
267
268 al_draw_line(x - 0.5,
269 height + y + 0.5,
270 x + width + 0.5,
271 height + y + 0.5,
272 zz_gui_colors[gray][BRIGHTGRAY], 1.0);
273
274 //left line
275 al_draw_line(x - 0.5,
276 y - 0.5,
277 x - 0.5,
278 y + height + 0.5,
279 zz_gui_colors[gray][DARKGRAY], 1.0);
280
281 al_draw_line(x + 0.5,
282 y - 0.5,
283 x + 0.5,
284 y + height + 0.5,
285 zz_gui_colors[gray][LIGHTGRAY], 1.0);
286
287 //right line
288 al_draw_line(x + width - 0.5,
289 y + 0.5,
290 x + width - 0.5,
291 height + y - 0.5,
292 zz_gui_colors[gray][DARKGRAY], 1.0);
293
294 al_draw_line(x + width + 0.5,
295 y - 0.5,
296 x + width + 0.5,
297 height + y + 0.5,
298 zz_gui_colors[gray][LIGHTGRAY], 1.0);
299
300 if(frame->titletext)
301 {
302 al_draw_filled_rectangle(frame->window->rect.left + frame->titlebox.left,
303 frame->window->rect.top + frame->titlebox.top,
304 frame->window->rect.left + frame->titlebox.right,
305 frame->window->rect.top + frame->titlebox.bottom,
306 zz_gui_colors[gray][GRAY]);
307
308 al_draw_text(frame->titlefont, zz_gui_colors[gray][BLACK], (float) frame->titlebox.left + frame->window->rect.left + 5,
309 (float) frame->titlebox.top + frame->window->rect.top, 0, frame->titletext);
310 }
311
312 if(frame->wrapbox.bufferarray)
313 {
314 row = frame->window->rect.top + frame->textbox.top;
315 width = frame->wrapbox.chars_width;
316 limit = frame->wrapbox.numdestlines;
317
318 for(indx = 0, i = 0; i < limit;indx+=width,i++)
319 {
320 al_draw_text(frame->wrapbox.textfont,
321 zz_gui_colors[gray][BLACK],
322 frame->window->rect.left + frame->textbox.left,
323 row,
324 0,
325 &frame->wrapbox.bufferarray[indx]);
326 row+=frame->wrapbox.fontheight;
327 }
328 }
329}
330
331int zz_init_scrolltext( ZZ_SCROLLTEXT *textbox, //textbox struct location
332 ZZ_WIN *parentwin, //parent window struct location
333 ALLEGRO_FONT *scrollfont, //font for body of text (may be 0 if no body desired)
334 char *body_text_src, //pointer to original cstring for body of text (may be 0 if no body desired)
335 int left, //left edge on parent window
336 int top, //top edge on parent window
337 int right, //right edge on parent window
338 int bottom, //bottom edge on parent window (will be rounded from 0.5 lines of text to provide vertical alignment)
339 int scrollwidth, //how wide the strings can be in pixels (horizontal scroll necessary if wider than right-left)
340 int gray)
341{
342 int err;
343 float rnder;
344 err = _zz_wordwrap(body_text_src, scrollfont, scrollwidth, &textbox->wrapbox);
345 if(err)
346 {
347 return err;
348 }
349 textbox->parent = parentwin;
350 textbox->boxwidth = right-left;
351 textbox->scrollrect.parent = parentwin;
352 textbox->scrollrect.rect.left = left;
353 textbox->scrollrect.rect.top = top;
354 textbox->scrollrect.rect.right = right;
355 rnder = bottom - top;
356 rnder /= textbox->wrapbox.fontheight;
357 textbox->scrollrect.rect.bottom = rnder;
358 textbox->scrollrect.rect.bottom *= textbox->wrapbox.fontheight;
359 textbox->gray = gray;
360 err = (textbox->scrollrect.rect.bottom - top)/textbox->wrapbox.fontheight;
361 zz_init_scrollbar((textbox->scrollrect.rect.bottom - top)/textbox->wrapbox.fontheight,textbox->wrapbox.numdestlines,ZZ_VERTICAL,&textbox->vscroll,&textbox->scrollrect);
362 return 0;
363}
364
365void zz_do_scrolltext(ZZ_SCROLLTEXT *textbox)
366{
367 int top;
368 int len;
369 int i;
370 int line;
371 int hgt;
372 zz_do_scrollbar(&textbox->vscroll);
373 top = textbox->vscroll.topline;
374 len = textbox->vscroll.items_in_window + top;
375 hgt = textbox->wrapbox.fontheight;
376 line = textbox->scrollrect.rect.top;
377 al_draw_filled_rectangle(textbox->scrollrect.rect.left,textbox->scrollrect.rect.top,textbox->scrollrect.rect.right,textbox->scrollrect.rect.bottom,zz_gui_colors[SELECTED][WHITE]);
378 zz_bevel(textbox->scrollrect.rect.left,
379 textbox->scrollrect.rect.top,
380 textbox->scrollrect.rect.right,
381 textbox->scrollrect.rect.bottom,
382 1,
383 textbox->parent,
384 1);
385 for(i=top;i<len;i++)
386 {
387 al_draw_text(textbox->wrapbox.textfont,zz_gui_colors[SELECTED][BLACK], textbox->scrollrect.rect.left, line, 0,&textbox->wrapbox.bufferarray[i * textbox->wrapbox.chars_width]);
388 line += hgt;
389 }
390}
They all watch too much MSNBC... they get ideas. |
Felix-The-Ghost
Member #9,729
April 2008
|
jmasterx
Member #11,410
October 2009
|
Here is the Agui class that does resizable text. It includes: I use this for Agui's Label class (and buttons and other widgets too). The Label was intended to be as feature-rich as the Label in .NET. https://github.com/jmasterx/Agui/blob/master/include/Agui/ResizableText.hpp Agui GUI API -> https://github.com/jmasterx/Agui |
SiegeLord
Member #7,827
October 2006
|
My opinion on this (and the HTML parser in the other thread) is that it's hard to get this routine to have just enough features for people to actually be able to use it while at the same time not being out of place for a mid/low-level library that Allegro is trying to be. If this was really desired, I'd rather add a al_draw_tokens(..., void (*cb)(*out_x, *out_y, *out_width, *out_color, ...) which would tokenize the text and call the callback in order to do the line switching etc. Of course it still wouldn't do the multi-line drawing out of the box but it'd be simpler to implement. Either way, this seems like a good topic for a wiki article or something so people can just be pointed to it. "For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18 |
beoran
Member #12,636
March 2011
|
Actually, a low level function that could be useful for composing higher level ones in a layout library would be void al_draw_character(const ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, int character) to draw a single character / unicode code point. This function would arguably be faster when doing word by word or letter by letter text output (as in some RPG's ,etc), than al_draw_ustr, etc. It would also enable, for example, alternative layouts such as lright to left or top to down writing, by allowing us to implement our own output functions for those. |
Edgar Reynaldo
Major Reynaldo
May 2007
|
The problem with a function like that is that it doesn't respect kerning of the font. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
beoran
Member #12,636
March 2011
|
true, though arguably one should be able to query that as well somehow... |
Edgar Reynaldo
Major Reynaldo
May 2007
|
al_get_kerning(const ALLEGRO_FONT* font , int char1 , int char2);? My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
beoran
Member #12,636
March 2011
|
Yeah. It could return 0 in case of no kerning and negative in case of kerning. Also a al_get_character_width(const ALLEGRO_FONT* font , int codepoint); should be added then to complete the API (we can already get height, etc ccrom font-specific functions). |
Cassio Renan
Member #14,189
April 2012
|
What I did was create a "text" structure witch will have word, font and spacing info for regular and justified drawing. That way it can be built and stored instead of a string, for (presumably)faster drawing. It doesn't support \n's yet, but I'll work on it. Full code, for anyone interested. 1struct word{
2 std::string value;
3 int width;
4};
5
6struct line{
7 std::vector<word> words;
8 int size;
9 float jSpaceSize;
10};
11
12struct text{
13 std::vector<line> lines;
14 float spaceSize;
15 int maxSize;
16 int height;
17 ALLEGRO_FONT *font;
18};
19
20void drawtext(text t, float x, float y, ALLEGRO_COLOR color, bool justified)
21{
22 std::vector<word>::iterator it;
23 for(int i = 0;i < t.lines.size();i++){
24 float curx = x;
25 for(it = t.lines[i].words.begin();it!=t.lines[i].words.end();it++){
26 al_draw_text(t.font, color, curx, y + t.height*i,0,it->value.c_str());
27 curx += it->width;
28 curx += justified?t.lines[i].jSpaceSize:t.spaceSize;
29 }
30 }
31}
32
33
34text buildText(const string &s, ALLEGRO_FONT* font, int maxSize, int maxLines)
35{
36 std::stringstream ss(s);
37 text result;
38 result.lines.clear();
39
40 line curline;
41 curline.words.clear();
42 curline.size = 0;
43
44 word curword;
45 bool firstword = true;
46
47 result.spaceSize = al_get_text_width(font, " ");
48 result.maxSize = maxSize;
49 result.font = font;
50 result.height = al_get_font_line_height(font);
51
52 while(!ss.eof()){
53 ss >> curword.value;
54 curword.width = al_get_text_width(font,curword.value.c_str());
55 if(firstword){
56 curline.words.push_back(curword);
57 curline.size += curword.width;
58 firstword = false;
59 }
60 else if(curline.size + curword.width + result.spaceSize < maxSize){
61 curline.words.push_back(curword);
62 curline.size += curword.width + result.spaceSize;
63 }
64 else{
65 curline.jSpaceSize = 0;
66 for(std::vector<word>::iterator it = curline.words.begin();it!= curline.words.end();it++){
67 curline.jSpaceSize += it->width;
68 }
69 curline.jSpaceSize = maxSize - curline.jSpaceSize;
70 curline.jSpaceSize /= curline.words.size() - 1;
71
72 result.lines.push_back(curline);
73 curline.size = 0;
74 curline.words.clear();
75 curline.words.push_back(curword);
76 curline.size += curword.width;
77 }
78 }
79 if(curline.size){
80 result.lines.push_back(curline);
81 }
82 while(result.lines.size() > maxLines){
83 result.lines.pop_back();
84 }
85 return result;
86}
|
|