diff --git a/examples/20-nanovg/blendish.h b/examples/20-nanovg/blendish.h new file mode 100644 index 000000000..ba061416b --- /dev/null +++ b/examples/20-nanovg/blendish.h @@ -0,0 +1,1305 @@ +/* +Blendish - Blender 2.5 UI based theming functions for NanoVG + +Copyright (c) 2014 Leonard Ritter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef BLENDISH_H +#define BLENDISH_H + +#ifndef NANOVG_H +#error "nanovg.h must be included first." +#endif + +#define fmaxf bx::fmax +#define fminf bx::fmin + +#ifdef __cplusplus +extern "C" { +#endif + +/* + +Revision 3 (2014-07-08) + +Summary +------- + +Blendish is a small collection of drawing functions for NanoVG, designed to +replicate the look of the Blender 2.5+ User Interface. You can use these +functions to theme your UI library. Several metric constants for faithful +reproduction are also included. + +Blendish supports the original Blender icon sheet; As the licensing of Blenders +icons is unclear, they are not included in Blendishes repository, but a SVG +template, "icons_template.svg" is provided, which you can use to build your own +icon sheet. + +To use icons, you must first load the icon sheet using one of the +nvgCreateImage*() functions and then pass the image handle to bndSetIconImage(); +otherwise, no icons will be drawn. See bndSetIconImage() for more information. + +Blendish will not render text until a suitable UI font has been passed to +bndSetFont() has been called. See bndSetFont() for more information. + + +Drawbacks +--------- + +There is no support varying dpi resolutions yet. The library is hardcoded +to the equivalent of 72 dpi in the Blender system settings. + +Support for label truncation is missing. Text rendering breaks when widgets are +too short to contain their labels. + +Usage +----- + +To use this header file in implementation mode, define BLENDISH_IMPLEMENTATION +before including blendish.h, otherwise the file will be in header-only mode. + +*/ + +// if that typedef is provided elsewhere, you may define +// BLENDISH_NO_NVG_TYPEDEFS before including the header. +#ifndef BLENDISH_NO_NVG_TYPEDEFS +typedef struct NVGcontext NVGcontext; +typedef struct NVGcolor NVGcolor; +typedef struct NVGglyphPosition NVGglyphPosition; +#endif + +// describes the theme used to draw a single widget or widget box; +// these values correspond to the same values that can be retrieved from +// the Theme panel in the Blender preferences +typedef struct BNDwidgetTheme { + // color of widget box outline + NVGcolor outlineColor; + // color of widget item (meaning changes depending on class) + NVGcolor itemColor; + // fill color of widget box + NVGcolor innerColor; + // fill color of widget box when active + NVGcolor innerSelectedColor; + // color of text label + NVGcolor textColor; + // color of text label when active + NVGcolor textSelectedColor; + // delta modifier for upper part of gradient (-100 to 100) + int shadeTop; + // delta modifier for lower part of gradient (-100 to 100) + int shadeDown; +} BNDwidgetTheme; + +// describes the theme used to draw widgets +typedef struct BNDtheme { + // the background color of panels and windows + NVGcolor backgroundColor; + // theme for labels + BNDwidgetTheme regularTheme; + // theme for tool buttons + BNDwidgetTheme toolTheme; + // theme for radio buttons + BNDwidgetTheme radioTheme; + // theme for text fields + BNDwidgetTheme textFieldTheme; + // theme for option buttons (checkboxes) + BNDwidgetTheme optionTheme; + // theme for choice buttons (comboboxes) + // Blender calls them "menu buttons" + BNDwidgetTheme choiceTheme; + // theme for number fields + BNDwidgetTheme numberFieldTheme; + // theme for slider controls + BNDwidgetTheme sliderTheme; + // theme for scrollbars + BNDwidgetTheme scrollBarTheme; + // theme for tooltips + BNDwidgetTheme tooltipTheme; + // theme for menu backgrounds + BNDwidgetTheme menuTheme; + // theme for menu items + BNDwidgetTheme menuItemTheme; +} BNDtheme; + +// how text on a control is aligned +typedef enum BNDtextAlignment { + BND_LEFT = 0, + BND_CENTER, +} BNDtextAlignment; + +// states altering the styling of a widget +typedef enum BNDwidgetState { + // not interacting + BND_DEFAULT = 0, + // the mouse is hovering over the control + BND_HOVER, + // the widget is activated (pressed) or in an active state (toggled) + BND_ACTIVE +} BNDwidgetState; + +// flags indicating which corners are sharp (for grouping widgets) +typedef enum BNDcornerFlags { + // all corners are round + BND_CORNER_NONE = 0, + // sharp top left corner + BND_CORNER_TOP_LEFT = 1, + // sharp top right corner + BND_CORNER_TOP_RIGHT = 2, + // sharp bottom right corner + BND_CORNER_DOWN_RIGHT = 4, + // sharp bottom left corner + BND_CORNER_DOWN_LEFT = 8, + // all corners are sharp; + // you can invert a set of flags using ^= BND_CORNER_ALL + BND_CORNER_ALL = 0xF, + // top border is sharp + BND_CORNER_TOP = 3, + // bottom border is sharp + BND_CORNER_DOWN = 0xC, + // left border is sharp + BND_CORNER_LEFT = 9, + // right border is sharp + BND_CORNER_RIGHT = 6 +} BNDcornerFlags; + +// build an icon ID from two coordinates into the icon sheet, where +// (0,0) designates the upper-leftmost icon, (1,0) the one right next to it, +// and so on. +#define BND_ICONID(x,y) ((x)|((y)<<8)) + +// default widget height +#define BND_WIDGET_HEIGHT 21 +// default toolbutton width (if icon only) +#define BND_TOOL_WIDTH 20 + +// width of vertical scrollbar +#define BND_SCROLLBAR_WIDTH 13 +// height of horizontal scrollbar +#define BND_SCROLLBAR_HEIGHT 14 + +//////////////////////////////////////////////////////////////////////////////// + +// set the current theme all widgets will be drawn with. +// the default Blender 2.6 theme is set by default. +void bndSetTheme(BNDtheme theme); + +// Returns the currently set theme +const BNDtheme *bndGetTheme(); + +// designates an image handle as returned by nvgCreateImage*() as the themes' +// icon sheet. The icon sheet format must be compatible to Blender 2.6's icon +// sheet; the order of icons does not matter. +// A valid icon sheet is e.g. shown at +// http://wiki.blender.org/index.php/Dev:2.5/Doc/How_to/Add_an_icon +void bndSetIconImage(int image); + +// designates an image handle as returned by nvgCreateFont*() as the themes' +// UI font. Blender's original UI font Droid Sans is perfectly suited and +// available here: +// https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/datafiles/fonts/ +void bndSetFont(int font); + +//////////////////////////////////////////////////////////////////////////////// + +// High Level Functions +// -------------------- +// Use these functions to draw themed widgets with your NVGcontext. + +// Draw a label with its lower left origin at (x,y) and size of (w,h). +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndLabel(NVGcontext *ctx, + float x, float y, float w, float h, int iconid, const char *label); + +// Draw a tool button with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndToolButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label); + +// Draw a radio button with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndRadioButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label); + +// Draw a text field with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if iconid >= 0, an icon will be added to the widget +// if text is not NULL, text will be printed to the widget +// cbegin must be >= 0 and <= strlen(text) and denotes the beginning of the caret +// cend must be >= cbegin and <= strlen(text) and denotes the end of the caret +// if cend < cbegin, then no caret will be drawn +// widget looks best when height is BND_WIDGET_HEIGHT +void bndTextField(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *text, int cbegin, int cend); + +// Draw an option button with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndOptionButton(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + const char *label); + +// Draw a choice button with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndChoiceButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label); + +// Draw a number field with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// if label is not NULL, a label will be added to the widget +// if value is not NULL, a value will be added to the widget, along with +// a ":" separator +// widget looks best when height is BND_WIDGET_HEIGHT +void bndNumberField(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + const char *label, const char *value); + +// Draw slider control with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags and state denotes +// the widgets current UI state. +// progress must be in the range 0..1 and controls the size of the slider bar +// if label is not NULL, a label will be added to the widget +// if value is not NULL, a value will be added to the widget, along with +// a ":" separator +// widget looks best when height is BND_WIDGET_HEIGHT +void bndSlider(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + float progress, const char *label, const char *value); + +// Draw scrollbar with its lower left origin at (x,y) and size of (w,h), +// where state denotes the widgets current UI state. +// offset is in the range 0..1 and controls the position of the scroll handle +// size is in the range 0..1 and controls the size of the scroll handle +// horizontal widget looks best when height is BND_SCROLLBAR_HEIGHT, +// vertical looks best when width is BND_SCROLLBAR_WIDTH +void bndScrollBar(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + float offset, float size); + +// Draw a menu background with its lower left origin at (x,y) and size of (w,h), +// where flags is one or multiple flags from BNDcornerFlags. +void bndMenuBackground(NVGcontext *ctx, + float x, float y, float w, float h, int flags); + +// Draw a menu label with its lower left origin at (x,y) and size of (w,h). +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndMenuLabel(NVGcontext *ctx, + float x, float y, float w, float h, int iconid, const char *label); + +// Draw a menu item with its lower left origin at (x,y) and size of (w,h), +// where state denotes the widgets current UI state. +// if iconid >= 0, an icon will be added to the widget +// if label is not NULL, a label will be added to the widget +// widget looks best when height is BND_WIDGET_HEIGHT +void bndMenuItem(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + int iconid, const char *label); + +// Draw a tooltip background with its lower left origin at (x,y) and size of (w,h) +void bndTooltipBackground(NVGcontext *ctx, float x, float y, float w, float h); + +//////////////////////////////////////////////////////////////////////////////// + +// Low Level Functions +// ------------------- +// these are part of the implementation detail and can be used to theme +// new kinds of controls in a similar fashion. + +// make color transparent using the default alpha value +NVGcolor bndTransparent(NVGcolor color); + +// offset a color by a given integer delta in the range -100 to 100 +NVGcolor bndOffsetColor(NVGcolor color, int delta); + +// assigns radius r to the four entries of array radiuses depending on whether +// the corner is marked as sharp or not; see BNDcornerFlags for possible +// flag values. +void bndSelectCorners(float *radiuses, float r, int flags); + +// computes the upper and lower gradient colors for the inner box from a widget +// theme and the widgets state. If flipActive is set and the state is +// BND_ACTIVE, the upper and lower colors will be swapped. +void bndInnerColors(NVGcolor *shade_top, NVGcolor *shade_down, + const BNDwidgetTheme *theme, BNDwidgetState state, int flipActive); + +// computes the text color for a widget label from a widget theme and the +// widgets state. +NVGcolor bndTextColor(const BNDwidgetTheme *theme, BNDwidgetState state); + +// computes the bounds of the scrollbar handle from the scrollbar size +// and the handles offset and size. +// offset is in the range 0..1 and defines the position of the scroll handle +// size is in the range 0..1 and defines the size of the scroll handle +void bndScrollHandleRect(float *x, float *y, float *w, float *h, + float offset, float size); + +// Add a rounded box path at position (x,y) with size (w,h) and a separate +// radius for each corner listed in clockwise order, so that cr0 = top left, +// cr1 = top right, cr2 = bottom right, cr3 = bottom left; +// this is a low level drawing function: the path must be stroked or filled +// to become visible. +void bndRoundedBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3); + +// Draw a flat panel without any decorations at position (x,y) with size (w,h) +// and fills it with backgroundColor +void bndBackground(NVGcontext *ctx, float x, float y, float w, float h); + +// Draw a lower inset for a rounded box at position (x,y) with size (w,h) +// that gives the impression the surface has been pushed in. +// cr2 and cr3 contain the radiuses of the bottom right and bottom left +// corners of the rounded box. +void bndBevelInset(NVGcontext *ctx, float x, float y, float w, float h, + float cr2, float cr3); + +// Draw an icon with (x,y) as its upper left coordinate; the iconid selects +// the icon from the sheet; use the BND_ICONID macro to build icon IDs. +void bndIcon(NVGcontext *ctx, float x, float y, int iconid); + +// Draw a drop shadow around the rounded box at (x,y) with size (w,h) and +// radius r, with feather as its maximum range in pixels. +// No shadow will be painted inside the rounded box. +void bndDropShadow(NVGcontext *ctx, float x, float y, float w, float h, + float r, float feather, float alpha); + +// Draw the inner part of a widget box, with a gradient from shade_top to +// shade_down. If h>w, the gradient will be horizontal instead of +// vertical. +void bndInnerBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3, + NVGcolor shade_top, NVGcolor shade_down); + +// Draw the outline part of a widget box with the given color +void bndOutlineBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3, NVGcolor color); + +// Draw an optional icon specified by and an optional label with +// given alignment (BNDtextAlignment), fontsize and color within a widget box. +// if iconid is >= 0, an icon will be drawn and the labels remaining space +// will be adjusted. +// if label is not NULL, it will be drawn with the specified alignment, fontsize +// and color. +// if value is not NULL, label and value will be drawn with a ":" separator +// inbetween. +void bndIconLabelValue(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, int align, float fontsize, const char *label, + const char *value); + +// Draw an optional icon specified by , an optional label and +// a caret with given fontsize and color within a widget box. +// if iconid is >= 0, an icon will be drawn and the labels remaining space +// will be adjusted. +// if label is not NULL, it will be drawn with the specified alignment, fontsize +// and color. +// cbegin must be >= 0 and <= strlen(text) and denotes the beginning of the caret +// cend must be >= cbegin and <= strlen(text) and denotes the end of the caret +// if cend < cbegin, then no caret will be drawn +void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, float fontsize, const char *label, + NVGcolor caretcolor, int cbegin, int cend); + +// Draw a checkmark for an option box with the given upper left coordinates +// (ox,oy) with the specified color. +void bndCheck(NVGcontext *ctx, float ox, float oy, NVGcolor color); + +// Draw a horizontal arrow for a number field with its center at (x,y) and +// size s; if s is negative, the arrow points to the left. +void bndArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color); + +// Draw an up/down arrow for a choice box with its center at (x,y) and size s +void bndUpDownArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color); + +#ifdef __cplusplus +}; +#endif + +#endif // BLENDISH_H + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#ifdef BLENDISH_IMPLEMENTATION + +#include +#include + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define BND_INLINE inline + #else + #define BND_INLINE + #endif +#else + #define BND_INLINE inline +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// default text size +#define BND_LABEL_FONT_SIZE 13 + +// default text padding in inner box +#define BND_PAD_LEFT 8 +#define BND_PAD_RIGHT 8 + +// label: value separator string +#define BND_LABEL_SEPARATOR ": " + +// alpha intensity of transparent items (0xa4) +#define BND_TRANSPARENT_ALPHA 0.643 + +// shade intensity of beveled insets +#define BND_BEVEL_SHADE 30 +// shade intensity of hovered inner boxes +#define BND_HOVER_SHADE 30 + +// width of icon sheet +#define BND_ICON_SHEET_WIDTH 602 +// height of icon sheet +#define BND_ICON_SHEET_HEIGHT 640 +// gridsize of icon sheet in both dimensions +#define BND_ICON_SHEET_GRID 21 +// offset of first icon tile relative to left border +#define BND_ICON_SHEET_OFFSET_X 5 +// offset of first icon tile relative to top border +#define BND_ICON_SHEET_OFFSET_Y 10 +// resolution of single icon +#define BND_ICON_SHEET_RES 16 + +// size of number field arrow +#define BND_NUMBER_ARROW_SIZE 4 + +// default text color +#define BND_COLOR_TEXT {{{ 0,0,0,1 }}} +// default highlighted text color +#define BND_COLOR_TEXT_SELECTED {{{ 1,1,1,1 }}} + +// radius of tool button +#define BND_TOOL_RADIUS 4 + +// radius of option button +#define BND_OPTION_RADIUS 4 +// width of option button checkbox +#define BND_OPTION_WIDTH 14 +// height of option button checkbox +#define BND_OPTION_HEIGHT 15 + +// radius of text field +#define BND_TEXT_RADIUS 4 + +// radius of number button +#define BND_NUMBER_RADIUS 10 + +// radius of menu popup +#define BND_MENU_RADIUS 3 +// feather of menu popup shadow +#define BND_SHADOW_FEATHER 12 +// alpha of menu popup shadow +#define BND_SHADOW_ALPHA 0.5 + +// radius of scrollbar +#define BND_SCROLLBAR_RADIUS 7 +// shade intensity of active scrollbar +#define BND_SCROLLBAR_ACTIVE_SHADE 15 + +// max glyphs for position testing +#define BND_MAX_GLYPHS 1024 + +//////////////////////////////////////////////////////////////////////////////// + +BND_INLINE float bnd_clamp(float v, float mn, float mx) { + return (v > mx)?mx:(v < mn)?mn:v; +} + +//////////////////////////////////////////////////////////////////////////////// + +// the initial theme +static BNDtheme bnd_theme = { + // backgroundColor + {{{ 0.447, 0.447, 0.447, 1.0 }}}, + // regularTheme + { + {{{ 0.098,0.098,0.098,1 }}}, // color_outline + {{{ 0.098,0.098,0.098,1 }}}, // color_item + {{{ 0.6,0.6,0.6,1 }}}, // color_inner + {{{ 0.392,0.392,0.392,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 0, // shade_top + 0, // shade_down + }, + // toolTheme + { + {{{ 0.098,0.098,0.098,1 }}}, // color_outline + {{{ 0.098,0.098,0.098,1 }}}, // color_item + {{{ 0.6,0.6,0.6,1 }}}, // color_inner + {{{ 0.392,0.392,0.392,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 15, // shade_top + -15, // shade_down + }, + // radioTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 1,1,1,1 }}}, // color_item + {{{ 0.275,0.275,0.275,1 }}}, // color_inner + {{{ 0.337,0.502,0.761,1 }}}, // color_inner_selected + BND_COLOR_TEXT_SELECTED, // color_text + BND_COLOR_TEXT, // color_text_selected + 15, // shade_top + -15, // shade_down + }, + // textFieldTheme + { + {{{ 0.098,0.098,0.098,1 }}}, // color_outline + {{{ 0.353, 0.353, 0.353,1 }}}, // color_item + {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner + {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 0, // shade_top + 25, // shade_down + }, + // optionTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 1,1,1,1 }}}, // color_item + {{{ 0.275,0.275,0.275,1 }}}, // color_inner + {{{ 0.275,0.275,0.275,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 15, // shade_top + -15, // shade_down + }, + // choiceTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 1,1,1,1 }}}, // color_item + {{{ 0.275,0.275,0.275,1 }}}, // color_inner + {{{ 0.275,0.275,0.275,1 }}}, // color_inner_selected + BND_COLOR_TEXT_SELECTED, // color_text + {{{ 0.8,0.8,0.8,1 }}}, // color_text_selected + 15, // shade_top + -15, // shade_down + }, + // numberFieldTheme + { + {{{ 0.098,0.098,0.098,1 }}}, // color_outline + {{{ 0.353, 0.353, 0.353,1 }}}, // color_item + {{{ 0.706, 0.706, 0.706,1 }}}, // color_inner + {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + -20, // shade_top + 0, // shade_down + }, + // sliderTheme + { + {{{ 0.098,0.098,0.098,1 }}}, // color_outline + {{{ 0.502,0.502,0.502,1 }}}, // color_item + {{{ 0.706, 0.706, 0.706,1 }}}, // color_inner + {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + -20, // shade_top + 0, // shade_down + }, + // scrollBarTheme + { + {{{ 0.196,0.196,0.196,1 }}}, // color_outline + {{{ 0.502,0.502,0.502,1 }}}, // color_item + {{{ 0.314, 0.314, 0.314,0.706 }}}, // color_inner + {{{ 0.392, 0.392, 0.392,0.706 }}}, // color_inner_selected + BND_COLOR_TEXT, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 5, // shade_top + -5, // shade_down + }, + // tooltipTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 0.392,0.392,0.392,1 }}}, // color_item + {{{ 0.098, 0.098, 0.098, 0.902 }}}, // color_inner + {{{ 0.176, 0.176, 0.176, 0.902 }}}, // color_inner_selected + {{{ 0.627, 0.627, 0.627, 1 }}}, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 0, // shade_top + 0, // shade_down + }, + // menuTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 0.392,0.392,0.392,1 }}}, // color_item + {{{ 0.098, 0.098, 0.098, 0.902 }}}, // color_inner + {{{ 0.176, 0.176, 0.176, 0.902 }}}, // color_inner_selected + {{{ 0.627, 0.627, 0.627, 1 }}}, // color_text + BND_COLOR_TEXT_SELECTED, // color_text_selected + 0, // shade_top + 0, // shade_down + }, + // menuItemTheme + { + {{{ 0,0,0,1 }}}, // color_outline + {{{ 0.675,0.675,0.675,0.502 }}}, // color_item + {{{ 0,0,0,0 }}}, // color_inner + {{{ 0.337,0.502,0.761,1 }}}, // color_inner_selected + BND_COLOR_TEXT_SELECTED, // color_text + BND_COLOR_TEXT, // color_text_selected + 38, // shade_top + 0, // shade_down + }, +}; + +//////////////////////////////////////////////////////////////////////////////// + +void bndSetTheme(BNDtheme theme) { + bnd_theme = theme; +} + +const BNDtheme *bndGetTheme() { + return &bnd_theme; +} + +// the handle to the image containing the icon sheet +static int bnd_icon_image = -1; + +void bndSetIconImage(int image) { + bnd_icon_image = image; +} + +// the handle to the UI font +static int bnd_font = -1; + +void bndSetFont(int font) { + bnd_font = font; +} + +//////////////////////////////////////////////////////////////////////////////// + +void bndLabel(NVGcontext *ctx, + float x, float y, float w, float h, int iconid, const char *label) { + bndIconLabelValue(ctx,x,y,w,h,iconid, + bnd_theme.regularTheme.textColor, BND_LEFT, + BND_LABEL_FONT_SIZE, label, NULL); +} + +void bndToolButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_TOOL_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.toolTheme, state, 1); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.toolTheme.outlineColor)); + bndIconLabelValue(ctx,x,y,w,h,iconid, + bndTextColor(&bnd_theme.toolTheme, state), BND_CENTER, + BND_LABEL_FONT_SIZE, label, NULL); +} + +void bndRadioButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_OPTION_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.radioTheme, state, 1); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.radioTheme.outlineColor)); + bndIconLabelValue(ctx,x,y,w,h,iconid, + bndTextColor(&bnd_theme.radioTheme, state), BND_CENTER, + BND_LABEL_FONT_SIZE, label, NULL); +} + +void bndTextField(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *text, int cbegin, int cend) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_TEXT_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.textFieldTheme, state, 0); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.textFieldTheme.outlineColor)); + if (state != BND_ACTIVE) { + cend = -1; + } + bndIconLabelCaret(ctx,x,y,w,h,iconid, + bndTextColor(&bnd_theme.textFieldTheme, state), BND_LABEL_FONT_SIZE, + text, bnd_theme.textFieldTheme.itemColor, cbegin, cend); +} + +void bndOptionButton(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + const char *label) { + float ox, oy; + NVGcolor shade_top, shade_down; + + ox = x; + oy = y+h-BND_OPTION_HEIGHT-3; + + bndBevelInset(ctx,ox,oy, + BND_OPTION_WIDTH,BND_OPTION_HEIGHT, + BND_OPTION_RADIUS,BND_OPTION_RADIUS); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.optionTheme, state, 1); + bndInnerBox(ctx,ox,oy, + BND_OPTION_WIDTH,BND_OPTION_HEIGHT, + BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS, + shade_top, shade_down); + bndOutlineBox(ctx,ox,oy, + BND_OPTION_WIDTH,BND_OPTION_HEIGHT, + BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS, + bndTransparent(bnd_theme.optionTheme.outlineColor)); + if (state == BND_ACTIVE) { + bndCheck(ctx,ox,oy, bndTransparent(bnd_theme.optionTheme.itemColor)); + } + bndIconLabelValue(ctx,x+12,y,w-12,h,-1, + bndTextColor(&bnd_theme.optionTheme, state), BND_LEFT, + BND_LABEL_FONT_SIZE, label, NULL); +} + +void bndChoiceButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_OPTION_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.choiceTheme, state, 1); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.choiceTheme.outlineColor)); + bndIconLabelValue(ctx,x,y,w,h,iconid, + bndTextColor(&bnd_theme.choiceTheme, state), BND_LEFT, + BND_LABEL_FONT_SIZE, label, NULL); + bndUpDownArrow(ctx,x+w-10,y+10,5, + bndTransparent(bnd_theme.choiceTheme.itemColor)); +} + +void bndNumberField(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + const char *label, const char *value) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_NUMBER_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.numberFieldTheme, state, 0); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.numberFieldTheme.outlineColor)); + bndIconLabelValue(ctx,x,y,w,h,-1, + bndTextColor(&bnd_theme.numberFieldTheme, state), BND_CENTER, + BND_LABEL_FONT_SIZE, label, value); + bndArrow(ctx,x+8,y+10,-BND_NUMBER_ARROW_SIZE, + bndTransparent(bnd_theme.numberFieldTheme.itemColor)); + bndArrow(ctx,x+w-8,y+10,BND_NUMBER_ARROW_SIZE, + bndTransparent(bnd_theme.numberFieldTheme.itemColor)); +} + +void bndSlider(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + float progress, const char *label, const char *value) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_NUMBER_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.sliderTheme, state, 0); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + + if (state == BND_ACTIVE) { + shade_top = bndOffsetColor( + bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeTop); + shade_down = bndOffsetColor( + bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeDown); + } else { + shade_top = bndOffsetColor( + bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeDown); + shade_down = bndOffsetColor( + bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeTop); + } + nvgScissor(ctx,x,y,8+(w-8)*bnd_clamp(progress,0,1),h); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + nvgResetScissor(ctx); + + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.sliderTheme.outlineColor)); + bndIconLabelValue(ctx,x,y,w,h,-1, + bndTextColor(&bnd_theme.sliderTheme, state), BND_CENTER, + BND_LABEL_FONT_SIZE, label, value); +} + +void bndScrollBar(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + float offset, float size) { + + bndBevelInset(ctx,x,y,w,h, + BND_SCROLLBAR_RADIUS, BND_SCROLLBAR_RADIUS); + bndInnerBox(ctx,x,y,w,h, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + bndOffsetColor( + bnd_theme.scrollBarTheme.innerColor, 3*bnd_theme.scrollBarTheme.shadeDown), + bndOffsetColor( + bnd_theme.scrollBarTheme.innerColor, 3*bnd_theme.scrollBarTheme.shadeTop)); + bndOutlineBox(ctx,x,y,w,h, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + bndTransparent(bnd_theme.scrollBarTheme.outlineColor)); + + NVGcolor itemColor = bndOffsetColor( + bnd_theme.scrollBarTheme.itemColor, + (state == BND_ACTIVE)?BND_SCROLLBAR_ACTIVE_SHADE:0); + + bndScrollHandleRect(&x,&y,&w,&h,offset,size); + + bndInnerBox(ctx,x,y,w,h, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + bndOffsetColor( + itemColor, 3*bnd_theme.scrollBarTheme.shadeTop), + bndOffsetColor( + itemColor, 3*bnd_theme.scrollBarTheme.shadeDown)); + bndOutlineBox(ctx,x,y,w,h, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS, + bndTransparent(bnd_theme.scrollBarTheme.outlineColor)); +} + +void bndMenuBackground(NVGcontext *ctx, + float x, float y, float w, float h, int flags) { + float cr[4]; + NVGcolor shade_top, shade_down; + + bndSelectCorners(cr, BND_MENU_RADIUS, flags); + bndInnerColors(&shade_top, &shade_down, &bnd_theme.menuTheme, + BND_DEFAULT, 0); + bndInnerBox(ctx,x,y,w,h+1,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h+1,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.menuTheme.outlineColor)); + bndDropShadow(ctx,x,y,w,h,BND_MENU_RADIUS, + BND_SHADOW_FEATHER,BND_SHADOW_ALPHA); +} + +void bndTooltipBackground(NVGcontext *ctx, float x, float y, float w, float h) { + NVGcolor shade_top, shade_down; + + bndInnerColors(&shade_top, &shade_down, &bnd_theme.tooltipTheme, + BND_DEFAULT, 0); + bndInnerBox(ctx,x,y,w,h+1, + BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS, + shade_top, shade_down); + bndOutlineBox(ctx,x,y,w,h+1, + BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS, + bndTransparent(bnd_theme.tooltipTheme.outlineColor)); + bndDropShadow(ctx,x,y,w,h,BND_MENU_RADIUS, + BND_SHADOW_FEATHER,BND_SHADOW_ALPHA); +} + +void bndMenuLabel(NVGcontext *ctx, + float x, float y, float w, float h, int iconid, const char *label) { + bndIconLabelValue(ctx,x,y,w,h,iconid, + bnd_theme.menuTheme.textColor, BND_LEFT, + BND_LABEL_FONT_SIZE, label, NULL); +} + +void bndMenuItem(NVGcontext *ctx, + float x, float y, float w, float h, BNDwidgetState state, + int iconid, const char *label) { + if (state != BND_DEFAULT) { + bndInnerBox(ctx,x,y,w,h,0,0,0,0, + bndOffsetColor(bnd_theme.menuItemTheme.innerSelectedColor, + bnd_theme.menuItemTheme.shadeTop), + bndOffsetColor(bnd_theme.menuItemTheme.innerSelectedColor, + bnd_theme.menuItemTheme.shadeDown)); + state = BND_ACTIVE; + } + bndIconLabelValue(ctx,x,y,w,h,iconid, + bndTextColor(&bnd_theme.menuItemTheme, state), BND_LEFT, + BND_LABEL_FONT_SIZE, label, NULL); +} + +//////////////////////////////////////////////////////////////////////////////// + +void bndRoundedBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3) { + float d; + + w = fmaxf(0, w); + h = fmaxf(0, h); + d = fminf(w, h); + + nvgMoveTo(ctx, x,y+h*0.5f); + nvgArcTo(ctx, x,y, x+w,y, fminf(cr0, d/2)); + nvgArcTo(ctx, x+w,y, x+w,y+h, fminf(cr1, d/2)); + nvgArcTo(ctx, x+w,y+h, x,y+h, fminf(cr2, d/2)); + nvgArcTo(ctx, x,y+h, x,y, fminf(cr3, d/2)); + nvgClosePath(ctx); +} + +NVGcolor bndTransparent(NVGcolor color) { + color.a *= BND_TRANSPARENT_ALPHA; + return color; +} + +NVGcolor bndOffsetColor(NVGcolor color, int delta) { + float offset = (float)delta / 255.0f; + return delta?( + nvgRGBAf( + bnd_clamp(color.r+offset,0,1), + bnd_clamp(color.g+offset,0,1), + bnd_clamp(color.b+offset,0,1), + color.a) + ):color; +} + +void bndBevelInset(NVGcontext *ctx, float x, float y, float w, float h, + float cr2, float cr3) { + float d; + + y -= 0.5f; + d = fminf(w, h); + cr2 = fminf(cr2, d/2); + cr3 = fminf(cr3, d/2); + + nvgBeginPath(ctx); + nvgMoveTo(ctx, x+w,y+h-cr2); + nvgArcTo(ctx, x+w,y+h, x,y+h, cr2); + nvgArcTo(ctx, x,y+h, x,y, cr3); + + NVGcolor bevelColor = bndOffsetColor(bnd_theme.backgroundColor, + BND_BEVEL_SHADE); + + nvgStrokeWidth(ctx, 1); + nvgStrokePaint(ctx, + nvgLinearGradient(ctx, + x,y+h-fmaxf(cr2,cr3)-1, + x,y+h-1, + nvgRGBAf(bevelColor.r, bevelColor.g, bevelColor.b, 0), + bevelColor)); + nvgStroke(ctx); +} + +void bndBackground(NVGcontext *ctx, float x, float y, float w, float h) { + nvgBeginPath(ctx); + nvgRect(ctx, x, y, w, h); + nvgFillColor(ctx, bnd_theme.backgroundColor); + nvgFill(ctx); +} + +void bndIcon(NVGcontext *ctx, float x, float y, int iconid) { + int ix, iy, u, v; + if (bnd_icon_image < 0) return; // no icons loaded + + ix = iconid & 0xff; + iy = (iconid>>8) & 0xff; + u = BND_ICON_SHEET_OFFSET_X + ix*BND_ICON_SHEET_GRID; + v = BND_ICON_SHEET_OFFSET_Y + iy*BND_ICON_SHEET_GRID; + + nvgBeginPath(ctx); + nvgRect(ctx,x,y,BND_ICON_SHEET_RES,BND_ICON_SHEET_RES); + nvgFillPaint(ctx, + nvgImagePattern(ctx,x-u,y-v, + BND_ICON_SHEET_WIDTH, + BND_ICON_SHEET_HEIGHT, + 0,bnd_icon_image,0)); + nvgFill(ctx); +} + +void bndDropShadow(NVGcontext *ctx, float x, float y, float w, float h, + float r, float feather, float alpha) { + + nvgBeginPath(ctx); + y += feather; + h -= feather; + + nvgMoveTo(ctx, x-feather, y-feather); + nvgLineTo(ctx, x, y-feather); + nvgLineTo(ctx, x, y+h-feather); + nvgArcTo(ctx, x,y+h,x+r,y+h,r); + nvgArcTo(ctx, x+w,y+h,x+w,y+h-r,r); + nvgLineTo(ctx, x+w, y-feather); + nvgLineTo(ctx, x+w+feather, y-feather); + nvgLineTo(ctx, x+w+feather, y+h+feather); + nvgLineTo(ctx, x-feather, y+h+feather); + nvgClosePath(ctx); + + nvgFillPaint(ctx, nvgBoxGradient(ctx, + x - feather*0.5f,y - feather*0.5f, + w + feather,h+feather, + r+feather*0.5f, + feather, + nvgRGBAf(0,0,0,alpha*alpha), + nvgRGBAf(0,0,0,0))); + nvgFill(ctx); +} + +void bndInnerBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3, + NVGcolor shade_top, NVGcolor shade_down) { + nvgBeginPath(ctx); + bndRoundedBox(ctx,x+1,y+1,w-2,h-3, + fmaxf(0,cr0-1),fmaxf(0,cr1-1),fmaxf(0,cr2-1),fmaxf(0,cr3-1)); + nvgFillPaint(ctx,((h-2)>w)? + nvgLinearGradient(ctx,x,y,x+w,y,shade_top,shade_down): + nvgLinearGradient(ctx,x,y,x,y+h,shade_top,shade_down)); + nvgFill(ctx); +} + +void bndOutlineBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3, NVGcolor color) { + nvgBeginPath(ctx); + bndRoundedBox(ctx,x+0.5f,y+0.5f,w-1,h-2,cr0,cr1,cr2,cr3); + nvgStrokeColor(ctx,color); + nvgStrokeWidth(ctx,1); + nvgStroke(ctx); +} + +void bndSelectCorners(float *radiuses, float r, int flags) { + radiuses[0] = (flags & BND_CORNER_TOP_LEFT)?0:r; + radiuses[1] = (flags & BND_CORNER_TOP_RIGHT)?0:r; + radiuses[2] = (flags & BND_CORNER_DOWN_RIGHT)?0:r; + radiuses[3] = (flags & BND_CORNER_DOWN_LEFT)?0:r; +} + +void bndInnerColors( + NVGcolor *shade_top, NVGcolor *shade_down, + const BNDwidgetTheme *theme, BNDwidgetState state, int flipActive) { + + switch(state) { + default: + case BND_DEFAULT: { + *shade_top = bndOffsetColor(theme->innerColor, theme->shadeTop); + *shade_down = bndOffsetColor(theme->innerColor, theme->shadeDown); + } break; + case BND_HOVER: { + NVGcolor color = bndOffsetColor(theme->innerColor, BND_HOVER_SHADE); + *shade_top = bndOffsetColor(color, theme->shadeTop); + *shade_down = bndOffsetColor(color, theme->shadeDown); + } break; + case BND_ACTIVE: { + *shade_top = bndOffsetColor(theme->innerSelectedColor, + flipActive?theme->shadeDown:theme->shadeTop); + *shade_down = bndOffsetColor(theme->innerSelectedColor, + flipActive?theme->shadeTop:theme->shadeDown); + } break; + } +} + +NVGcolor bndTextColor(const BNDwidgetTheme *theme, BNDwidgetState state) { + return (state == BND_ACTIVE)?theme->textSelectedColor:theme->textColor; +} + +void bndIconLabelValue(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, int align, float fontsize, const char *label, + const char *value) { + float pleft = BND_PAD_LEFT; + if (label) { + if (iconid >= 0) { + bndIcon(ctx,x+4,y+2,iconid); + pleft += BND_ICON_SHEET_RES; + } + + if (bnd_font < 0) return; + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, fontsize); + nvgBeginPath(ctx); + nvgFillColor(ctx, color); + if (value) { + float label_width = nvgTextBounds(ctx, 1, 1, label, NULL, NULL); + float sep_width = nvgTextBounds(ctx, 1, 1, + BND_LABEL_SEPARATOR, NULL, NULL); + + nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE); + x += pleft; + if (align == BND_CENTER) { + float width = label_width + sep_width + + nvgTextBounds(ctx, 1, 1, value, NULL, NULL); + x += ((w-BND_PAD_RIGHT-pleft)-width)*0.5f; + } + y += h-6; + nvgText(ctx, x, y, label, NULL); + x += label_width; + nvgText(ctx, x, y, BND_LABEL_SEPARATOR, NULL); + x += sep_width; + nvgText(ctx, x, y, value, NULL); + } else { + nvgTextAlign(ctx, + (align==BND_LEFT)?(NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE): + (NVG_ALIGN_CENTER|NVG_ALIGN_BASELINE)); + nvgTextBox(ctx,x+pleft,y+h-6,w-BND_PAD_RIGHT-pleft,label, NULL); + } + } else if (iconid >= 0) { + bndIcon(ctx,x+2,y+2,iconid); + } +} + +void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, float fontsize, const char *label, + NVGcolor caretcolor, int cbegin, int cend) { + float bounds[4]; + float pleft = BND_TEXT_RADIUS; + if (!label) return; + if (iconid >= 0) { + bndIcon(ctx,x+4,y+2,iconid); + pleft += BND_ICON_SHEET_RES; + } + + if (bnd_font < 0) return; + + x+=pleft; + y+=h-6; + + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, fontsize); + nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE); + + if (cend >= cbegin) { + float c0,c1; + const char *cb;const char *ce; + static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; + int nglyphs = nvgTextGlyphPositions( + ctx, x, y, label, label+cend+1, glyphs, BND_MAX_GLYPHS); + c0=glyphs[0].x; + c1=glyphs[nglyphs-1].x; + cb = label+cbegin; ce = label+cend; + // TODO: this is slow + for (int i=0; i < nglyphs; ++i) { + if (glyphs[i].str == cb) + c0 = glyphs[i].x; + if (glyphs[i].str == ce) + c1 = glyphs[i].x; + } + + nvgTextBounds(ctx,x,y,label,NULL, bounds); + nvgBeginPath(ctx); + if (cbegin == cend) { + nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761)); + nvgRect(ctx, c0-1, bounds[1], 2, bounds[3]-bounds[1]); + } else { + nvgFillColor(ctx, caretcolor); + nvgRect(ctx, c0-1, bounds[1], c1-c0+1, bounds[3]-bounds[1]); + } + nvgFill(ctx); + } + + nvgBeginPath(ctx); + nvgFillColor(ctx, color); + nvgTextBox(ctx,x,y,w-BND_TEXT_RADIUS-pleft,label, NULL); +} + +void bndCheck(NVGcontext *ctx, float ox, float oy, NVGcolor color) { + nvgBeginPath(ctx); + nvgStrokeWidth(ctx,2); + nvgStrokeColor(ctx,color); + nvgLineCap(ctx,NVG_BUTT); + nvgLineJoin(ctx,NVG_MITER); + nvgMoveTo(ctx,ox+4,oy+5); + nvgLineTo(ctx,ox+7,oy+8); + nvgLineTo(ctx,ox+14,oy+1); + nvgStroke(ctx); +} + +void bndArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color) { + nvgBeginPath(ctx); + nvgMoveTo(ctx,x,y); + nvgLineTo(ctx,x-s,y+s); + nvgLineTo(ctx,x-s,y-s); + nvgClosePath(ctx); + nvgFillColor(ctx,color); + nvgFill(ctx); +} + +void bndUpDownArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color) { + float w; + + nvgBeginPath(ctx); + w = 1.1f*s; + nvgMoveTo(ctx,x,y-1); + nvgLineTo(ctx,x+0.5*w,y-s-1); + nvgLineTo(ctx,x+w,y-1); + nvgClosePath(ctx); + nvgMoveTo(ctx,x,y+1); + nvgLineTo(ctx,x+0.5*w,y+s+1); + nvgLineTo(ctx,x+w,y+1); + nvgClosePath(ctx); + nvgFillColor(ctx,color); + nvgFill(ctx); +} + +void bndScrollHandleRect(float *x, float *y, float *w, float *h, + float offset, float size) { + size = bnd_clamp(size,0,1); + offset = bnd_clamp(offset,0,1); + if ((*h) > (*w)) { + float hs = fmaxf(size*(*h), (*w)+1); + *y = (*y) + ((*h)-hs)*offset; + *h = hs; + } else { + float ws = fmaxf(size*(*w), (*h)-1); + *x = (*x) + ((*w)-ws)*offset; + *w = ws; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef BND_INLINE +#undef BND_INLINE +#endif + +#endif // BLENDISH_IMPLEMENTATION diff --git a/examples/20-nanovg/nanovg.cpp b/examples/20-nanovg/nanovg.cpp index bd31b0bc9..9ab06a04a 100644 --- a/examples/20-nanovg/nanovg.cpp +++ b/examples/20-nanovg/nanovg.cpp @@ -32,6 +32,14 @@ #include "entry/entry.h" #include "nanovg/nanovg.h" +#ifdef BX_COMPILER_MSVC + #pragma warning (disable: 4305) // Switch off truncation from double to float. + #pragma warning (disable: 4244) // Switch off conversion from int to float, posssible loss of data. +#endif + +#define BLENDISH_IMPLEMENTATION +#include "blendish.h" + #define ICON_SEARCH 0x1F50D #define ICON_CIRCLED_CROSS 0x2716 #define ICON_CHEVRON_RIGHT 0xE75E @@ -778,9 +786,158 @@ void drawLines(struct NVGcontext* vg, float x, float y, float w, float h, float nvgRestore(vg); } +void drawBlendish(struct NVGcontext* _vg, int _x, int _y, float _w, float _h, float _t) +{ + // Don't draw background. + //bndBackground(_vg, float(_x-10), float(_y-10), _w, _h); + + int x = _x; + int y = _y; + + bndToolButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, BND_ICONID(6,3),"Default"); + y += 25; + bndToolButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, BND_ICONID(6,3),"Hovered"); + y += 25; + bndToolButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, BND_ICONID(6,3),"Active"); + + y += 40; + bndRadioButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, -1,"Default"); + y += 25; + bndRadioButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, -1,"Hovered"); + y += 25; + bndRadioButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, -1,"Active"); + + y += 25; + bndLabel(_vg,x,y,120,BND_WIDGET_HEIGHT,-1,"Label:"); + y += BND_WIDGET_HEIGHT; + bndChoiceButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, -1, "Default"); + y += 25; + bndChoiceButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, -1, "Hovered"); + y += 25; + bndChoiceButton(_vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, -1, "Active"); + + y += 25; + int ry = y; + int rx = x; + + y = _y; + x += 130; + bndOptionButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_DEFAULT,"Default"); + y += 25; + bndOptionButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_HOVER,"Hovered"); + y += 25; + bndOptionButton(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_ACTIVE,"Active"); + + y += 40; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_DOWN,BND_DEFAULT, "Top","100"); + y += BND_WIDGET_HEIGHT-2; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, "Center","100"); + y += BND_WIDGET_HEIGHT-2; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_TOP,BND_DEFAULT, "Bottom","100"); + + int mx = x-30; + int my = y-12; + int mw = 120; + bndMenuBackground(_vg,mx,my,mw,120,BND_CORNER_TOP); + bndMenuLabel(_vg,mx,my,mw,BND_WIDGET_HEIGHT,-1,"Menu Title"); + my += BND_WIDGET_HEIGHT-2; + bndMenuItem(_vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_DEFAULT, BND_ICONID(17,3),"Default"); + my += BND_WIDGET_HEIGHT-2; + bndMenuItem(_vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_HOVER, BND_ICONID(18,3),"Hovered"); + my += BND_WIDGET_HEIGHT-2; + bndMenuItem(_vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_ACTIVE, BND_ICONID(19,3),"Active"); + + y = _y; + x += 130; + int ox = x; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, "Default","100"); + y += 25; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, "Hovered","100"); + y += 25; + bndNumberField(_vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, "Active","100"); + + y += 40; + bndRadioButton(_vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT,BND_DEFAULT, -1,"One"); + x += 60-1; + bndRadioButton(_vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, -1,"Two"); + x += 60-1; + bndRadioButton(_vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, -1,"Three"); + x += 60-1; + bndRadioButton(_vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_LEFT,BND_ACTIVE, -1,"Butts"); + + x = ox; + y += 40; + float progress_value = fmodf(_t/10.0,1.0); + char progress_label[32]; + sprintf(progress_label, "%d%%", int(progress_value*100+0.5f)); + bndSlider(_vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, progress_value,"Default",progress_label); + y += 25; + bndSlider(_vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, progress_value,"Hovered",progress_label); + y += 25; + bndSlider(_vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, progress_value,"Active",progress_label); + + int rw = x+240-rx; + float s_offset = sinf(_t/2.0)*0.5+0.5; + float s_size = cosf(_t/3.11)*0.5+0.5; + + bndScrollBar(_vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_DEFAULT,s_offset,s_size); + ry += 20; + bndScrollBar(_vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_HOVER,s_offset,s_size); + ry += 20; + bndScrollBar(_vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_ACTIVE,s_offset,s_size); + + const char edit_text[] = "The quick brown fox"; + int textlen = int(strlen(edit_text)+1); + int t = int(_t*2); + int idx1 = (t/textlen)%textlen; + int idx2 = idx1 + (t%(textlen-idx1)); + + ry += 25; + bndTextField(_vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, -1, edit_text, idx1, idx2); + ry += 25; + bndTextField(_vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, -1, edit_text, idx1, idx2); + ry += 25; + bndTextField(_vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, -1, edit_text, idx1, idx2); + + rx += rw + 20; + ry = _y; + bndScrollBar(_vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_DEFAULT,s_offset,s_size); + rx += 20; + bndScrollBar(_vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_HOVER,s_offset,s_size); + rx += 20; + bndScrollBar(_vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_ACTIVE,s_offset,s_size); + + x = ox; + y += 40; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT, BND_DEFAULT,BND_ICONID(0,10),NULL); + x += BND_TOOL_WIDTH-1; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(1,10),NULL); + x += BND_TOOL_WIDTH-1; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(2,10),NULL); + x += BND_TOOL_WIDTH-1; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(3,10),NULL); + x += BND_TOOL_WIDTH-1; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(4,10),NULL); + x += BND_TOOL_WIDTH-1; + bndToolButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, BND_DEFAULT,BND_ICONID(5,10),NULL); + x += BND_TOOL_WIDTH-1; + x += 5; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT, BND_DEFAULT,BND_ICONID(0,11),NULL); + x += BND_TOOL_WIDTH-1; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(1,11),NULL); + x += BND_TOOL_WIDTH-1; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(2,11),NULL); + x += BND_TOOL_WIDTH-1; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_DEFAULT,BND_ICONID(3,11),NULL); + x += BND_TOOL_WIDTH-1; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, BND_ACTIVE,BND_ICONID(4,11),NULL); + x += BND_TOOL_WIDTH-1; + bndRadioButton(_vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, BND_DEFAULT,BND_ICONID(5,11),NULL); +} + struct DemoData { - int fontNormal, fontBold, fontIcons; + int fontNormal, fontBold, fontIcons; int images[12]; }; @@ -983,18 +1140,16 @@ void drawWidths(struct NVGcontext* vg, float x, float y, float width) void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, struct DemoData* data) { - float x,y,popy; + float x,y,popx,popy; - drawEyes(vg, width - 250, 50, 150, 100, mx, my, t); - drawParagraph(vg, width - 450, 50, 150, 100, mx, my); + drawEyes(vg, width-800, height-280, 150, 100, mx, my, t); + drawParagraph(vg, width - 550, 35, 150, 100, mx, my); drawGraph(vg, 0, height/2, width, height/2, t); - drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t); + + drawColorwheel(vg, width-350, 35, 250.0f, 250.0f, t); // Line joints - drawLines(vg, 50, height-50, 600, 50, t); - - // Line width; - drawWidths(vg, 10, 50, 30); + drawLines(vg, 50, height-50, 600, 35, t); nvgSave(vg); if (blowup) @@ -1003,12 +1158,18 @@ void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float he nvgScale(vg, 2.0f, 2.0f); } - // Widgets - drawWindow(vg, "Widgets `n Stuff", 50, 50, 300, 400); - x = 60; y = 95; + // Line width. + drawWidths(vg, width-50, 35, 30); + + // Widgets. + x = width-520; y = height-420; + drawWindow(vg, "Widgets `n Stuff", x, y, 300, 400); + x += 10; + y += 45; drawSearchBox(vg, "Search", x,y,280,25); y += 40; drawDropDown(vg, "Effects", x,y,280,28); + popx = x + 300; popy = y + 14; y += 45; @@ -1034,7 +1195,10 @@ void renderDemo(struct NVGcontext* vg, float mx, float my, float width, float he drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0)); // Thumbnails box - drawThumbnails(vg, 365, popy-30, 160, 300, data->images, 12, t); + drawThumbnails(vg, popx, popy-30, 160, 300, data->images, 12, t); + + // Blendish + drawBlendish(vg, 10, 62, 600.0f, 420.0f, t); nvgRestore(vg); } @@ -1066,6 +1230,9 @@ int _main_(int /*_argc*/, char** /*_argv*/) DemoData data; loadDemoData(nvg, &data); + bndSetFont(nvgCreateFont(nvg, "droidsans", "font/droidsans.ttf")); + bndSetIconImage(nvgCreateImage(nvg, "images/blender_icons16.png")); + int64_t timeOffset = bx::getHPCounter(); entry::MouseState mouseState; @@ -1093,7 +1260,7 @@ int _main_(int /*_argc*/, char** /*_argv*/) nvgEndFrame(nvg); - // Advance to next frame. Rendering thread will be kicked to + // Advance to next frame. Rendering thread will be kicked to // process submitted rendering primitives. bgfx::frame(); } diff --git a/examples/20-nanovg/screenshot.png b/examples/20-nanovg/screenshot.png index dc9c24c7e..e0b281021 100644 Binary files a/examples/20-nanovg/screenshot.png and b/examples/20-nanovg/screenshot.png differ diff --git a/examples/runtime/images/blender_icons16.png b/examples/runtime/images/blender_icons16.png new file mode 100644 index 000000000..2f2f8deed Binary files /dev/null and b/examples/runtime/images/blender_icons16.png differ