2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2009 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/interface/interface_style.c
27 * \ingroup edinterface
36 #include "MEM_guardedalloc.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_userdef_types.h"
41 #include "BLI_listbase.h"
43 #include "BLI_string.h"
44 #include "BLI_utildefines.h"
46 #include "BKE_global.h"
50 #include "BLF_translation.h"
52 #include "UI_interface.h"
54 #include "ED_datafiles.h"
56 #include "interface_intern.h"
59 # include "BLI_math_base.h" /* M_PI */
62 /* style + theme + layout-engine = UI */
65 * This is a complete set of layout rules, the 'state' of the Layout
66 * Engine. Multiple styles are possible, defined via C or Python. Styles
67 * get a name, and will typically get activated per region type, like
68 * "Header", or "Listview" or "Toolbar". Properties of Style definitions
71 * - default column properties, internal spacing, aligning, min/max width
72 * - button alignment rules (for groups)
73 * - label placement rules
74 * - internal labeling or external labeling default
75 * - default minimum widths for buttons/labels (in amount of characters)
76 * - font types, styles and relative sizes for Panel titles, labels, etc.
80 /* ********************************************** */
82 static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id)
84 uiStyle *style = MEM_callocN(sizeof(uiStyle), "new style");
86 BLI_addtail(styles, style);
87 BLI_strncpy(style->name, name, MAX_STYLE_NAME);
89 style->panelzoom = 1.0; /* unused */
91 style->paneltitle.uifont_id = uifont_id;
92 style->paneltitle.points = 12;
93 style->paneltitle.kerning = 1;
94 style->paneltitle.shadow = 1;
95 style->paneltitle.shadx = 0;
96 style->paneltitle.shady = -1;
97 style->paneltitle.shadowalpha = 0.15f;
98 style->paneltitle.shadowcolor = 1.0f;
100 style->grouplabel.uifont_id = uifont_id;
101 style->grouplabel.points = 12;
102 style->grouplabel.kerning = 1;
103 style->grouplabel.shadow = 3;
104 style->grouplabel.shadx = 0;
105 style->grouplabel.shady = -1;
106 style->grouplabel.shadowalpha = 0.25f;
108 style->widgetlabel.uifont_id = uifont_id;
109 style->widgetlabel.points = 11;
110 style->widgetlabel.kerning = 1;
111 style->widgetlabel.shadow = 3;
112 style->widgetlabel.shadx = 0;
113 style->widgetlabel.shady = -1;
114 style->widgetlabel.shadowalpha = 0.15f;
115 style->widgetlabel.shadowcolor = 1.0f;
117 style->widget.uifont_id = uifont_id;
118 style->widget.points = 11;
119 style->widget.kerning = 1;
120 style->widget.shadowalpha = 0.25f;
122 style->columnspace = 8;
123 style->templatespace = 5;
125 style->buttonspacex = 8;
126 style->buttonspacey = 2;
127 style->panelspace = 8;
128 style->panelouter = 4;
133 static uiFont *uifont_to_blfont(int id)
135 uiFont *font = U.uifonts.first;
137 for (; font; font = font->next) {
138 if (font->uifont_id == id) {
142 return U.uifonts.first;
145 /* *************** draw ************************ */
148 void uiStyleFontDrawExt(uiFontStyle *fs, const rcti *rect, const char *str,
149 size_t len, float *r_xofs, float *r_yofs)
156 height = BLF_ascender(fs->uifont_id);
157 yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
159 if (fs->align == UI_STYLE_TEXT_CENTER) {
160 xofs = floor(0.5f * (BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len)));
161 /* don't center text if it chops off the start of the text, 2 gives some margin */
166 else if (fs->align == UI_STYLE_TEXT_RIGHT) {
167 xofs = BLI_rcti_size_x(rect) - BLF_width(fs->uifont_id, str, len) - 0.1f * U.widget_unit;
170 /* clip is very strict, so we give it some space */
171 BLF_clipping(fs->uifont_id, rect->xmin - 2, rect->ymin - 4, rect->xmax + 1, rect->ymax + 4);
172 BLF_enable(fs->uifont_id, BLF_CLIPPING);
173 BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f);
176 BLF_enable(fs->uifont_id, BLF_SHADOW);
177 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
178 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
181 if (fs->kerning == 1)
182 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
184 BLF_draw(fs->uifont_id, str, len);
185 BLF_disable(fs->uifont_id, BLF_CLIPPING);
187 BLF_disable(fs->uifont_id, BLF_SHADOW);
188 if (fs->kerning == 1)
189 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
195 void uiStyleFontDraw(uiFontStyle *fs, const rcti *rect, const char *str)
198 uiStyleFontDrawExt(fs, rect, str,
199 BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs);
202 /* drawn same as above, but at 90 degree angle */
203 void uiStyleFontDrawRotated(uiFontStyle *fs, const rcti *rect, const char *str)
212 height = BLF_ascender(fs->uifont_id);
213 /* becomes x-offset when rotated */
214 xofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
216 /* ignore UI_STYLE, always aligned to top */
218 /* rotate counter-clockwise for now (assumes left-to-right language)*/
220 yofs = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX) + 5;
221 angle = (float)M_PI / 2.0f;
223 /* translate rect to vertical */
224 txtrect.xmin = rect->xmin - BLI_rcti_size_y(rect);
225 txtrect.ymin = rect->ymin - BLI_rcti_size_x(rect);
226 txtrect.xmax = rect->xmin;
227 txtrect.ymax = rect->ymin;
229 /* clip is very strict, so we give it some space */
230 /* clipping is done without rotation, so make rect big enough to contain both positions */
231 BLF_clipping(fs->uifont_id, txtrect.xmin - 1, txtrect.ymin - yofs - xofs - 4, rect->xmax + 1, rect->ymax + 4);
232 BLF_enable(fs->uifont_id, BLF_CLIPPING);
233 BLF_position(fs->uifont_id, txtrect.xmin + xofs, txtrect.ymax - yofs, 0.0f);
235 BLF_enable(fs->uifont_id, BLF_ROTATION);
236 BLF_rotation(fs->uifont_id, angle);
239 BLF_enable(fs->uifont_id, BLF_SHADOW);
240 BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
241 BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
244 if (fs->kerning == 1)
245 BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
247 BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
248 BLF_disable(fs->uifont_id, BLF_ROTATION);
249 BLF_disable(fs->uifont_id, BLF_CLIPPING);
251 BLF_disable(fs->uifont_id, BLF_SHADOW);
252 if (fs->kerning == 1)
253 BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
256 /* ************** helpers ************************ */
257 /* XXX: read a style configure */
258 uiStyle *UI_GetStyle(void)
260 uiStyle *style = NULL;
261 /* offset is two struct uiStyle pointers */
262 /* style = BLI_findstring(&U.uistyles, "Unifont Style", sizeof(style) * 2) */;
263 return (style != NULL) ? style : U.uistyles.first;
266 /* for drawing, scaled with DPI setting */
267 uiStyle *UI_GetStyleDraw(void)
269 uiStyle *style = UI_GetStyle();
270 static uiStyle _style;
274 _style.paneltitle.shadx = (short)(UI_DPI_FAC * _style.paneltitle.shadx);
275 _style.paneltitle.shady = (short)(UI_DPI_FAC * _style.paneltitle.shady);
276 _style.grouplabel.shadx = (short)(UI_DPI_FAC * _style.grouplabel.shadx);
277 _style.grouplabel.shady = (short)(UI_DPI_FAC * _style.grouplabel.shady);
278 _style.widgetlabel.shadx = (short)(UI_DPI_FAC * _style.widgetlabel.shadx);
279 _style.widgetlabel.shady = (short)(UI_DPI_FAC * _style.widgetlabel.shady);
281 _style.columnspace = (short)(UI_DPI_FAC * _style.columnspace);
282 _style.templatespace = (short)(UI_DPI_FAC * _style.templatespace);
283 _style.boxspace = (short)(UI_DPI_FAC * _style.boxspace);
284 _style.buttonspacex = (short)(UI_DPI_FAC * _style.buttonspacex);
285 _style.buttonspacey = (short)(UI_DPI_FAC * _style.buttonspacey);
286 _style.panelspace = (short)(UI_DPI_FAC * _style.panelspace);
287 _style.panelouter = (short)(UI_DPI_FAC * _style.panelouter);
292 /* temporarily, does widget font */
293 int UI_GetStringWidth(const char *str)
295 uiStyle *style = UI_GetStyle();
296 uiFontStyle *fstyle = &style->widget;
299 if (fstyle->kerning == 1) /* for BLF_width */
300 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
302 uiStyleFontSet(fstyle);
303 width = BLF_width(fstyle->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
305 if (fstyle->kerning == 1)
306 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
311 /* temporarily, does widget font */
312 void UI_DrawString(float x, float y, const char *str)
314 uiStyle *style = UI_GetStyle();
316 if (style->widget.kerning == 1)
317 BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
319 uiStyleFontSet(&style->widget);
320 BLF_position(style->widget.uifont_id, x, y, 0.0f);
321 BLF_draw(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
323 if (style->widget.kerning == 1)
324 BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
327 /* ************** init exit ************************ */
329 /* called on each startup.blend read */
330 /* reading without uifont will create one */
331 void uiStyleInit(void)
333 uiFont *font = U.uifonts.first;
334 uiStyle *style = U.uistyles.first;
335 int monofont_size = datatoc_bmonofont_ttf_size;
336 unsigned char *monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf;
338 /* recover from uninitialized dpi */
341 CLAMP(U.dpi, 48, 144);
343 for (font = U.uifonts.first; font; font = font->next) {
344 BLF_unload_id(font->blf_id);
347 font = U.uifonts.first;
349 /* default builtin */
351 font = MEM_callocN(sizeof(uiFont), "ui font");
352 BLI_addtail(&U.uifonts, font);
355 if (U.font_path_ui[0]) {
356 BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename));
357 font->uifont_id = UIFONT_CUSTOM1;
360 BLI_strncpy(font->filename, "default", sizeof(font->filename));
361 font->uifont_id = UIFONT_DEFAULT;
364 for (font = U.uifonts.first; font; font = font->next) {
366 if (font->uifont_id == UIFONT_DEFAULT) {
367 #ifdef WITH_INTERNATIONAL
368 int font_size = datatoc_bfont_ttf_size;
369 unsigned char *font_ttf = (unsigned char *)datatoc_bfont_ttf;
370 static int last_font_size = 0;
372 /* use unicode font for translation */
373 if (U.transopts & USER_DOTRANSLATE) {
374 font_ttf = BLF_get_unifont(&font_size);
377 /* fall back if not found */
378 font_size = datatoc_bfont_ttf_size;
379 font_ttf = (unsigned char *)datatoc_bfont_ttf;
383 /* relload only if needed */
384 if (last_font_size != font_size) {
385 BLF_unload("default");
386 last_font_size = font_size;
389 font->blf_id = BLF_load_mem("default", font_ttf, font_size);
391 font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
395 font->blf_id = BLF_load(font->filename);
396 if (font->blf_id == -1) {
397 font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
400 BLF_default_set(font->blf_id);
404 if (font->blf_id == -1) {
405 if (G.debug & G_DEBUG)
406 printf("%s: error, no fonts available\n", __func__);
409 /* ? just for speed to initialize?
410 * Yes, this build the glyph cache and create
413 BLF_size(font->blf_id, 11 * U.pixelsize, U.dpi);
414 BLF_size(font->blf_id, 12 * U.pixelsize, U.dpi);
415 BLF_size(font->blf_id, 14 * U.pixelsize, U.dpi);
420 ui_style_new(&U.uistyles, "Default Style", UIFONT_DEFAULT);
423 #ifdef WITH_INTERNATIONAL
424 /* use unicode font for text editor and interactive console */
425 if (U.transopts & USER_DOTRANSLATE) {
426 monofont_ttf = BLF_get_unifont_mono(&monofont_size);
429 /* fall back if not found */
430 monofont_size = datatoc_bmonofont_ttf_size;
431 monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf;
436 BLF_unload("monospace");
438 blf_mono_font_render = -1;
441 /* XXX, this should be moved into a style, but for now best only load the monospaced font once. */
442 if (blf_mono_font == -1)
443 blf_mono_font = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
445 BLF_size(blf_mono_font, 12 * U.pixelsize, 72);
447 /* second for rendering else we get threading problems */
448 if (blf_mono_font_render == -1)
449 blf_mono_font_render = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
451 BLF_size(blf_mono_font_render, 12 * U.pixelsize, 72);
454 void uiStyleFontSet(uiFontStyle *fs)
456 uiFont *font = uifont_to_blfont(fs->uifont_id);
458 BLF_size(font->blf_id, fs->points * U.pixelsize, U.dpi);