1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2009 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edinterface
22 */
23
24 #include <limits.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_userdef_types.h"
32
33 #include "BLI_listbase.h"
34 #include "BLI_rect.h"
35 #include "BLI_string.h"
36 #include "BLI_utildefines.h"
37
38 #include "BKE_global.h"
39
40 #include "BLF_api.h"
41 #ifdef WITH_INTERNATIONAL
42 # include "BLT_translation.h"
43 #endif
44
45 #include "UI_interface.h"
46
47 #include "ED_datafiles.h"
48
49 #include "interface_intern.h"
50
51 #ifdef WIN32
52 # include "BLI_math_base.h" /* M_PI */
53 #endif
54
55 /* style + theme + layout-engine = UI */
56
57 /**
58 * This is a complete set of layout rules, the 'state' of the Layout
59 * Engine. Multiple styles are possible, defined via C or Python. Styles
60 * get a name, and will typically get activated per region type, like
61 * "Header", or "Listview" or "Toolbar". Properties of Style definitions
62 * are:
63 *
64 * - default column properties, internal spacing, aligning, min/max width
65 * - button alignment rules (for groups)
66 * - label placement rules
67 * - internal labeling or external labeling default
68 * - default minimum widths for buttons/labels (in amount of characters)
69 * - font types, styles and relative sizes for Panel titles, labels, etc.
70 */
71
72 /* ********************************************** */
73
ui_style_new(ListBase * styles,const char * name,short uifont_id)74 static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id)
75 {
76 uiStyle *style = MEM_callocN(sizeof(uiStyle), "new style");
77
78 BLI_addtail(styles, style);
79 BLI_strncpy(style->name, name, MAX_STYLE_NAME);
80
81 style->panelzoom = 1.0; /* unused */
82
83 style->paneltitle.uifont_id = uifont_id;
84 style->paneltitle.points = UI_DEFAULT_TITLE_POINTS;
85 style->paneltitle.kerning = 1;
86 style->paneltitle.shadow = 3;
87 style->paneltitle.shadx = 0;
88 style->paneltitle.shady = -1;
89 style->paneltitle.shadowalpha = 0.5f;
90 style->paneltitle.shadowcolor = 0.0f;
91
92 style->grouplabel.uifont_id = uifont_id;
93 style->grouplabel.points = UI_DEFAULT_TITLE_POINTS;
94 style->grouplabel.kerning = 1;
95 style->grouplabel.shadow = 3;
96 style->grouplabel.shadx = 0;
97 style->grouplabel.shady = -1;
98 style->grouplabel.shadowalpha = 0.5f;
99 style->grouplabel.shadowcolor = 0.0f;
100
101 style->widgetlabel.uifont_id = uifont_id;
102 style->widgetlabel.points = UI_DEFAULT_TEXT_POINTS;
103 style->widgetlabel.kerning = 1;
104 style->widgetlabel.shadow = 3;
105 style->widgetlabel.shadx = 0;
106 style->widgetlabel.shady = -1;
107 style->widgetlabel.shadowalpha = 0.5f;
108 style->widgetlabel.shadowcolor = 0.0f;
109
110 style->widget.uifont_id = uifont_id;
111 style->widget.points = UI_DEFAULT_TEXT_POINTS;
112 style->widget.kerning = 1;
113 style->widget.shadow = 1;
114 style->widget.shady = -1;
115 style->widget.shadowalpha = 0.5f;
116 style->widget.shadowcolor = 0.0f;
117
118 style->columnspace = 8;
119 style->templatespace = 5;
120 style->boxspace = 5;
121 style->buttonspacex = 8;
122 style->buttonspacey = 2;
123 style->panelspace = 8;
124 style->panelouter = 4;
125
126 return style;
127 }
128
uifont_to_blfont(int id)129 static uiFont *uifont_to_blfont(int id)
130 {
131 uiFont *font = U.uifonts.first;
132
133 for (; font; font = font->next) {
134 if (font->uifont_id == id) {
135 return font;
136 }
137 }
138 return U.uifonts.first;
139 }
140
141 /* *************** draw ************************ */
142
UI_fontstyle_draw_ex(const uiFontStyle * fs,const rcti * rect,const char * str,const uchar col[4],const struct uiFontStyleDraw_Params * fs_params,size_t len,int * r_xofs,int * r_yofs,struct ResultBLF * r_info)143 void UI_fontstyle_draw_ex(const uiFontStyle *fs,
144 const rcti *rect,
145 const char *str,
146 const uchar col[4],
147 const struct uiFontStyleDraw_Params *fs_params,
148 size_t len,
149 int *r_xofs,
150 int *r_yofs,
151 struct ResultBLF *r_info)
152 {
153 int xofs = 0, yofs;
154 int font_flag = BLF_CLIPPING;
155
156 UI_fontstyle_set(fs);
157
158 /* set the flag */
159 if (fs->shadow) {
160 font_flag |= BLF_SHADOW;
161 const float shadow_color[4] = {
162 fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha};
163 BLF_shadow(fs->uifont_id, fs->shadow, shadow_color);
164 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
165 }
166 if (fs->kerning == 1) {
167 font_flag |= BLF_KERNING_DEFAULT;
168 }
169 if (fs_params->word_wrap == 1) {
170 font_flag |= BLF_WORD_WRAP;
171 }
172 if (fs->bold) {
173 font_flag |= BLF_BOLD;
174 }
175 if (fs->italic) {
176 font_flag |= BLF_ITALIC;
177 }
178
179 BLF_enable(fs->uifont_id, font_flag);
180
181 if (fs_params->word_wrap == 1) {
182 /* draw from boundbox top */
183 yofs = BLI_rcti_size_y(rect) - BLF_height_max(fs->uifont_id);
184 }
185 else {
186 /* draw from boundbox center */
187 const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
188 yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
189 }
190
191 if (fs_params->align == UI_STYLE_TEXT_CENTER) {
192 xofs = floor(0.5f * (BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len)));
193 }
194 else if (fs_params->align == UI_STYLE_TEXT_RIGHT) {
195 xofs = BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len);
196 }
197
198 yofs = MAX2(0, yofs);
199 xofs = MAX2(0, xofs);
200
201 BLF_clipping(fs->uifont_id, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
202 BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f);
203 BLF_color4ubv(fs->uifont_id, col);
204
205 BLF_draw_ex(fs->uifont_id, str, len, r_info);
206
207 BLF_disable(fs->uifont_id, font_flag);
208
209 *r_xofs = xofs;
210 *r_yofs = yofs;
211 }
212
UI_fontstyle_draw(const uiFontStyle * fs,const rcti * rect,const char * str,const uchar col[4],const struct uiFontStyleDraw_Params * fs_params)213 void UI_fontstyle_draw(const uiFontStyle *fs,
214 const rcti *rect,
215 const char *str,
216 const uchar col[4],
217 const struct uiFontStyleDraw_Params *fs_params)
218 {
219 int xofs, yofs;
220
221 UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs, NULL);
222 }
223
224 /* drawn same as above, but at 90 degree angle */
UI_fontstyle_draw_rotated(const uiFontStyle * fs,const rcti * rect,const char * str,const uchar col[4])225 void UI_fontstyle_draw_rotated(const uiFontStyle *fs,
226 const rcti *rect,
227 const char *str,
228 const uchar col[4])
229 {
230 float height;
231 int xofs, yofs;
232 float angle;
233 rcti txtrect;
234
235 UI_fontstyle_set(fs);
236
237 height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
238 /* becomes x-offset when rotated */
239 xofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
240
241 /* ignore UI_STYLE, always aligned to top */
242
243 /* rotate counter-clockwise for now (assumes left-to-right language)*/
244 xofs += height;
245 yofs = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX) + 5;
246 angle = M_PI_2;
247
248 /* translate rect to vertical */
249 txtrect.xmin = rect->xmin - BLI_rcti_size_y(rect);
250 txtrect.ymin = rect->ymin - BLI_rcti_size_x(rect);
251 txtrect.xmax = rect->xmin;
252 txtrect.ymax = rect->ymin;
253
254 /* clip is very strict, so we give it some space */
255 /* clipping is done without rotation, so make rect big enough to contain both positions */
256 BLF_clipping(fs->uifont_id,
257 txtrect.xmin - 1,
258 txtrect.ymin - yofs - xofs - 4,
259 rect->xmax + 1,
260 rect->ymax + 4);
261 BLF_enable(fs->uifont_id, BLF_CLIPPING);
262 BLF_position(fs->uifont_id, txtrect.xmin + xofs, txtrect.ymax - yofs, 0.0f);
263
264 BLF_enable(fs->uifont_id, BLF_ROTATION);
265 BLF_rotation(fs->uifont_id, angle);
266 BLF_color4ubv(fs->uifont_id, col);
267
268 if (fs->shadow) {
269 BLF_enable(fs->uifont_id, BLF_SHADOW);
270 const float shadow_color[4] = {
271 fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha};
272 BLF_shadow(fs->uifont_id, fs->shadow, shadow_color);
273 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
274 }
275
276 if (fs->kerning == 1) {
277 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
278 }
279
280 BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
281 BLF_disable(fs->uifont_id, BLF_ROTATION);
282 BLF_disable(fs->uifont_id, BLF_CLIPPING);
283 if (fs->shadow) {
284 BLF_disable(fs->uifont_id, BLF_SHADOW);
285 }
286 if (fs->kerning == 1) {
287 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
288 }
289 }
290
291 /**
292 * Similar to #UI_fontstyle_draw
293 * but ignore alignment, shadow & no clipping rect.
294 *
295 * For drawing on-screen labels.
296 */
UI_fontstyle_draw_simple(const uiFontStyle * fs,float x,float y,const char * str,const uchar col[4])297 void UI_fontstyle_draw_simple(
298 const uiFontStyle *fs, float x, float y, const char *str, const uchar col[4])
299 {
300 if (fs->kerning == 1) {
301 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
302 }
303
304 UI_fontstyle_set(fs);
305 BLF_position(fs->uifont_id, x, y, 0.0f);
306 BLF_color4ubv(fs->uifont_id, col);
307 BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
308
309 if (fs->kerning == 1) {
310 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
311 }
312 }
313
314 /**
315 * Same as #UI_fontstyle_draw but draw a colored backdrop.
316 */
UI_fontstyle_draw_simple_backdrop(const uiFontStyle * fs,float x,float y,const char * str,const float col_fg[4],const float col_bg[4])317 void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
318 float x,
319 float y,
320 const char *str,
321 const float col_fg[4],
322 const float col_bg[4])
323 {
324 if (fs->kerning == 1) {
325 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
326 }
327
328 UI_fontstyle_set(fs);
329
330 {
331 const float width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
332 const float height = BLF_height_max(fs->uifont_id);
333 const float decent = BLF_descender(fs->uifont_id);
334 const float margin = height / 4.0f;
335
336 /* backdrop */
337 const float color[4] = {col_bg[0], col_bg[1], col_bg[2], 0.5f};
338
339 UI_draw_roundbox_corner_set(UI_CNR_ALL);
340 UI_draw_roundbox_aa(true,
341 x - margin,
342 (y + decent) - margin,
343 x + width + margin,
344 (y + decent) + height + margin,
345 margin,
346 color);
347 }
348
349 BLF_position(fs->uifont_id, x, y, 0.0f);
350 BLF_color4fv(fs->uifont_id, col_fg);
351 BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
352
353 if (fs->kerning == 1) {
354 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
355 }
356 }
357
358 /* ************** helpers ************************ */
359 /* XXX: read a style configure */
UI_style_get(void)360 const uiStyle *UI_style_get(void)
361 {
362 #if 0
363 uiStyle *style = NULL;
364 /* offset is two struct uiStyle pointers */
365 style = BLI_findstring(&U.uistyles, "Unifont Style", sizeof(style) * 2);
366 return (style != NULL) ? style : U.uistyles.first;
367 #else
368 return U.uistyles.first;
369 #endif
370 }
371
372 /* for drawing, scaled with DPI setting */
UI_style_get_dpi(void)373 const uiStyle *UI_style_get_dpi(void)
374 {
375 const uiStyle *style = UI_style_get();
376 static uiStyle _style;
377
378 _style = *style;
379
380 _style.paneltitle.shadx = (short)(UI_DPI_FAC * _style.paneltitle.shadx);
381 _style.paneltitle.shady = (short)(UI_DPI_FAC * _style.paneltitle.shady);
382 _style.grouplabel.shadx = (short)(UI_DPI_FAC * _style.grouplabel.shadx);
383 _style.grouplabel.shady = (short)(UI_DPI_FAC * _style.grouplabel.shady);
384 _style.widgetlabel.shadx = (short)(UI_DPI_FAC * _style.widgetlabel.shadx);
385 _style.widgetlabel.shady = (short)(UI_DPI_FAC * _style.widgetlabel.shady);
386
387 _style.columnspace = (short)(UI_DPI_FAC * _style.columnspace);
388 _style.templatespace = (short)(UI_DPI_FAC * _style.templatespace);
389 _style.boxspace = (short)(UI_DPI_FAC * _style.boxspace);
390 _style.buttonspacex = (short)(UI_DPI_FAC * _style.buttonspacex);
391 _style.buttonspacey = (short)(UI_DPI_FAC * _style.buttonspacey);
392 _style.panelspace = (short)(UI_DPI_FAC * _style.panelspace);
393 _style.panelouter = (short)(UI_DPI_FAC * _style.panelouter);
394
395 return &_style;
396 }
397
UI_fontstyle_string_width(const uiFontStyle * fs,const char * str)398 int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str)
399 {
400 int width;
401
402 if (fs->kerning == 1) {
403 /* for BLF_width */
404 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
405 }
406
407 UI_fontstyle_set(fs);
408 width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
409
410 if (fs->kerning == 1) {
411 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
412 }
413
414 return width;
415 }
416
UI_fontstyle_height_max(const uiFontStyle * fs)417 int UI_fontstyle_height_max(const uiFontStyle *fs)
418 {
419 UI_fontstyle_set(fs);
420 return BLF_height_max(fs->uifont_id);
421 }
422
423 /* ************** init exit ************************ */
424
425 /* called on each startup.blend read */
426 /* reading without uifont will create one */
uiStyleInit(void)427 void uiStyleInit(void)
428 {
429 uiStyle *style = U.uistyles.first;
430
431 /* recover from uninitialized dpi */
432 if (U.dpi == 0) {
433 U.dpi = 72;
434 }
435 CLAMP(U.dpi, 48, 144);
436
437 LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
438 BLF_unload_id(font->blf_id);
439 }
440
441 if (blf_mono_font != -1) {
442 BLF_unload_id(blf_mono_font);
443 blf_mono_font = -1;
444 }
445
446 if (blf_mono_font_render != -1) {
447 BLF_unload_id(blf_mono_font_render);
448 blf_mono_font_render = -1;
449 }
450
451 uiFont *font_first = U.uifonts.first;
452
453 /* default builtin */
454 if (font_first == NULL) {
455 font_first = MEM_callocN(sizeof(uiFont), "ui font");
456 BLI_addtail(&U.uifonts, font_first);
457 }
458
459 if (U.font_path_ui[0]) {
460 BLI_strncpy(font_first->filename, U.font_path_ui, sizeof(font_first->filename));
461 font_first->uifont_id = UIFONT_CUSTOM1;
462 }
463 else {
464 BLI_strncpy(font_first->filename, "default", sizeof(font_first->filename));
465 font_first->uifont_id = UIFONT_DEFAULT;
466 }
467
468 LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
469 const bool unique = false;
470
471 if (font->uifont_id == UIFONT_DEFAULT) {
472 font->blf_id = BLF_load_default(unique);
473 }
474 else {
475 font->blf_id = BLF_load(font->filename);
476 if (font->blf_id == -1) {
477 font->blf_id = BLF_load_default(unique);
478 }
479 }
480
481 BLF_default_set(font->blf_id);
482
483 if (font->blf_id == -1) {
484 if (G.debug & G_DEBUG) {
485 printf("%s: error, no fonts available\n", __func__);
486 }
487 }
488 else {
489 /* ? just for speed to initialize?
490 * Yes, this build the glyph cache and create
491 * the texture.
492 */
493 BLF_size(font->blf_id, 11 * U.pixelsize, U.dpi);
494 BLF_size(font->blf_id, 12 * U.pixelsize, U.dpi);
495 BLF_size(font->blf_id, 14 * U.pixelsize, U.dpi);
496 }
497 }
498
499 if (style == NULL) {
500 ui_style_new(&U.uistyles, "Default Style", UIFONT_DEFAULT);
501 }
502
503 /* XXX, this should be moved into a style,
504 * but for now best only load the monospaced font once. */
505 BLI_assert(blf_mono_font == -1);
506 /* Use unique font loading to avoid thread safety issues with mono font
507 * used for render metadata stamp in threads. */
508 if (U.font_path_ui_mono[0]) {
509 blf_mono_font = BLF_load_unique(U.font_path_ui_mono);
510 }
511 if (blf_mono_font == -1) {
512 const bool unique = true;
513 blf_mono_font = BLF_load_mono_default(unique);
514 }
515
516 BLF_size(blf_mono_font, 12 * U.pixelsize, 72);
517
518 /* Set default flags based on UI preferences (not render fonts) */
519 {
520 const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT |
521 BLF_HINTING_FULL);
522 int flag_enable = 0;
523
524 if (U.text_render & USER_TEXT_HINTING_NONE) {
525 flag_enable |= BLF_HINTING_NONE;
526 }
527 else if (U.text_render & USER_TEXT_HINTING_SLIGHT) {
528 flag_enable |= BLF_HINTING_SLIGHT;
529 }
530 else if (U.text_render & USER_TEXT_HINTING_FULL) {
531 flag_enable |= BLF_HINTING_FULL;
532 }
533
534 if (U.text_render & USER_TEXT_DISABLE_AA) {
535 flag_enable |= BLF_MONOCHROME;
536 }
537
538 LISTBASE_FOREACH (uiFont *, font, &U.uifonts) {
539 if (font->blf_id != -1) {
540 BLF_disable(font->blf_id, flag_disable);
541 BLF_enable(font->blf_id, flag_enable);
542 }
543 }
544 if (blf_mono_font != -1) {
545 BLF_disable(blf_mono_font, flag_disable);
546 BLF_enable(blf_mono_font, flag_enable);
547 }
548 }
549
550 /**
551 * Second for rendering else we get threading problems,
552 *
553 * \note This isn't good that the render font depends on the preferences,
554 * keep for now though, since without this there is no way to display many unicode chars.
555 */
556 if (blf_mono_font_render == -1) {
557 const bool unique = true;
558 blf_mono_font_render = BLF_load_mono_default(unique);
559 }
560
561 BLF_size(blf_mono_font_render, 12 * U.pixelsize, 72);
562 }
563
UI_fontstyle_set(const uiFontStyle * fs)564 void UI_fontstyle_set(const uiFontStyle *fs)
565 {
566 uiFont *font = uifont_to_blfont(fs->uifont_id);
567
568 BLF_size(font->blf_id, fs->points * U.pixelsize, U.dpi);
569 }
570