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