/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2009 Blender Foundation. * All rights reserved. * * Contributor(s): Blender Foundation * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/editors/interface/interface_style.c * \ingroup edinterface */ #include #include #include #include #include "MEM_guardedalloc.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BKE_global.h" #include "BLF_api.h" #include "UI_interface.h" #include "ED_datafiles.h" #include "interface_intern.h" /* style + theme + layout-engine = UI */ /* This is a complete set of layout rules, the 'state' of the Layout Engine. Multiple styles are possible, defined via C or Python. Styles get a name, and will typically get activated per region type, like "Header", or "Listview" or "Toolbar". Properties of Style definitions are: - default collumn properties, internal spacing, aligning, min/max width - button alignment rules (for groups) - label placement rules - internal labeling or external labeling default - default minimum widths for buttons/labels (in amount of characters) - font types, styles and relative sizes for Panel titles, labels, etc. */ /* ********************************************** */ static uiStyle *ui_style_new(ListBase *styles, const char *name) { uiStyle *style= MEM_callocN(sizeof(uiStyle), "new style"); BLI_addtail(styles, style); BLI_strncpy(style->name, name, MAX_STYLE_NAME); style->panelzoom= 1.0; style->paneltitle.uifont_id= UIFONT_DEFAULT; style->paneltitle.points= 12; style->paneltitle.kerning= 1; style->paneltitle.shadow= 1; style->paneltitle.shadx= 0; style->paneltitle.shady= -1; style->paneltitle.shadowalpha= 0.15f; style->paneltitle.shadowcolor= 1.0f; style->grouplabel.uifont_id= UIFONT_DEFAULT; style->grouplabel.points= 12; style->grouplabel.kerning= 1; style->grouplabel.shadow= 3; style->grouplabel.shadx= 0; style->grouplabel.shady= -1; style->grouplabel.shadowalpha= 0.25f; style->widgetlabel.uifont_id= UIFONT_DEFAULT; style->widgetlabel.points= 11; style->widgetlabel.kerning= 1; style->widgetlabel.shadow= 3; style->widgetlabel.shadx= 0; style->widgetlabel.shady= -1; style->widgetlabel.shadowalpha= 0.15f; style->widgetlabel.shadowcolor= 1.0f; style->widget.uifont_id= UIFONT_DEFAULT; style->widget.points= 11; style->widget.kerning= 1; style->widget.shadowalpha= 0.25f; style->columnspace= 8; style->templatespace= 5; style->boxspace= 5; style->buttonspacex= 8; style->buttonspacey= 2; style->panelspace= 8; style->panelouter= 4; return style; } static uiFont *uifont_to_blfont(int id) { uiFont *font= U.uifonts.first; for(; font; font= font->next) { if(font->uifont_id==id) { return font; } } return U.uifonts.first; } /* *************** draw ************************ */ void uiStyleFontDrawExt(uiFontStyle *fs, rcti *rect, const char *str, float *r_xofs, float *r_yofs) { float height; int xofs=0, yofs; uiStyleFontSet(fs); height= BLF_height(fs->uifont_id, "2"); /* correct offset is on baseline, the j is below that */ yofs= floor( 0.5f*(rect->ymax - rect->ymin - height)); if(fs->align==UI_STYLE_TEXT_CENTER) { xofs= floor( 0.5f*(rect->xmax - rect->xmin - BLF_width(fs->uifont_id, str))); /* don't center text if it chops off the start of the text, 2 gives some margin */ if(xofs < 2) { xofs= 2; } } else if(fs->align==UI_STYLE_TEXT_RIGHT) { xofs= rect->xmax - rect->xmin - BLF_width(fs->uifont_id, str) - 1; } /* clip is very strict, so we give it some space */ BLF_clipping(fs->uifont_id, rect->xmin-1, rect->ymin-4, rect->xmax+1, rect->ymax+4); BLF_enable(fs->uifont_id, BLF_CLIPPING); BLF_position(fs->uifont_id, rect->xmin+xofs, rect->ymin+yofs, 0.0f); if (fs->shadow) { BLF_enable(fs->uifont_id, BLF_SHADOW); BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } if (fs->kerning == 1) BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); BLF_draw(fs->uifont_id, str, 65535); /* XXX, use real length */ BLF_disable(fs->uifont_id, BLF_CLIPPING); if (fs->shadow) BLF_disable(fs->uifont_id, BLF_SHADOW); if (fs->kerning == 1) BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); *r_xofs= xofs; *r_yofs= yofs; } void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, const char *str) { float xofs, yofs; uiStyleFontDrawExt(fs, rect, str, &xofs, &yofs); } /* drawn same as above, but at 90 degree angle */ void uiStyleFontDrawRotated(uiFontStyle *fs, rcti *rect, const char *str) { float height; int xofs, yofs; float angle; rcti txtrect; uiStyleFontSet(fs); height= BLF_height(fs->uifont_id, "2"); /* correct offset is on baseline, the j is below that */ /* becomes x-offset when rotated */ xofs= floor( 0.5f*(rect->ymax - rect->ymin - height)) + 1; /* ignore UI_STYLE, always aligned to top */ /* rotate counter-clockwise for now (assumes left-to-right language)*/ xofs+= height; yofs= BLF_width(fs->uifont_id, str) + 5; angle= 90.0f; /* translate rect to vertical */ txtrect.xmin= rect->xmin - (rect->ymax - rect->ymin); txtrect.ymin= rect->ymin - (rect->xmax - rect->xmin); txtrect.xmax= rect->xmin; txtrect.ymax= rect->ymin; /* clip is very strict, so we give it some space */ /* clipping is done without rotation, so make rect big enough to contain both positions */ BLF_clipping(fs->uifont_id, txtrect.xmin-1, txtrect.ymin-yofs-xofs-4, rect->xmax+1, rect->ymax+4); BLF_enable(fs->uifont_id, BLF_CLIPPING); BLF_position(fs->uifont_id, txtrect.xmin+xofs, txtrect.ymax-yofs, 0.0f); BLF_enable(fs->uifont_id, BLF_ROTATION); BLF_rotation(fs->uifont_id, angle); if (fs->shadow) { BLF_enable(fs->uifont_id, BLF_SHADOW); BLF_shadow(fs->uifont_id, fs->shadow, fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha); BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady); } if (fs->kerning == 1) BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT); BLF_draw(fs->uifont_id, str, 65535); /* XXX, use real length */ BLF_disable(fs->uifont_id, BLF_ROTATION); BLF_disable(fs->uifont_id, BLF_CLIPPING); if (fs->shadow) BLF_disable(fs->uifont_id, BLF_SHADOW); if (fs->kerning == 1) BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT); } /* ************** helpers ************************ */ /* temporarily, does widget font */ int UI_GetStringWidth(const char *str) { uiStyle *style= U.uistyles.first; uiFontStyle *fstyle= &style->widget; int width; if (fstyle->kerning==1) /* for BLF_width */ BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); uiStyleFontSet(fstyle); width= BLF_width(fstyle->uifont_id, str); if (fstyle->kerning==1) BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); return width; } /* temporarily, does widget font */ void UI_DrawString(float x, float y, const char *str) { uiStyle *style= U.uistyles.first; if (style->widget.kerning == 1) BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT); uiStyleFontSet(&style->widget); BLF_position(style->widget.uifont_id, x, y, 0.0f); BLF_draw(style->widget.uifont_id, str, 65535); /* XXX, use real length */ if (style->widget.kerning == 1) BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT); } /* ************** init exit ************************ */ /* called on each startup.blend read */ /* reading without uifont will create one */ void uiStyleInit(void) { uiFont *font= U.uifonts.first; uiStyle *style= U.uistyles.first; /* recover from uninitialized dpi */ if(U.dpi == 0) U.dpi= 72; CLAMP(U.dpi, 48, 128); /* default builtin */ if(font==NULL) { font= MEM_callocN(sizeof(uiFont), "ui font"); BLI_addtail(&U.uifonts, font); strcpy(font->filename, "default"); font->uifont_id= UIFONT_DEFAULT; } for(font= U.uifonts.first; font; font= font->next) { if(font->uifont_id==UIFONT_DEFAULT) { font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size); } else { font->blf_id= BLF_load(font->filename); if(font->blf_id == -1) font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size); } if (font->blf_id == -1) { if (G.f & G_DEBUG) printf("uiStyleInit error, no fonts available\n"); } else { /* ? just for speed to initialize? * Yes, this build the glyph cache and create * the texture. */ BLF_size(font->blf_id, 11, U.dpi); BLF_size(font->blf_id, 12, U.dpi); BLF_size(font->blf_id, 14, U.dpi); } } if(style==NULL) { ui_style_new(&U.uistyles, "Default Style"); } // XXX, this should be moved into a style, but for now best only load the monospaced font once. if (blf_mono_font == -1) blf_mono_font= BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); BLF_size(blf_mono_font, 12, 72); /* second for rendering else we get threading problems */ if (blf_mono_font_render == -1) blf_mono_font_render= BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); BLF_size(blf_mono_font_render, 12, 72); } void uiStyleFontSet(uiFontStyle *fs) { uiFont *font= uifont_to_blfont(fs->uifont_id); BLF_size(font->blf_id, fs->points, U.dpi); }