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