From 912c95c0a7e469b4b382ce4827e334bd6bc2b4b4 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 2 May 2015 20:29:47 +0200 Subject: [PATCH] Add blendish code --- dgl/src/oui-blendish/LICENSE | 21 + dgl/src/oui-blendish/blendish.h | 2399 +++++++++++++++++++++++++++++++ dgl/src/oui-blendish/oui.h | 2025 ++++++++++++++++++++++++++ 3 files changed, 4445 insertions(+) create mode 100644 dgl/src/oui-blendish/LICENSE create mode 100644 dgl/src/oui-blendish/blendish.h create mode 100644 dgl/src/oui-blendish/oui.h diff --git a/dgl/src/oui-blendish/LICENSE b/dgl/src/oui-blendish/LICENSE new file mode 100644 index 00000000..c1603468 --- /dev/null +++ b/dgl/src/oui-blendish/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/dgl/src/oui-blendish/blendish.h b/dgl/src/oui-blendish/blendish.h new file mode 100644 index 00000000..9f8c9d23 --- /dev/null +++ b/dgl/src/oui-blendish/blendish.h @@ -0,0 +1,2399 @@ +/* +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 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + +Revision 6 (2014-09-21) + +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 for 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. + +*/ + +// you can override this from the outside to pick +// the export level you need +#ifndef BND_EXPORT +#define BND_EXPORT +#endif + +// 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 nodes +typedef struct BNDnodeTheme { + // inner color of selected node (and downarrow) + NVGcolor nodeSelectedColor; + // outline of wires + NVGcolor wiresColor; + // color of text label when active + NVGcolor textSelectedColor; + + // inner color of active node (and dragged wire) + NVGcolor activeNodeColor; + // color of selected wire + NVGcolor wireSelectColor; + // color of background of node + NVGcolor nodeBackdropColor; + + // how much a noodle curves (0 to 10) + int noodleCurving; +} BNDnodeTheme; + +// 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; + // theme for nodes + BNDnodeTheme nodeTheme; +} 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)) +// alpha of disabled widget groups +// can be used in conjunction with nvgGlobalAlpha() +#define BND_DISABLED_ALPHA 0.5 + +enum { + // default widget height + BND_WIDGET_HEIGHT = 21, + // default toolbutton width (if icon only) + BND_TOOL_WIDTH = 20, + + // default radius of node ports + BND_NODE_PORT_RADIUS = 5, + // top margin of node content + BND_NODE_MARGIN_TOP = 25, + // bottom margin of node content + BND_NODE_MARGIN_DOWN = 5, + // left and right margin of node content + BND_NODE_MARGIN_SIDE = 10, + // height of node title bar + BND_NODE_TITLE_HEIGHT = 20, + // width of node title arrow click area + BND_NODE_ARROW_AREA_WIDTH = 20, + + // size of splitter corner click area + BND_SPLITTER_AREA_SIZE = 12, + + // width of vertical scrollbar + BND_SCROLLBAR_WIDTH = 13, + // height of horizontal scrollbar + BND_SCROLLBAR_HEIGHT = 14, + + // default vertical spacing + BND_VSPACING = 1, + // default vertical spacing between groups + BND_VSPACING_GROUP = 8, + // default horizontal spacing + BND_HSPACING = 8, +}; + +typedef enum BNDicon { + BND_ICON_NONE = BND_ICONID(0,29), + BND_ICON_QUESTION = BND_ICONID(1,29), + BND_ICON_ERROR = BND_ICONID(2,29), + BND_ICON_CANCEL = BND_ICONID(3,29), + BND_ICON_TRIA_RIGHT = BND_ICONID(4,29), + BND_ICON_TRIA_DOWN = BND_ICONID(5,29), + BND_ICON_TRIA_LEFT = BND_ICONID(6,29), + BND_ICON_TRIA_UP = BND_ICONID(7,29), + BND_ICON_ARROW_LEFTRIGHT = BND_ICONID(8,29), + BND_ICON_PLUS = BND_ICONID(9,29), + BND_ICON_DISCLOSURE_TRI_DOWN = BND_ICONID(10,29), + BND_ICON_DISCLOSURE_TRI_RIGHT = BND_ICONID(11,29), + BND_ICON_RADIOBUT_OFF = BND_ICONID(12,29), + BND_ICON_RADIOBUT_ON = BND_ICONID(13,29), + BND_ICON_MENU_PANEL = BND_ICONID(14,29), + BND_ICON_BLENDER = BND_ICONID(15,29), + BND_ICON_GRIP = BND_ICONID(16,29), + BND_ICON_DOT = BND_ICONID(17,29), + BND_ICON_COLLAPSEMENU = BND_ICONID(18,29), + BND_ICON_X = BND_ICONID(19,29), + BND_ICON_GO_LEFT = BND_ICONID(21,29), + BND_ICON_PLUG = BND_ICONID(22,29), + BND_ICON_UI = BND_ICONID(23,29), + BND_ICON_NODE = BND_ICONID(24,29), + BND_ICON_NODE_SEL = BND_ICONID(25,29), + + BND_ICON_FULLSCREEN = BND_ICONID(0,28), + BND_ICON_SPLITSCREEN = BND_ICONID(1,28), + BND_ICON_RIGHTARROW_THIN = BND_ICONID(2,28), + BND_ICON_BORDERMOVE = BND_ICONID(3,28), + BND_ICON_VIEWZOOM = BND_ICONID(4,28), + BND_ICON_ZOOMIN = BND_ICONID(5,28), + BND_ICON_ZOOMOUT = BND_ICONID(6,28), + BND_ICON_PANEL_CLOSE = BND_ICONID(7,28), + BND_ICON_COPY_ID = BND_ICONID(8,28), + BND_ICON_EYEDROPPER = BND_ICONID(9,28), + BND_ICON_LINK_AREA = BND_ICONID(10,28), + BND_ICON_AUTO = BND_ICONID(11,28), + BND_ICON_CHECKBOX_DEHLT = BND_ICONID(12,28), + BND_ICON_CHECKBOX_HLT = BND_ICONID(13,28), + BND_ICON_UNLOCKED = BND_ICONID(14,28), + BND_ICON_LOCKED = BND_ICONID(15,28), + BND_ICON_UNPINNED = BND_ICONID(16,28), + BND_ICON_PINNED = BND_ICONID(17,28), + BND_ICON_SCREEN_BACK = BND_ICONID(18,28), + BND_ICON_RIGHTARROW = BND_ICONID(19,28), + BND_ICON_DOWNARROW_HLT = BND_ICONID(20,28), + BND_ICON_DOTSUP = BND_ICONID(21,28), + BND_ICON_DOTSDOWN = BND_ICONID(22,28), + BND_ICON_LINK = BND_ICONID(23,28), + BND_ICON_INLINK = BND_ICONID(24,28), + BND_ICON_PLUGIN = BND_ICONID(25,28), + + BND_ICON_HELP = BND_ICONID(0,27), + BND_ICON_GHOST_ENABLED = BND_ICONID(1,27), + BND_ICON_COLOR = BND_ICONID(2,27), + BND_ICON_LINKED = BND_ICONID(3,27), + BND_ICON_UNLINKED = BND_ICONID(4,27), + BND_ICON_HAND = BND_ICONID(5,27), + BND_ICON_ZOOM_ALL = BND_ICONID(6,27), + BND_ICON_ZOOM_SELECTED = BND_ICONID(7,27), + BND_ICON_ZOOM_PREVIOUS = BND_ICONID(8,27), + BND_ICON_ZOOM_IN = BND_ICONID(9,27), + BND_ICON_ZOOM_OUT = BND_ICONID(10,27), + BND_ICON_RENDER_REGION = BND_ICONID(11,27), + BND_ICON_BORDER_RECT = BND_ICONID(12,27), + BND_ICON_BORDER_LASSO = BND_ICONID(13,27), + BND_ICON_FREEZE = BND_ICONID(14,27), + BND_ICON_STYLUS_PRESSURE = BND_ICONID(15,27), + BND_ICON_GHOST_DISABLED = BND_ICONID(16,27), + BND_ICON_NEW = BND_ICONID(17,27), + BND_ICON_FILE_TICK = BND_ICONID(18,27), + BND_ICON_QUIT = BND_ICONID(19,27), + BND_ICON_URL = BND_ICONID(20,27), + BND_ICON_RECOVER_LAST = BND_ICONID(21,27), + BND_ICON_FULLSCREEN_ENTER = BND_ICONID(23,27), + BND_ICON_FULLSCREEN_EXIT = BND_ICONID(24,27), + BND_ICON_BLANK1 = BND_ICONID(25,27), + + BND_ICON_LAMP = BND_ICONID(0,26), + BND_ICON_MATERIAL = BND_ICONID(1,26), + BND_ICON_TEXTURE = BND_ICONID(2,26), + BND_ICON_ANIM = BND_ICONID(3,26), + BND_ICON_WORLD = BND_ICONID(4,26), + BND_ICON_SCENE = BND_ICONID(5,26), + BND_ICON_EDIT = BND_ICONID(6,26), + BND_ICON_GAME = BND_ICONID(7,26), + BND_ICON_RADIO = BND_ICONID(8,26), + BND_ICON_SCRIPT = BND_ICONID(9,26), + BND_ICON_PARTICLES = BND_ICONID(10,26), + BND_ICON_PHYSICS = BND_ICONID(11,26), + BND_ICON_SPEAKER = BND_ICONID(12,26), + BND_ICON_TEXTURE_SHADED = BND_ICONID(13,26), + + BND_ICON_VIEW3D = BND_ICONID(0,25), + BND_ICON_IPO = BND_ICONID(1,25), + BND_ICON_OOPS = BND_ICONID(2,25), + BND_ICON_BUTS = BND_ICONID(3,25), + BND_ICON_FILESEL = BND_ICONID(4,25), + BND_ICON_IMAGE_COL = BND_ICONID(5,25), + BND_ICON_INFO = BND_ICONID(6,25), + BND_ICON_SEQUENCE = BND_ICONID(7,25), + BND_ICON_TEXT = BND_ICONID(8,25), + BND_ICON_IMASEL = BND_ICONID(9,25), + BND_ICON_SOUND = BND_ICONID(10,25), + BND_ICON_ACTION = BND_ICONID(11,25), + BND_ICON_NLA = BND_ICONID(12,25), + BND_ICON_SCRIPTWIN = BND_ICONID(13,25), + BND_ICON_TIME = BND_ICONID(14,25), + BND_ICON_NODETREE = BND_ICONID(15,25), + BND_ICON_LOGIC = BND_ICONID(16,25), + BND_ICON_CONSOLE = BND_ICONID(17,25), + BND_ICON_PREFERENCES = BND_ICONID(18,25), + BND_ICON_CLIP = BND_ICONID(19,25), + BND_ICON_ASSET_MANAGER = BND_ICONID(20,25), + + BND_ICON_OBJECT_DATAMODE = BND_ICONID(0,24), + BND_ICON_EDITMODE_HLT = BND_ICONID(1,24), + BND_ICON_FACESEL_HLT = BND_ICONID(2,24), + BND_ICON_VPAINT_HLT = BND_ICONID(3,24), + BND_ICON_TPAINT_HLT = BND_ICONID(4,24), + BND_ICON_WPAINT_HLT = BND_ICONID(5,24), + BND_ICON_SCULPTMODE_HLT = BND_ICONID(6,24), + BND_ICON_POSE_HLT = BND_ICONID(7,24), + BND_ICON_PARTICLEMODE = BND_ICONID(8,24), + BND_ICON_LIGHTPAINT = BND_ICONID(9,24), + + BND_ICON_SCENE_DATA = BND_ICONID(0,23), + BND_ICON_RENDERLAYERS = BND_ICONID(1,23), + BND_ICON_WORLD_DATA = BND_ICONID(2,23), + BND_ICON_OBJECT_DATA = BND_ICONID(3,23), + BND_ICON_MESH_DATA = BND_ICONID(4,23), + BND_ICON_CURVE_DATA = BND_ICONID(5,23), + BND_ICON_META_DATA = BND_ICONID(6,23), + BND_ICON_LATTICE_DATA = BND_ICONID(7,23), + BND_ICON_LAMP_DATA = BND_ICONID(8,23), + BND_ICON_MATERIAL_DATA = BND_ICONID(9,23), + BND_ICON_TEXTURE_DATA = BND_ICONID(10,23), + BND_ICON_ANIM_DATA = BND_ICONID(11,23), + BND_ICON_CAMERA_DATA = BND_ICONID(12,23), + BND_ICON_PARTICLE_DATA = BND_ICONID(13,23), + BND_ICON_LIBRARY_DATA_DIRECT = BND_ICONID(14,23), + BND_ICON_GROUP = BND_ICONID(15,23), + BND_ICON_ARMATURE_DATA = BND_ICONID(16,23), + BND_ICON_POSE_DATA = BND_ICONID(17,23), + BND_ICON_BONE_DATA = BND_ICONID(18,23), + BND_ICON_CONSTRAINT = BND_ICONID(19,23), + BND_ICON_SHAPEKEY_DATA = BND_ICONID(20,23), + BND_ICON_CONSTRAINT_BONE = BND_ICONID(21,23), + BND_ICON_CAMERA_STEREO = BND_ICONID(22,23), + BND_ICON_PACKAGE = BND_ICONID(23,23), + BND_ICON_UGLYPACKAGE = BND_ICONID(24,23), + + BND_ICON_BRUSH_DATA = BND_ICONID(0,22), + BND_ICON_IMAGE_DATA = BND_ICONID(1,22), + BND_ICON_FILE = BND_ICONID(2,22), + BND_ICON_FCURVE = BND_ICONID(3,22), + BND_ICON_FONT_DATA = BND_ICONID(4,22), + BND_ICON_RENDER_RESULT = BND_ICONID(5,22), + BND_ICON_SURFACE_DATA = BND_ICONID(6,22), + BND_ICON_EMPTY_DATA = BND_ICONID(7,22), + BND_ICON_SETTINGS = BND_ICONID(8,22), + BND_ICON_RENDER_ANIMATION = BND_ICONID(9,22), + BND_ICON_RENDER_STILL = BND_ICONID(10,22), + BND_ICON_BOIDS = BND_ICONID(12,22), + BND_ICON_STRANDS = BND_ICONID(13,22), + BND_ICON_LIBRARY_DATA_INDIRECT = BND_ICONID(14,22), + BND_ICON_GREASEPENCIL = BND_ICONID(15,22), + BND_ICON_LINE_DATA = BND_ICONID(16,22), + BND_ICON_GROUP_BONE = BND_ICONID(18,22), + BND_ICON_GROUP_VERTEX = BND_ICONID(19,22), + BND_ICON_GROUP_VCOL = BND_ICONID(20,22), + BND_ICON_GROUP_UVS = BND_ICONID(21,22), + BND_ICON_RNA = BND_ICONID(24,22), + BND_ICON_RNA_ADD = BND_ICONID(25,22), + + BND_ICON_OUTLINER_OB_EMPTY = BND_ICONID(0,20), + BND_ICON_OUTLINER_OB_MESH = BND_ICONID(1,20), + BND_ICON_OUTLINER_OB_CURVE = BND_ICONID(2,20), + BND_ICON_OUTLINER_OB_LATTICE = BND_ICONID(3,20), + BND_ICON_OUTLINER_OB_META = BND_ICONID(4,20), + BND_ICON_OUTLINER_OB_LAMP = BND_ICONID(5,20), + BND_ICON_OUTLINER_OB_CAMERA = BND_ICONID(6,20), + BND_ICON_OUTLINER_OB_ARMATURE = BND_ICONID(7,20), + BND_ICON_OUTLINER_OB_FONT = BND_ICONID(8,20), + BND_ICON_OUTLINER_OB_SURFACE = BND_ICONID(9,20), + BND_ICON_OUTLINER_OB_SPEAKER = BND_ICONID(10,20), + BND_ICON_RESTRICT_VIEW_OFF = BND_ICONID(19,20), + BND_ICON_RESTRICT_VIEW_ON = BND_ICONID(20,20), + BND_ICON_RESTRICT_SELECT_OFF = BND_ICONID(21,20), + BND_ICON_RESTRICT_SELECT_ON = BND_ICONID(22,20), + BND_ICON_RESTRICT_RENDER_OFF = BND_ICONID(23,20), + BND_ICON_RESTRICT_RENDER_ON = BND_ICONID(24,20), + + BND_ICON_OUTLINER_DATA_EMPTY = BND_ICONID(0,19), + BND_ICON_OUTLINER_DATA_MESH = BND_ICONID(1,19), + BND_ICON_OUTLINER_DATA_CURVE = BND_ICONID(2,19), + BND_ICON_OUTLINER_DATA_LATTICE = BND_ICONID(3,19), + BND_ICON_OUTLINER_DATA_META = BND_ICONID(4,19), + BND_ICON_OUTLINER_DATA_LAMP = BND_ICONID(5,19), + BND_ICON_OUTLINER_DATA_CAMERA = BND_ICONID(6,19), + BND_ICON_OUTLINER_DATA_ARMATURE = BND_ICONID(7,19), + BND_ICON_OUTLINER_DATA_FONT = BND_ICONID(8,19), + BND_ICON_OUTLINER_DATA_SURFACE = BND_ICONID(9,19), + BND_ICON_OUTLINER_DATA_SPEAKER = BND_ICONID(10,19), + BND_ICON_OUTLINER_DATA_POSE = BND_ICONID(11,19), + + BND_ICON_MESH_PLANE = BND_ICONID(0,18), + BND_ICON_MESH_CUBE = BND_ICONID(1,18), + BND_ICON_MESH_CIRCLE = BND_ICONID(2,18), + BND_ICON_MESH_UVSPHERE = BND_ICONID(3,18), + BND_ICON_MESH_ICOSPHERE = BND_ICONID(4,18), + BND_ICON_MESH_GRID = BND_ICONID(5,18), + BND_ICON_MESH_MONKEY = BND_ICONID(6,18), + BND_ICON_MESH_CYLINDER = BND_ICONID(7,18), + BND_ICON_MESH_TORUS = BND_ICONID(8,18), + BND_ICON_MESH_CONE = BND_ICONID(9,18), + BND_ICON_LAMP_POINT = BND_ICONID(12,18), + BND_ICON_LAMP_SUN = BND_ICONID(13,18), + BND_ICON_LAMP_SPOT = BND_ICONID(14,18), + BND_ICON_LAMP_HEMI = BND_ICONID(15,18), + BND_ICON_LAMP_AREA = BND_ICONID(16,18), + BND_ICON_META_EMPTY = BND_ICONID(19,18), + BND_ICON_META_PLANE = BND_ICONID(20,18), + BND_ICON_META_CUBE = BND_ICONID(21,18), + BND_ICON_META_BALL = BND_ICONID(22,18), + BND_ICON_META_ELLIPSOID = BND_ICONID(23,18), + BND_ICON_META_CAPSULE = BND_ICONID(24,18), + + BND_ICON_SURFACE_NCURVE = BND_ICONID(0,17), + BND_ICON_SURFACE_NCIRCLE = BND_ICONID(1,17), + BND_ICON_SURFACE_NSURFACE = BND_ICONID(2,17), + BND_ICON_SURFACE_NCYLINDER = BND_ICONID(3,17), + BND_ICON_SURFACE_NSPHERE = BND_ICONID(4,17), + BND_ICON_SURFACE_NTORUS = BND_ICONID(5,17), + BND_ICON_CURVE_BEZCURVE = BND_ICONID(9,17), + BND_ICON_CURVE_BEZCIRCLE = BND_ICONID(10,17), + BND_ICON_CURVE_NCURVE = BND_ICONID(11,17), + BND_ICON_CURVE_NCIRCLE = BND_ICONID(12,17), + BND_ICON_CURVE_PATH = BND_ICONID(13,17), + BND_ICON_COLOR_RED = BND_ICONID(19,17), + BND_ICON_COLOR_GREEN = BND_ICONID(20,17), + BND_ICON_COLOR_BLUE = BND_ICONID(21,17), + + BND_ICON_FORCE_FORCE = BND_ICONID(0,16), + BND_ICON_FORCE_WIND = BND_ICONID(1,16), + BND_ICON_FORCE_VORTEX = BND_ICONID(2,16), + BND_ICON_FORCE_MAGNETIC = BND_ICONID(3,16), + BND_ICON_FORCE_HARMONIC = BND_ICONID(4,16), + BND_ICON_FORCE_CHARGE = BND_ICONID(5,16), + BND_ICON_FORCE_LENNARDJONES = BND_ICONID(6,16), + BND_ICON_FORCE_TEXTURE = BND_ICONID(7,16), + BND_ICON_FORCE_CURVE = BND_ICONID(8,16), + BND_ICON_FORCE_BOID = BND_ICONID(9,16), + BND_ICON_FORCE_TURBULENCE = BND_ICONID(10,16), + BND_ICON_FORCE_DRAG = BND_ICONID(11,16), + BND_ICON_FORCE_SMOKEFLOW = BND_ICONID(12,16), + + BND_ICON_MODIFIER = BND_ICONID(0,12), + BND_ICON_MOD_WAVE = BND_ICONID(1,12), + BND_ICON_MOD_BUILD = BND_ICONID(2,12), + BND_ICON_MOD_DECIM = BND_ICONID(3,12), + BND_ICON_MOD_MIRROR = BND_ICONID(4,12), + BND_ICON_MOD_SOFT = BND_ICONID(5,12), + BND_ICON_MOD_SUBSURF = BND_ICONID(6,12), + BND_ICON_HOOK = BND_ICONID(7,12), + BND_ICON_MOD_PHYSICS = BND_ICONID(8,12), + BND_ICON_MOD_PARTICLES = BND_ICONID(9,12), + BND_ICON_MOD_BOOLEAN = BND_ICONID(10,12), + BND_ICON_MOD_EDGESPLIT = BND_ICONID(11,12), + BND_ICON_MOD_ARRAY = BND_ICONID(12,12), + BND_ICON_MOD_UVPROJECT = BND_ICONID(13,12), + BND_ICON_MOD_DISPLACE = BND_ICONID(14,12), + BND_ICON_MOD_CURVE = BND_ICONID(15,12), + BND_ICON_MOD_LATTICE = BND_ICONID(16,12), + BND_ICON_CONSTRAINT_DATA = BND_ICONID(17,12), + BND_ICON_MOD_ARMATURE = BND_ICONID(18,12), + BND_ICON_MOD_SHRINKWRAP = BND_ICONID(19,12), + BND_ICON_MOD_CAST = BND_ICONID(20,12), + BND_ICON_MOD_MESHDEFORM = BND_ICONID(21,12), + BND_ICON_MOD_BEVEL = BND_ICONID(22,12), + BND_ICON_MOD_SMOOTH = BND_ICONID(23,12), + BND_ICON_MOD_SIMPLEDEFORM = BND_ICONID(24,12), + BND_ICON_MOD_MASK = BND_ICONID(25,12), + + BND_ICON_MOD_CLOTH = BND_ICONID(0,11), + BND_ICON_MOD_EXPLODE = BND_ICONID(1,11), + BND_ICON_MOD_FLUIDSIM = BND_ICONID(2,11), + BND_ICON_MOD_MULTIRES = BND_ICONID(3,11), + BND_ICON_MOD_SMOKE = BND_ICONID(4,11), + BND_ICON_MOD_SOLIDIFY = BND_ICONID(5,11), + BND_ICON_MOD_SCREW = BND_ICONID(6,11), + BND_ICON_MOD_VERTEX_WEIGHT = BND_ICONID(7,11), + BND_ICON_MOD_DYNAMICPAINT = BND_ICONID(8,11), + BND_ICON_MOD_REMESH = BND_ICONID(9,11), + BND_ICON_MOD_OCEAN = BND_ICONID(10,11), + BND_ICON_MOD_WARP = BND_ICONID(11,11), + BND_ICON_MOD_SKIN = BND_ICONID(12,11), + BND_ICON_MOD_TRIANGULATE = BND_ICONID(13,11), + BND_ICON_MOD_WIREFRAME = BND_ICONID(14,11), + + BND_ICON_REC = BND_ICONID(0,10), + BND_ICON_PLAY = BND_ICONID(1,10), + BND_ICON_FF = BND_ICONID(2,10), + BND_ICON_REW = BND_ICONID(3,10), + BND_ICON_PAUSE = BND_ICONID(4,10), + BND_ICON_PREV_KEYFRAME = BND_ICONID(5,10), + BND_ICON_NEXT_KEYFRAME = BND_ICONID(6,10), + BND_ICON_PLAY_AUDIO = BND_ICONID(7,10), + BND_ICON_PLAY_REVERSE = BND_ICONID(8,10), + BND_ICON_PREVIEW_RANGE = BND_ICONID(9,10), + BND_ICON_ACTION_TWEAK = BND_ICONID(10,10), + BND_ICON_PMARKER_ACT = BND_ICONID(11,10), + BND_ICON_PMARKER_SEL = BND_ICONID(12,10), + BND_ICON_PMARKER = BND_ICONID(13,10), + BND_ICON_MARKER_HLT = BND_ICONID(14,10), + BND_ICON_MARKER = BND_ICONID(15,10), + BND_ICON_SPACE2 = BND_ICONID(16,10), + BND_ICON_SPACE3 = BND_ICONID(17,10), + BND_ICON_KEYINGSET = BND_ICONID(18,10), + BND_ICON_KEY_DEHLT = BND_ICONID(19,10), + BND_ICON_KEY_HLT = BND_ICONID(20,10), + BND_ICON_MUTE_IPO_OFF = BND_ICONID(21,10), + BND_ICON_MUTE_IPO_ON = BND_ICONID(22,10), + BND_ICON_VISIBLE_IPO_OFF = BND_ICONID(23,10), + BND_ICON_VISIBLE_IPO_ON = BND_ICONID(24,10), + BND_ICON_DRIVER = BND_ICONID(25,10), + + BND_ICON_SOLO_OFF = BND_ICONID(0,9), + BND_ICON_SOLO_ON = BND_ICONID(1,9), + BND_ICON_FRAME_PREV = BND_ICONID(2,9), + BND_ICON_FRAME_NEXT = BND_ICONID(3,9), + BND_ICON_NLA_PUSHDOWN = BND_ICONID(4,9), + BND_ICON_IPO_CONSTANT = BND_ICONID(5,9), + BND_ICON_IPO_LINEAR = BND_ICONID(6,9), + BND_ICON_IPO_BEZIER = BND_ICONID(7,9), + BND_ICON_IPO_SINE = BND_ICONID(8,9), + BND_ICON_IPO_QUAD = BND_ICONID(9,9), + BND_ICON_IPO_CUBIC = BND_ICONID(10,9), + BND_ICON_IPO_QUART = BND_ICONID(11,9), + BND_ICON_IPO_QUINT = BND_ICONID(12,9), + BND_ICON_IPO_EXPO = BND_ICONID(13,9), + BND_ICON_IPO_CIRC = BND_ICONID(14,9), + BND_ICON_IPO_BOUNCE = BND_ICONID(15,9), + BND_ICON_IPO_ELASTIC = BND_ICONID(16,9), + BND_ICON_IPO_BACK = BND_ICONID(17,9), + BND_ICON_IPO_EASE_IN = BND_ICONID(18,9), + BND_ICON_IPO_EASE_OUT = BND_ICONID(19,9), + BND_ICON_IPO_EASE_IN_OUT = BND_ICONID(20,9), + + BND_ICON_VERTEXSEL = BND_ICONID(0,8), + BND_ICON_EDGESEL = BND_ICONID(1,8), + BND_ICON_FACESEL = BND_ICONID(2,8), + BND_ICON_LOOPSEL = BND_ICONID(3,8), + BND_ICON_ROTATE = BND_ICONID(5,8), + BND_ICON_CURSOR = BND_ICONID(6,8), + BND_ICON_ROTATECOLLECTION = BND_ICONID(7,8), + BND_ICON_ROTATECENTER = BND_ICONID(8,8), + BND_ICON_ROTACTIVE = BND_ICONID(9,8), + BND_ICON_ALIGN = BND_ICONID(10,8), + BND_ICON_SMOOTHCURVE = BND_ICONID(12,8), + BND_ICON_SPHERECURVE = BND_ICONID(13,8), + BND_ICON_ROOTCURVE = BND_ICONID(14,8), + BND_ICON_SHARPCURVE = BND_ICONID(15,8), + BND_ICON_LINCURVE = BND_ICONID(16,8), + BND_ICON_NOCURVE = BND_ICONID(17,8), + BND_ICON_RNDCURVE = BND_ICONID(18,8), + BND_ICON_PROP_OFF = BND_ICONID(19,8), + BND_ICON_PROP_ON = BND_ICONID(20,8), + BND_ICON_PROP_CON = BND_ICONID(21,8), + BND_ICON_SCULPT_DYNTOPO = BND_ICONID(22,8), + BND_ICON_PARTICLE_POINT = BND_ICONID(23,8), + BND_ICON_PARTICLE_TIP = BND_ICONID(24,8), + BND_ICON_PARTICLE_PATH = BND_ICONID(25,8), + + BND_ICON_MAN_TRANS = BND_ICONID(0,7), + BND_ICON_MAN_ROT = BND_ICONID(1,7), + BND_ICON_MAN_SCALE = BND_ICONID(2,7), + BND_ICON_MANIPUL = BND_ICONID(3,7), + BND_ICON_SNAP_OFF = BND_ICONID(4,7), + BND_ICON_SNAP_ON = BND_ICONID(5,7), + BND_ICON_SNAP_NORMAL = BND_ICONID(6,7), + BND_ICON_SNAP_INCREMENT = BND_ICONID(7,7), + BND_ICON_SNAP_VERTEX = BND_ICONID(8,7), + BND_ICON_SNAP_EDGE = BND_ICONID(9,7), + BND_ICON_SNAP_FACE = BND_ICONID(10,7), + BND_ICON_SNAP_VOLUME = BND_ICONID(11,7), + BND_ICON_STICKY_UVS_LOC = BND_ICONID(13,7), + BND_ICON_STICKY_UVS_DISABLE = BND_ICONID(14,7), + BND_ICON_STICKY_UVS_VERT = BND_ICONID(15,7), + BND_ICON_CLIPUV_DEHLT = BND_ICONID(16,7), + BND_ICON_CLIPUV_HLT = BND_ICONID(17,7), + BND_ICON_SNAP_PEEL_OBJECT = BND_ICONID(18,7), + BND_ICON_GRID = BND_ICONID(19,7), + + BND_ICON_PASTEDOWN = BND_ICONID(0,6), + BND_ICON_COPYDOWN = BND_ICONID(1,6), + BND_ICON_PASTEFLIPUP = BND_ICONID(2,6), + BND_ICON_PASTEFLIPDOWN = BND_ICONID(3,6), + BND_ICON_SNAP_SURFACE = BND_ICONID(8,6), + BND_ICON_AUTOMERGE_ON = BND_ICONID(9,6), + BND_ICON_AUTOMERGE_OFF = BND_ICONID(10,6), + BND_ICON_RETOPO = BND_ICONID(11,6), + BND_ICON_UV_VERTEXSEL = BND_ICONID(12,6), + BND_ICON_UV_EDGESEL = BND_ICONID(13,6), + BND_ICON_UV_FACESEL = BND_ICONID(14,6), + BND_ICON_UV_ISLANDSEL = BND_ICONID(15,6), + BND_ICON_UV_SYNC_SELECT = BND_ICONID(16,6), + + BND_ICON_BBOX = BND_ICONID(0,5), + BND_ICON_WIRE = BND_ICONID(1,5), + BND_ICON_SOLID = BND_ICONID(2,5), + BND_ICON_SMOOTH = BND_ICONID(3,5), + BND_ICON_POTATO = BND_ICONID(4,5), + BND_ICON_ORTHO = BND_ICONID(6,5), + BND_ICON_LOCKVIEW_OFF = BND_ICONID(9,5), + BND_ICON_LOCKVIEW_ON = BND_ICONID(10,5), + BND_ICON_AXIS_SIDE = BND_ICONID(12,5), + BND_ICON_AXIS_FRONT = BND_ICONID(13,5), + BND_ICON_AXIS_TOP = BND_ICONID(14,5), + BND_ICON_NDOF_DOM = BND_ICONID(15,5), + BND_ICON_NDOF_TURN = BND_ICONID(16,5), + BND_ICON_NDOF_FLY = BND_ICONID(17,5), + BND_ICON_NDOF_TRANS = BND_ICONID(18,5), + BND_ICON_LAYER_USED = BND_ICONID(19,5), + BND_ICON_LAYER_ACTIVE = BND_ICONID(20,5), + + BND_ICON_SORTALPHA = BND_ICONID(0,3), + BND_ICON_SORTBYEXT = BND_ICONID(1,3), + BND_ICON_SORTTIME = BND_ICONID(2,3), + BND_ICON_SORTSIZE = BND_ICONID(3,3), + BND_ICON_LONGDISPLAY = BND_ICONID(4,3), + BND_ICON_SHORTDISPLAY = BND_ICONID(5,3), + BND_ICON_GHOST = BND_ICONID(6,3), + BND_ICON_IMGDISPLAY = BND_ICONID(7,3), + BND_ICON_SAVE_AS = BND_ICONID(8,3), + BND_ICON_SAVE_COPY = BND_ICONID(9,3), + BND_ICON_BOOKMARKS = BND_ICONID(10,3), + BND_ICON_FONTPREVIEW = BND_ICONID(11,3), + BND_ICON_FILTER = BND_ICONID(12,3), + BND_ICON_NEWFOLDER = BND_ICONID(13,3), + BND_ICON_OPEN_RECENT = BND_ICONID(14,3), + BND_ICON_FILE_PARENT = BND_ICONID(15,3), + BND_ICON_FILE_REFRESH = BND_ICONID(16,3), + BND_ICON_FILE_FOLDER = BND_ICONID(17,3), + BND_ICON_FILE_BLANK = BND_ICONID(18,3), + BND_ICON_FILE_BLEND = BND_ICONID(19,3), + BND_ICON_FILE_IMAGE = BND_ICONID(20,3), + BND_ICON_FILE_MOVIE = BND_ICONID(21,3), + BND_ICON_FILE_SCRIPT = BND_ICONID(22,3), + BND_ICON_FILE_SOUND = BND_ICONID(23,3), + BND_ICON_FILE_FONT = BND_ICONID(24,3), + BND_ICON_FILE_TEXT = BND_ICONID(25,3), + + BND_ICON_RECOVER_AUTO = BND_ICONID(0,2), + BND_ICON_SAVE_PREFS = BND_ICONID(1,2), + BND_ICON_LINK_BLEND = BND_ICONID(2,2), + BND_ICON_APPEND_BLEND = BND_ICONID(3,2), + BND_ICON_IMPORT = BND_ICONID(4,2), + BND_ICON_EXPORT = BND_ICONID(5,2), + BND_ICON_EXTERNAL_DATA = BND_ICONID(6,2), + BND_ICON_LOAD_FACTORY = BND_ICONID(7,2), + BND_ICON_LOOP_BACK = BND_ICONID(13,2), + BND_ICON_LOOP_FORWARDS = BND_ICONID(14,2), + BND_ICON_BACK = BND_ICONID(15,2), + BND_ICON_FORWARD = BND_ICONID(16,2), + BND_ICON_FILE_BACKUP = BND_ICONID(24,2), + BND_ICON_DISK_DRIVE = BND_ICONID(25,2), + + BND_ICON_MATPLANE = BND_ICONID(0,1), + BND_ICON_MATSPHERE = BND_ICONID(1,1), + BND_ICON_MATCUBE = BND_ICONID(2,1), + BND_ICON_MONKEY = BND_ICONID(3,1), + BND_ICON_HAIR = BND_ICONID(4,1), + BND_ICON_ALIASED = BND_ICONID(5,1), + BND_ICON_ANTIALIASED = BND_ICONID(6,1), + BND_ICON_MAT_SPHERE_SKY = BND_ICONID(7,1), + BND_ICON_WORDWRAP_OFF = BND_ICONID(12,1), + BND_ICON_WORDWRAP_ON = BND_ICONID(13,1), + BND_ICON_SYNTAX_OFF = BND_ICONID(14,1), + BND_ICON_SYNTAX_ON = BND_ICONID(15,1), + BND_ICON_LINENUMBERS_OFF = BND_ICONID(16,1), + BND_ICON_LINENUMBERS_ON = BND_ICONID(17,1), + BND_ICON_SCRIPTPLUGINS = BND_ICONID(18,1), + + BND_ICON_SEQ_SEQUENCER = BND_ICONID(0,0), + BND_ICON_SEQ_PREVIEW = BND_ICONID(1,0), + BND_ICON_SEQ_LUMA_WAVEFORM = BND_ICONID(2,0), + BND_ICON_SEQ_CHROMA_SCOPE = BND_ICONID(3,0), + BND_ICON_SEQ_HISTOGRAM = BND_ICONID(4,0), + BND_ICON_SEQ_SPLITVIEW = BND_ICONID(5,0), + BND_ICON_IMAGE_RGB = BND_ICONID(9,0), + BND_ICON_IMAGE_RGB_ALPHA = BND_ICONID(10,0), + BND_ICON_IMAGE_ALPHA = BND_ICONID(11,0), + BND_ICON_IMAGE_ZDEPTH = BND_ICONID(12,0), + BND_ICON_IMAGEFILE = BND_ICONID(13,0), +} BNDicon; + +//////////////////////////////////////////////////////////////////////////////// + +// set the current theme all widgets will be drawn with. +// the default Blender 2.6 theme is set by default. +BND_EXPORT void bndSetTheme(BNDtheme theme); + +// Returns the currently set theme +BND_EXPORT 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 +BND_EXPORT 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/ +BND_EXPORT 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 +BND_EXPORT 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 +BND_EXPORT 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 +BND_EXPORT void bndRadioButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label); + + +// Calculate the corresponding text position for given coordinates px/py +// in a text field. +// See bndTextField for more info. +BND_EXPORT int bndTextFieldTextPosition(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, const char *text, int px, int py); + +// 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 +BND_EXPORT 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 +BND_EXPORT 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 +BND_EXPORT void bndChoiceButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, BNDwidgetState state, + int iconid, const char *label); + +// Draw a color 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. +// widget looks best when height is BND_WIDGET_HEIGHT +BND_EXPORT void bndColorButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, NVGcolor color); + +// 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 +BND_EXPORT 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 +BND_EXPORT 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 +BND_EXPORT 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. +BND_EXPORT 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 +BND_EXPORT 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 +BND_EXPORT 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) +BND_EXPORT void bndTooltipBackground(NVGcontext *ctx, float x, float y, float w, float h); + +// Draw a node port at the given position filled with the given color +BND_EXPORT void bndNodePort(NVGcontext *ctx, float x, float y, BNDwidgetState state, + NVGcolor color); + +// Draw a node wire originating at (x0,y0) and floating to (x1,y1), with +// a colored gradient based on the states state0 and state1: +// BND_DEFAULT: default wire color +// BND_HOVER: selected wire color +// BND_ACTIVE: dragged wire color +BND_EXPORT void bndNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1, + BNDwidgetState state0, BNDwidgetState state1); + +// Draw a node wire originating at (x0,y0) and floating to (x1,y1), with +// a colored gradient based on the two colors color0 and color1 +BND_EXPORT void bndColoredNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1, + NVGcolor color0, NVGcolor color1); + +// Draw a node background with its upper left origin at (x,y) and size of (w,h) +// where titleColor provides the base color for the title bar +BND_EXPORT void bndNodeBackground(NVGcontext *ctx, float x, float y, float w, float h, + BNDwidgetState state, int iconid, const char *label, NVGcolor titleColor); + +// Draw a window with the upper right and lower left splitter widgets into +// the rectangle at origin (x,y) and size (w, h) +BND_EXPORT void bndSplitterWidgets(NVGcontext *ctx, float x, float y, float w, float h); + +// Draw the join area overlay stencil into the rectangle +// at origin (x,y) and size (w,h) +// vertical is 0 or 1 and designates the arrow orientation, +// mirror is 0 or 1 and flips the arrow side +BND_EXPORT void bndJoinAreaOverlay(NVGcontext *ctx, float x, float y, float w, float h, + int vertical, int mirror); + +//////////////////////////////////////////////////////////////////////////////// + +// Estimator Functions +// ------------------- +// Use these functions to estimate sizes for widgets with your NVGcontext. + +// returns the ideal width for a label with given icon and text +BND_EXPORT float bndLabelWidth(NVGcontext *ctx, int iconid, const char *label); + +// returns the height for a label with given icon, text and width; this +// function is primarily useful in conjunction with multiline labels and textboxes +BND_EXPORT float bndLabelHeight(NVGcontext *ctx, int iconid, const char *label, + float width); + +//////////////////////////////////////////////////////////////////////////////// + +// 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 +BND_EXPORT NVGcolor bndTransparent(NVGcolor color); + +// offset a color by a given integer delta in the range -100 to 100 +BND_EXPORT 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. +BND_EXPORT 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. +BND_EXPORT 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. +BND_EXPORT 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 +BND_EXPORT 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. +BND_EXPORT 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 +BND_EXPORT void bndBackground(NVGcontext *ctx, float x, float y, float w, float h); + +// Draw a beveled border at position (x,y) with size (w,h) shaded with +// lighter and darker versions of backgroundColor +BND_EXPORT void bndBevel(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. +BND_EXPORT 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. +BND_EXPORT 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. +BND_EXPORT 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. +BND_EXPORT 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 +BND_EXPORT 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. +BND_EXPORT 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 and an optional label with +// given alignment (BNDtextAlignment), fontsize and color within a node title bar +// if iconid is >= 0, an icon will be drawn +// if label is not NULL, it will be drawn with the specified alignment, fontsize +// and color. +BND_EXPORT void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, NVGcolor shadowColor, int align, + float fontsize, const char *label); + +// Calculate the corresponding text position for given coordinates px/py +// in an iconLabel. +// See bndIconLabelCaret for more info. +BND_EXPORT int bndIconLabelTextPosition(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, float fontsize, const char *label, int px, int py); + +// 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 +BND_EXPORT 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. +BND_EXPORT 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. +BND_EXPORT 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 +BND_EXPORT void bndUpDownArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color); + +// Draw a node down-arrow with its tip at (x,y) and size s +BND_EXPORT void bndNodeArrowDown(NVGcontext *ctx, float x, float y, float s, NVGcolor color); + +// return the color of a node wire based on state +// BND_HOVER indicates selected state, +// BND_ACTIVE indicates dragged state +BND_EXPORT NVGcolor bndNodeWireColor(const BNDnodeTheme *theme, BNDwidgetState state); + +#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 + #pragma warning (disable: 4244) + #pragma warning (disable: 4305) + #ifdef __cplusplus + #define BND_INLINE inline + #else + #define BND_INLINE + #endif + +#include + +static float bnd_fminf ( float a, float b ) +{ + return _isnan(a) ? b : ( _isnan(b) ? a : ((a < b) ? a : b)); +} + +static float bnd_fmaxf ( float a, float b ) +{ + return _isnan(a) ? b : ( _isnan(b) ? a : ((a > b) ? a : b)); +} + +static double bnd_fmin ( double a, double b ) +{ + return _isnan(a) ? b : ( _isnan(b) ? a : ((a < b) ? a : b)); +} + +static double bnd_fmax ( double a, double b ) +{ + return _isnan(a) ? b : ( _isnan(b) ? a : ((a > b) ? a : b)); +} + +#else + #define BND_INLINE inline + #define bnd_fminf(a, b) fminf(a, b) + #define bnd_fmaxf(a, b) fmaxf(a, b) + #define bnd_fmin(a, b) fmin(a, b) + #define bnd_fmax(a, b) fmax(a, b) +#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 panels +#define BND_BEVEL_SHADE 30 +// shade intensity of beveled insets +#define BND_INSET_BEVEL_SHADE 30 +// shade intensity of hovered inner boxes +#define BND_HOVER_SHADE 15 +// shade intensity of splitter bevels +#define BND_SPLITTER_SHADE 100 + +// 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 + +// max rows for position testing +#define BND_MAX_ROWS 32 + +// text distance from bottom +#define BND_TEXT_PAD_DOWN 7 + +// stroke width of wire outline +#define BND_NODE_WIRE_OUTLINE_WIDTH 4 +// stroke width of wire +#define BND_NODE_WIRE_WIDTH 2 +// radius of node box +#define BND_NODE_RADIUS 8 +// feather of node title text +#define BND_NODE_TITLE_FEATHER 1 +// size of node title arrow +#define BND_NODE_ARROW_SIZE 9 + +//////////////////////////////////////////////////////////////////////////////// + +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 + }, + // nodeTheme + { + {{{ 0.945,0.345,0,1 }}}, // nodeSelectedColor + {{{ 0,0,0,1 }}}, // wiresColor + {{{ 0.498,0.439,0.439,1 }}}, // textSelectedColor + {{{ 1,0.667,0.251,1 }}}, // activeNodeColor + {{{ 1,1,1,1 }}}, // wireSelectColor + {{{ 0.608,0.608,0.608,0.627 }}}, // nodeBackdropColor + 5, // noodleCurving + }, +}; + +//////////////////////////////////////////////////////////////////////////////// + +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); +} + +int bndTextFieldTextPosition(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, const char *text, int px, int py) { + return bndIconLabelTextPosition(ctx, x, y, w, h, + iconid, BND_LABEL_FONT_SIZE, text, px, py); +} + +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 bndColorButton(NVGcontext *ctx, + float x, float y, float w, float h, int flags, NVGcolor color) { + float cr[4]; + bndSelectCorners(cr, BND_TOOL_RADIUS, flags); + bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]); + bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], color, color); + bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], + bndTransparent(bnd_theme.toolTheme.outlineColor)); +} + +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 bndNodePort(NVGcontext *ctx, float x, float y, BNDwidgetState state, + NVGcolor color) { + nvgBeginPath(ctx); + nvgCircle(ctx, x, y, BND_NODE_PORT_RADIUS); + nvgStrokeColor(ctx,bnd_theme.nodeTheme.wiresColor); + nvgStrokeWidth(ctx,1.0f); + nvgStroke(ctx); + nvgFillColor(ctx,(state != BND_DEFAULT)? + bndOffsetColor(color, BND_HOVER_SHADE):color); + nvgFill(ctx); +} + +void bndColoredNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1, + NVGcolor color0, NVGcolor color1) { + float length = bnd_fmaxf(fabsf(x1 - x0),fabsf(y1 - y0)); + float delta = length*(float)bnd_theme.nodeTheme.noodleCurving/10.0f; + + nvgBeginPath(ctx); + nvgMoveTo(ctx, x0, y0); + nvgBezierTo(ctx, + x0 + delta, y0, + x1 - delta, y1, + x1, y1); + NVGcolor colorw = bnd_theme.nodeTheme.wiresColor; + colorw.a = (color0.a= 0) { + w += BND_ICON_SHEET_RES; + } + if (label && (bnd_font >= 0)) { + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, BND_LABEL_FONT_SIZE); + w += nvgTextBounds(ctx, 1, 1, label, NULL, NULL); + } + return w; +} + +float bndLabelHeight(NVGcontext *ctx, int iconid, const char *label, float width) { + int h = BND_WIDGET_HEIGHT; + width -= BND_TEXT_RADIUS*2; + if (iconid >= 0) { + width -= BND_ICON_SHEET_RES; + } + if (label && (bnd_font >= 0)) { + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, BND_LABEL_FONT_SIZE); + float bounds[4]; + nvgTextBoxBounds(ctx, 1, 1, width, label, NULL, bounds); + int bh = (int)(bounds[3] - bounds[1]) + BND_TEXT_PAD_DOWN; + if (bh > h) + h = bh; + } + return h; +} + +//////////////////////////////////////////////////////////////////////////////// + +void bndRoundedBox(NVGcontext *ctx, float x, float y, float w, float h, + float cr0, float cr1, float cr2, float cr3) { + float d; + + w = bnd_fmaxf(0, w); + h = bnd_fmaxf(0, h); + d = bnd_fminf(w, h); + + nvgMoveTo(ctx, x,y+h*0.5f); + nvgArcTo(ctx, x,y, x+w,y, bnd_fminf(cr0, d/2)); + nvgArcTo(ctx, x+w,y, x+w,y+h, bnd_fminf(cr1, d/2)); + nvgArcTo(ctx, x+w,y+h, x,y+h, bnd_fminf(cr2, d/2)); + nvgArcTo(ctx, x,y+h, x,y, bnd_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 bndBevel(NVGcontext *ctx, float x, float y, float w, float h) { + nvgStrokeWidth(ctx, 1); + + x += 0.5f; + y += 0.5f; + w -= 1; + h -= 1; + + nvgBeginPath(ctx); + nvgMoveTo(ctx, x, y+h); + nvgLineTo(ctx, x+w, y+h); + nvgLineTo(ctx, x+w, y); + nvgStrokeColor(ctx, bndTransparent( + bndOffsetColor(bnd_theme.backgroundColor, -BND_BEVEL_SHADE))); + nvgStroke(ctx); + + nvgBeginPath(ctx); + nvgMoveTo(ctx, x, y+h); + nvgLineTo(ctx, x, y); + nvgLineTo(ctx, x+w, y); + nvgStrokeColor(ctx, bndTransparent( + bndOffsetColor(bnd_theme.backgroundColor, BND_BEVEL_SHADE))); + nvgStroke(ctx); +} + +void bndBevelInset(NVGcontext *ctx, float x, float y, float w, float h, + float cr2, float cr3) { + float d; + + y -= 0.5f; + d = bnd_fminf(w, h); + cr2 = bnd_fminf(cr2, d/2); + cr3 = bnd_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_INSET_BEVEL_SHADE); + + nvgStrokeWidth(ctx, 1); + nvgStrokePaint(ctx, + nvgLinearGradient(ctx, + x,y+h-bnd_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,1)); + 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,bnd_fmaxf(0,cr0-1), + bnd_fmaxf(0,cr1-1),bnd_fmaxf(0,cr2-1),bnd_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 += BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN; + 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+BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN, + w-BND_PAD_RIGHT-pleft,label, NULL); + } + } else if (iconid >= 0) { + bndIcon(ctx,x+2,y+2,iconid); + } +} + +void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, NVGcolor color, NVGcolor shadowColor, + int align, float fontsize, const char *label) { + if (label && (bnd_font >= 0)) { + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, fontsize); + nvgBeginPath(ctx); + nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE); + nvgFillColor(ctx, shadowColor); + nvgFontBlur(ctx, BND_NODE_TITLE_FEATHER); + nvgTextBox(ctx,x+1,y+h+3-BND_TEXT_PAD_DOWN, + w,label, NULL); + nvgFillColor(ctx, color); + nvgFontBlur(ctx, 0); + nvgTextBox(ctx,x,y+h+2-BND_TEXT_PAD_DOWN, + w,label, NULL); + } + if (iconid >= 0) { + bndIcon(ctx,x+w-BND_ICON_SHEET_RES,y+3,iconid); + } +} + +int bndIconLabelTextPosition(NVGcontext *ctx, float x, float y, float w, float h, + int iconid, float fontsize, const char *label, int px, int py) { + float bounds[4]; + float pleft = BND_TEXT_RADIUS; + if (!label) return -1; + if (iconid >= 0) + pleft += BND_ICON_SHEET_RES; + + if (bnd_font < 0) return -1; + + x += pleft; + y += BND_WIDGET_HEIGHT - BND_TEXT_PAD_DOWN; + + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, fontsize); + nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE); + + w -= BND_TEXT_RADIUS + pleft; + + float asc, desc, lh; + static NVGtextRow rows[BND_MAX_ROWS]; + int nrows = nvgTextBreakLines( + ctx, label, NULL, w, rows, BND_MAX_ROWS); + if (nrows == 0) return 0; + nvgTextBoxBounds(ctx, x, y, w, label, NULL, bounds); + nvgTextMetrics(ctx, &asc, &desc, &lh); + + // calculate vertical position + int row = bnd_clamp((int)((float)(py - bounds[1]) / lh), 0, nrows - 1); + // search horizontal position + static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; + int nglyphs = nvgTextGlyphPositions( + ctx, x, y, rows[row].start, rows[row].end + 1, glyphs, BND_MAX_GLYPHS); + int col, p = 0; + for (col = 0; col < nglyphs && glyphs[col].x < px; ++col) + p = glyphs[col].str - label; + // see if we should move one character further + if (col > 0 && col < nglyphs && glyphs[col].x - px < px - glyphs[col - 1].x) + p = glyphs[col].str - label; + return p; +} + +static void bndCaretPosition(NVGcontext *ctx, float x, float y, + float desc, float lineHeight, const char *caret, NVGtextRow *rows,int nrows, + int *cr, float *cx, float *cy) { + static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; + int i,r,nglyphs; + for (r=0; r < nrows && rows[r].end < caret; ++r); + *cr = r; + *cx = x; + *cy = y-lineHeight-desc + r*lineHeight; + if (nrows == 0) return; + *cx = rows[r].minx; + nglyphs = nvgTextGlyphPositions( + ctx, x, y, rows[r].start, rows[r].end+1, glyphs, BND_MAX_GLYPHS); + for (i=0; i < nglyphs; ++i) { + *cx=glyphs[i].x; + if (glyphs[i].str == caret) break; + } +} + +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 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+=BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN; + + nvgFontFaceId(ctx, bnd_font); + nvgFontSize(ctx, fontsize); + nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE); + + w -= BND_TEXT_RADIUS+pleft; + + if (cend >= cbegin) { + int c0r,c1r; + float c0x,c0y,c1x,c1y; + float desc,lh; + static NVGtextRow rows[BND_MAX_ROWS]; + int nrows = nvgTextBreakLines( + ctx, label, label+cend+1, w, rows, BND_MAX_ROWS); + nvgTextMetrics(ctx, NULL, &desc, &lh); + + bndCaretPosition(ctx, x, y, desc, lh, label+cbegin, + rows, nrows, &c0r, &c0x, &c0y); + bndCaretPosition(ctx, x, y, desc, lh, label+cend, + rows, nrows, &c1r, &c1x, &c1y); + + nvgBeginPath(ctx); + if (cbegin == cend) { + nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761)); + nvgRect(ctx, c0x-1, c0y, 2, lh+1); + } else { + nvgFillColor(ctx, caretcolor); + if (c0r == c1r) { + nvgRect(ctx, c0x-1, c0y, c1x-c0x+1, lh+1); + } else { + int blk=c1r-c0r-1; + nvgRect(ctx, c0x-1, c0y, x+w-c0x+1, lh+1); + nvgRect(ctx, x, c1y, c1x-x+1, lh+1); + + if (blk) + nvgRect(ctx, x, c0y+lh, w, blk*lh+1); + } + } + nvgFill(ctx); + } + + nvgBeginPath(ctx); + nvgFillColor(ctx, color); + nvgTextBox(ctx,x,y,w,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 bndNodeArrowDown(NVGcontext *ctx, float x, float y, float s, NVGcolor color) { + float w; + nvgBeginPath(ctx); + w = 1.0f*s; + nvgMoveTo(ctx,x,y); + nvgLineTo(ctx,x+0.5*w,y-s); + nvgLineTo(ctx,x-0.5*w,y-s); + 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 = bnd_fmaxf(size*(*h), (*w)+1); + *y = (*y) + ((*h)-hs)*offset; + *h = hs; + } else { + float ws = bnd_fmaxf(size*(*w), (*h)-1); + *x = (*x) + ((*w)-ws)*offset; + *w = ws; + } +} + +NVGcolor bndNodeWireColor(const BNDnodeTheme *theme, BNDwidgetState state) { + switch(state) { + default: + case BND_DEFAULT: return nvgRGBf(0.5f,0.5f,0.5f); + case BND_HOVER: return theme->wireSelectColor; + case BND_ACTIVE: return theme->activeNodeColor; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef BND_INLINE +#undef BND_INLINE +#endif + +#endif // BLENDISH_IMPLEMENTATION diff --git a/dgl/src/oui-blendish/oui.h b/dgl/src/oui-blendish/oui.h new file mode 100644 index 00000000..b6807f9b --- /dev/null +++ b/dgl/src/oui-blendish/oui.h @@ -0,0 +1,2025 @@ +/* +OUI - A minimal semi-immediate GUI handling & layouting library + +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 _OUI_H_ +#define _OUI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Revision 4 (2014-12-17) + +OUI (short for "Open UI", spoken like the french "oui" for "yes") is a +platform agnostic single-header C library for layouting GUI elements and +handling related user input. Together with a set of widget drawing and logic +routines it can be used to build complex user interfaces. + +OUI is a semi-immediate GUI. Widget declarations are persistent for the duration +of the setup and evaluation, but do not need to be kept around longer than one +frame. + +OUI has no widget types; instead, it provides only one kind of element, "Items", +which can be tailored to the application by the user and expanded with custom +buffers and event handlers to behave as containers, buttons, sliders, radio +buttons, and so on. + +OUI also does not draw anything; Instead it provides a set of functions to +iterate and query the layouted items in order to allow client code to render +each widget with its current state using a preferred graphics library. + +See example.cpp in the repository for a full usage example. + +A basic setup for OUI usage in C looks like this: +================================================= + +// a header for each widget +typedef struct Data { + int type; + UIhandler handler; +} Data; + +/// global event dispatch +void ui_handler(int item, UIevent event) { + Data *data = (Data *)uiGetHandle(item); + if (data && data->handler) { + data->handler(item, event); + } +} + +void app_main(...) { + UIcontext *context = uiCreateContext(4096, 1<<20); + uiMakeCurrent(context); + uiSetHandler(ui_handler); + + while (app_running()) { + // update position of mouse cursor; the ui can also be updated + // from received events. + uiSetCursor(app_get_mouse_x(), app_get_mouse_y()); + + // update button state + for (int i = 0; i < 3; ++i) + uiSetButton(i, app_get_button_state(i)); + + // you can also send keys and scroll events; see example.cpp for more + + // -------------- + // this section does not have to be regenerated on frame; a good + // policy is to invalidate it on events, as this usually alters + // structure and layout. + + // begin new UI declarations + uiBeginLayout(); + + // - UI setup code goes here - + app_setup_ui(); + + // layout UI + uiEndLayout(); + + // -------------- + + // draw UI, starting with the first item, index 0 + app_draw_ui(render_context,0); + + // update states and fire handlers + uiProcess(get_time_ms()); + } + + uiDestroyContext(context); +} + +Here's an example setup for a checkbox control: +=============================================== + +typedef struct CheckBoxData { + Data head; + const char *label; + bool *checked; +} CheckBoxData; + +// called when the item is clicked (see checkbox()) +void app_checkbox_handler(int item, UIevent event) { + // retrieve custom data (see checkbox()) + CheckBoxData *data = (CheckBoxData *)uiGetHandle(item); + + switch(event) { + default: break; + case UI_BUTTON0_DOWN: { + // toggle value + *data->checked = !(*data->checked); + } break; + } +} + +// creates a checkbox control for a pointer to a boolean +int checkbox(const char *label, bool *checked) { + + // create new ui item + int item = uiItem(); + + // set minimum size of wiget; horizontal size is dynamic, vertical is fixed + uiSetSize(item, 0, APP_WIDGET_HEIGHT); + + // store some custom data with the checkbox that we use for rendering + // and value changes. + CheckBoxData *data = (CheckBoxData *)uiAllocHandle(item, sizeof(CheckBoxData)); + + // assign a custom typeid to the data so the renderer knows how to + // render this control, and our event handler + data->head.type = APP_WIDGET_CHECKBOX; + data->head.handler = app_checkbox_handler; + data->label = label; + data->checked = checked; + + // set to fire as soon as the left button is + // pressed; UI_BUTTON0_HOT_UP is also a popular alternative. + uiSetEvents(item, UI_BUTTON0_DOWN); + + return item; +} + +A simple recursive drawing routine can look like this: +====================================================== + +void app_draw_ui(AppRenderContext *ctx, int item) { + // retrieve custom data and cast it to Data; we assume the first member + // of every widget data item to be a Data field. + Data *head = (Data *)uiGetHandle(item); + + // if a handle is set, this is a specialized widget + if (head) { + // get the widgets absolute rectangle + UIrect rect = uiGetRect(item); + + switch(head->type) { + default: break; + case APP_WIDGET_LABEL: { + // ... + } break; + case APP_WIDGET_BUTTON: { + // ... + } break; + case APP_WIDGET_CHECKBOX: { + // cast to the full data type + CheckBoxData *data = (CheckBoxData*)head; + + // get the widgets current state + int state = uiGetState(item); + + // if the value is set, the state is always active + if (*data->checked) + state = UI_ACTIVE; + + // draw the checkbox + app_draw_checkbox(ctx, rect, state, data->label); + } break; + } + } + + // iterate through all children and draw + int kid = uiFirstChild(item); + while (kid != -1) { + app_draw_ui(ctx, kid); + kid = uiNextSibling(kid); + } +} + +Layouting items works like this: +================================ + +void layout_window(int w, int h) { + // create root item; the first item always has index 0 + int parent = uiItem(); + // assign fixed size + uiSetSize(parent, w, h); + + // create column box and use as new parent + parent = uiInsert(parent, uiItem()); + // configure as column + uiSetBox(parent, UI_COLUMN); + // span horizontally, attach to top + uiSetLayout(parent, UI_HFILL | UI_TOP); + + // add a label - we're assuming custom control functions to exist + int item = uiInsert(parent, label("Hello World")); + // set a fixed height for the label + uiSetSize(item, 0, APP_WIDGET_HEIGHT); + // span the label horizontally + uiSetLayout(item, UI_HFILL); + + static bool checked = false; + + // add a checkbox to the same parent as item; this is faster than + // calling uiInsert on the same parent repeatedly. + item = uiAppend(item, checkbox("Checked:", &checked)); + // set a fixed height for the checkbox + uiSetSize(item, 0, APP_WIDGET_HEIGHT); + // span the checkbox in the same way as the label + uiSetLayout(item, UI_HFILL); +} + + + + */ + +// you can override this from the outside to pick +// the export level you need +#ifndef OUI_EXPORT +#define OUI_EXPORT +#endif + +// limits + +enum { + // maximum size in bytes of a single data buffer passed to uiAllocData(). + UI_MAX_DATASIZE = 4096, + // maximum depth of nested containers + UI_MAX_DEPTH = 64, + // maximum number of buffered input events + UI_MAX_INPUT_EVENTS = 64, + // consecutive click threshold in ms + UI_CLICK_THRESHOLD = 250, +}; + +typedef unsigned int UIuint; + +// opaque UI context +typedef struct UIcontext UIcontext; + +// item states as returned by uiGetState() + +typedef enum UIitemState { + // the item is inactive + UI_COLD = 0, + // the item is inactive, but the cursor is hovering over this item + UI_HOT = 1, + // the item is toggled, activated, focused (depends on item kind) + UI_ACTIVE = 2, + // the item is unresponsive + UI_FROZEN = 3, +} UIitemState; + +// container flags to pass to uiSetBox() +typedef enum UIboxFlags { + // flex-direction (bit 0+1) + + // left to right + UI_ROW = 0x002, + // top to bottom + UI_COLUMN = 0x003, + + // model (bit 1) + + // free layout + UI_LAYOUT = 0x000, + // flex model + UI_FLEX = 0x002, + + // flex-wrap (bit 2) + + // single-line + UI_NOWRAP = 0x000, + // multi-line, wrap left to right + UI_WRAP = 0x004, + + + // justify-content (start, end, center, space-between) + // at start of row/column + UI_START = 0x008, + // at center of row/column + UI_MIDDLE = 0x000, + // at end of row/column + UI_END = 0x010, + // insert spacing to stretch across whole row/column + UI_JUSTIFY = 0x018, + + // align-items + // can be implemented by putting a flex container in a layout container, + // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc. + // FILL is equivalent to stretch/grow + + // align-content (start, end, center, stretch) + // can be implemented by putting a flex container in a layout container, + // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc. + // FILL is equivalent to stretch; space-between is not supported. +} UIboxFlags; + +// child layout flags to pass to uiSetLayout() +typedef enum UIlayoutFlags { + // attachments (bit 5-8) + // fully valid when parent uses UI_LAYOUT model + // partially valid when in UI_FLEX model + + // anchor to left item or left side of parent + UI_LEFT = 0x020, + // anchor to top item or top side of parent + UI_TOP = 0x040, + // anchor to right item or right side of parent + UI_RIGHT = 0x080, + // anchor to bottom item or bottom side of parent + UI_DOWN = 0x100, + // anchor to both left and right item or parent borders + UI_HFILL = 0x0a0, + // anchor to both top and bottom item or parent borders + UI_VFILL = 0x140, + // center horizontally, with left margin as offset + UI_HCENTER = 0x000, + // center vertically, with top margin as offset + UI_VCENTER = 0x000, + // center in both directions, with left/top margin as offset + UI_CENTER = 0x000, + // anchor to all four directions + UI_FILL = 0x1e0, + // when wrapping, put this element on a new line + // wrapping layout code auto-inserts UI_BREAK flags, + // drawing routines can read them with uiGetLayout() + UI_BREAK = 0x200 +} UIlayoutFlags; + +// event flags +typedef enum UIevent { + // on button 0 down + UI_BUTTON0_DOWN = 0x0400, + // on button 0 up + // when this event has a handler, uiGetState() will return UI_ACTIVE as + // long as button 0 is down. + UI_BUTTON0_UP = 0x0800, + // on button 0 up while item is hovered + // when this event has a handler, uiGetState() will return UI_ACTIVE + // when the cursor is hovering the items rectangle; this is the + // behavior expected for buttons. + UI_BUTTON0_HOT_UP = 0x1000, + // item is being captured (button 0 constantly pressed); + // when this event has a handler, uiGetState() will return UI_ACTIVE as + // long as button 0 is down. + UI_BUTTON0_CAPTURE = 0x2000, + // on button 2 down (right mouse button, usually triggers context menu) + UI_BUTTON2_DOWN = 0x4000, + // item has received a scrollwheel event + // the accumulated wheel offset can be queried with uiGetScroll() + UI_SCROLL = 0x8000, + // item is focused and has received a key-down event + // the respective key can be queried using uiGetKey() and uiGetModifier() + UI_KEY_DOWN = 0x10000, + // item is focused and has received a key-up event + // the respective key can be queried using uiGetKey() and uiGetModifier() + UI_KEY_UP = 0x20000, + // item is focused and has received a character event + // the respective character can be queried using uiGetKey() + UI_CHAR = 0x40000, +} UIevent; + +enum { + // these bits, starting at bit 24, can be safely assigned by the + // application, e.g. as item types, other event types, drop targets, etc. + // they can be set and queried using uiSetFlags() and uiGetFlags() + UI_USERMASK = 0xff000000, + + // a special mask passed to uiFindItem() + UI_ANY = 0xffffffff, +}; + +// handler callback; event is one of UI_EVENT_* +typedef void (*UIhandler)(UIcontext* ui_context, int item, UIevent event); + +// for cursor positions, mainly +typedef struct UIvec2 { + union { + int v[2]; + struct { int x, y; }; + }; +} UIvec2; + +// layout rectangle +typedef struct UIrect { + union { + int v[4]; + struct { int x, y, w, h; }; + }; +} UIrect; + +// unless declared otherwise, all operations have the complexity O(1). + +// Context Management +// ------------------ + +// create a new UI context; call uiMakeCurrent() to make this context the +// current context. The context is managed by the client and must be released +// using uiDestroyContext() +// item_capacity is the maximum of number of items that can be declared. +// buffer_capacity is the maximum total size of bytes that can be allocated +// using uiAllocHandle(); you may pass 0 if you don't need to allocate +// handles. +// 4096 and (1<<20) are good starting values. +OUI_EXPORT UIcontext *uiCreateContext( + unsigned int item_capacity, + unsigned int buffer_capacity); + +// release the memory of an UI context created with uiCreateContext(); if the +// context is the current context, the current context will be set to NULL +OUI_EXPORT void uiDestroyContext(UIcontext *ctx); + +// User Data +OUI_EXPORT void uiSetContextHandle(UIcontext *ui_context, void *handle); +OUI_EXPORT void *uiGetContextHandle(UIcontext *ui_context); + +// Input Control +// ------------- + +// sets the current cursor position (usually belonging to a mouse) to the +// screen coordinates at (x,y) +OUI_EXPORT void uiSetCursor(UIcontext *ui_context, int x, int y); + +// returns the current cursor position in screen coordinates as set by +// uiSetCursor() +OUI_EXPORT UIvec2 uiGetCursor(UIcontext *ui_context); + +// returns the offset of the cursor relative to the last call to uiProcess() +OUI_EXPORT UIvec2 uiGetCursorDelta(UIcontext *ui_context); + +// returns the beginning point of a drag operation. +OUI_EXPORT UIvec2 uiGetCursorStart(UIcontext *ui_context); + +// returns the offset of the cursor relative to the beginning point of a drag +// operation. +OUI_EXPORT UIvec2 uiGetCursorStartDelta(UIcontext *ui_context); + +// sets a mouse or gamepad button as pressed/released +// button is in the range 0..63 and maps to an application defined input +// source. +// mod is an application defined set of flags for modifier keys +// enabled is 1 for pressed, 0 for released +OUI_EXPORT void uiSetButton(UIcontext *ui_context, unsigned int button, unsigned int mod, bool enabled); + +// returns the current state of an application dependent input button +// as set by uiSetButton(). +// the function returns 1 if the button has been set to pressed, 0 for released. +OUI_EXPORT int uiGetButton(UIcontext *ui_context, unsigned int button); + +// returns the number of chained clicks; 1 is a single click, +// 2 is a double click, etc. +OUI_EXPORT int uiGetClicks(UIcontext *ui_context); + +// sets a key as down/up; the key can be any application defined keycode +// mod is an application defined set of flags for modifier keys +// enabled is 1 for key down, 0 for key up +// all key events are being buffered until the next call to uiProcess() +OUI_EXPORT void uiSetKey(UIcontext *ui_context, unsigned int key, unsigned int mod, bool enabled); + +// sends a single character for text input; the character is usually in the +// unicode range, but can be application defined. +// all char events are being buffered until the next call to uiProcess() +OUI_EXPORT void uiSetChar(UIcontext *ui_context, unsigned int value); + +// accumulates scroll wheel offsets for the current frame +// all offsets are being accumulated until the next call to uiProcess() +OUI_EXPORT void uiSetScroll(UIcontext *ui_context, int x, int y); + +// returns the currently accumulated scroll wheel offsets for this frame +OUI_EXPORT UIvec2 uiGetScroll(UIcontext *ui_context); + + + + + +// Stages +// ------ + +// clear the item buffer; uiBeginLayout() should be called before the first +// UI declaration for this frame to avoid concatenation of the same UI multiple +// times. +// After the call, all previously declared item IDs are invalid, and all +// application dependent context data has been freed. +// uiBeginLayout() must be followed by uiEndLayout(). +OUI_EXPORT void uiBeginLayout(UIcontext *ui_context); + +// layout all added items starting from the root item 0. +// after calling uiEndLayout(), no further modifications to the item tree should +// be done until the next call to uiBeginLayout(). +// It is safe to immediately draw the items after a call to uiEndLayout(). +// this is an O(N) operation for N = number of declared items. +OUI_EXPORT void uiEndLayout(UIcontext *ui_context); + +// update the current hot item; this only needs to be called if items are kept +// for more than one frame and uiEndLayout() is not called +OUI_EXPORT void uiUpdateHotItem(UIcontext *ui_context); + +// update the internal state according to the current cursor position and +// button states, and call all registered handlers. +// timestamp is the time in milliseconds relative to the last call to uiProcess() +// and is used to estimate the threshold for double-clicks +// after calling uiProcess(), no further modifications to the item tree should +// be done until the next call to uiBeginLayout(). +// Items should be drawn before a call to uiProcess() +// this is an O(N) operation for N = number of declared items. +OUI_EXPORT void uiProcess(UIcontext *ui_context, int timestamp); + +// reset the currently stored hot/active etc. handles; this should be called when +// a re-declaration of the UI changes the item indices, to avoid state +// related glitches because item identities have changed. +OUI_EXPORT void uiClearState(UIcontext *ui_context); + +// UI Declaration +// -------------- + +// create a new UI item and return the new items ID. +OUI_EXPORT int uiItem(UIcontext *ui_context); + +// set an items state to frozen; the UI will not recurse into frozen items +// when searching for hot or active items; subsequently, frozen items and +// their child items will not cause mouse event notifications. +// The frozen state is not applied recursively; uiGetState() will report +// UI_COLD for child items. Upon encountering a frozen item, the drawing +// routine needs to handle rendering of child items appropriately. +// see example.cpp for a demonstration. +OUI_EXPORT void uiSetFrozen(UIcontext *ui_context, int item, bool enable); + +// set the application-dependent handle of an item. +// handle is an application defined 64-bit handle. If handle is NULL, the item +// will not be interactive. +OUI_EXPORT void uiSetHandle(UIcontext *ui_context, int item, void *handle); + +// allocate space for application-dependent context data and assign it +// as the handle to the item. +// The memory of the pointer is managed by the UI context and released +// upon the next call to uiBeginLayout() +OUI_EXPORT void *uiAllocHandle(UIcontext *ui_context, int item, unsigned int size); + +// set the global handler callback for interactive items. +// the handler will be called for each item whose event flags are set using +// uiSetEvents. +OUI_EXPORT void uiSetHandler(UIcontext *ui_context, UIhandler handler); + +// flags is a combination of UI_EVENT_* and designates for which events the +// handler should be called. +OUI_EXPORT void uiSetEvents(UIcontext *ui_context, int item, unsigned int flags); + +// flags is a user-defined set of flags defined by UI_USERMASK. +OUI_EXPORT void uiSetFlags(UIcontext *ui_context, int item, unsigned int flags); + +// assign an item to a container. +// an item ID of 0 refers to the root item. +// the function returns the child item ID +// if the container has already added items, the function searches +// for the last item and calls uiAppend() on it, which is an +// O(N) operation for N siblings. +// it is usually more efficient to call uiInsert() for the first child, +// then chain additional siblings using uiAppend(). +OUI_EXPORT int uiInsert(UIcontext *ui_context, int item, int child); + +// assign an item to the same container as another item +// sibling is inserted after item. +OUI_EXPORT int uiAppend(UIcontext *ui_context, int item, int sibling); + +// insert child into container item like uiInsert(), but prepend +// it to the first child item, effectively putting it in +// the background. +// it is efficient to call uiInsertBack() repeatedly +// in cases where drawing or layout order doesn't matter. +OUI_EXPORT int uiInsertBack(UIcontext *ui_context, int item, int child); + +// same as uiInsert() +OUI_EXPORT int uiInsertFront(UIcontext *ui_context, int item, int child); + +// set the size of the item; a size of 0 indicates the dimension to be +// dynamic; if the size is set, the item can not expand beyond that size. +OUI_EXPORT void uiSetSize(UIcontext *ui_context, int item, int w, int h); + +// set the anchoring behavior of the item to one or multiple UIlayoutFlags +OUI_EXPORT void uiSetLayout(UIcontext *ui_context, int item, unsigned int flags); + +// set the box model behavior of the item to one or multiple UIboxFlags +OUI_EXPORT void uiSetBox(UIcontext *ui_context, int item, unsigned int flags); + +// set the left, top, right and bottom margins of an item; when the item is +// anchored to the parent or another item, the margin controls the distance +// from the neighboring element. +OUI_EXPORT void uiSetMargins(UIcontext *ui_context, int item, short l, short t, short r, short b); + +// set item as recipient of all keyboard events; if item is -1, no item will +// be focused. +OUI_EXPORT void uiFocus(UIcontext *ui_context, int item); + +// Iteration +// --------- + +// returns the first child item of a container item. If the item is not +// a container or does not contain any items, -1 is returned. +// if item is 0, the first child item of the root item will be returned. +OUI_EXPORT int uiFirstChild(UIcontext *ui_context, int item); + +// returns an items next sibling in the list of the parent containers children. +// if item is 0 or the item is the last child item, -1 will be returned. +OUI_EXPORT int uiNextSibling(UIcontext *ui_context, int item); + +// Querying +// -------- + +// return the total number of allocated items +OUI_EXPORT int uiGetItemCount(UIcontext *ui_context); + +// return the total bytes that have been allocated by uiAllocHandle() +OUI_EXPORT unsigned int uiGetAllocSize(UIcontext *ui_context); + +// return the current state of the item. This state is only valid after +// a call to uiProcess(). +// The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE, UI_FROZEN. +OUI_EXPORT UIitemState uiGetState(UIcontext *ui_context, int item); + +// return the application-dependent handle of the item as passed to uiSetHandle() +// or uiAllocHandle(). +OUI_EXPORT void *uiGetHandle(UIcontext *ui_context, int item); + +// return the item that is currently under the cursor or -1 for none +OUI_EXPORT int uiGetHotItem(UIcontext *ui_context); + +// return the item that is currently focused or -1 for none +OUI_EXPORT int uiGetFocusedItem(UIcontext *ui_context); + +// returns the topmost item containing absolute location (x,y), starting with +// item as parent, using a set of flags and masks as filter: +// if both flags and mask are UI_ANY, the first topmost item is returned. +// if mask is UI_ANY, the first topmost item matching *any* of flags is returned. +// otherwise the first item matching (item.flags & flags) == mask is returned. +// you may combine box, layout, event and user flags. +// frozen items will always be ignored. +OUI_EXPORT int uiFindItem(UIcontext *ui_context, int item, int x, int y, + unsigned int flags, unsigned int mask); + +// return the handler callback as passed to uiSetHandler() +OUI_EXPORT UIhandler uiGetHandler(UIcontext *ui_context); +// return the event flags for an item as passed to uiSetEvents() +OUI_EXPORT unsigned int uiGetEvents(UIcontext *ui_context, int item); +// return the user-defined flags for an item as passed to uiSetFlags() +OUI_EXPORT unsigned int uiGetFlags(UIcontext *ui_context, int item); + +// when handling a KEY_DOWN/KEY_UP event: the key that triggered this event +OUI_EXPORT unsigned int uiGetKey(UIcontext *ui_context); +// when handling a keyboard or mouse event: the active modifier keys +OUI_EXPORT unsigned int uiGetModifier(UIcontext *ui_context); + +// returns the items layout rectangle in absolute coordinates. If +// uiGetRect() is called before uiEndLayout(), the values of the returned +// rectangle are undefined. +OUI_EXPORT UIrect uiGetRect(UIcontext *ui_context, int item); + +// returns 1 if an items absolute rectangle contains a given coordinate +// otherwise 0 +OUI_EXPORT int uiContains(UIcontext *ui_context, int item, int x, int y); + +// return the width of the item as set by uiSetSize() +OUI_EXPORT int uiGetWidth(UIcontext *ui_context, int item); +// return the height of the item as set by uiSetSize() +OUI_EXPORT int uiGetHeight(UIcontext *ui_context, int item); + +// return the anchoring behavior as set by uiSetLayout() +OUI_EXPORT unsigned int uiGetLayout(UIcontext *ui_context, int item); +// return the box model as set by uiSetBox() +OUI_EXPORT unsigned int uiGetBox(UIcontext *ui_context, int item); + +// return the left margin of the item as set with uiSetMargins() +OUI_EXPORT short uiGetMarginLeft(UIcontext *ui_context, int item); +// return the top margin of the item as set with uiSetMargins() +OUI_EXPORT short uiGetMarginTop(UIcontext *ui_context, int item); +// return the right margin of the item as set with uiSetMargins() +OUI_EXPORT short uiGetMarginRight(UIcontext *ui_context, int item); +// return the bottom margin of the item as set with uiSetMargins() +OUI_EXPORT short uiGetMarginDown(UIcontext *ui_context, int item); + +// when uiBeginLayout() is called, the most recently declared items are retained. +// when uiEndLayout() completes, it matches the old item hierarchy to the new one +// and attempts to map old items to new items as well as possible. +// when passed an item Id from the previous frame, uiRecoverItem() returns the +// items new assumed Id, or -1 if the item could not be mapped. +// it is valid to pass -1 as item. +OUI_EXPORT int uiRecoverItem(UIcontext *ui_context, int olditem); + +// in cases where it is important to recover old state over changes in +// the view, and the built-in remapping fails, the UI declaration can manually +// remap old items to new IDs in cases where e.g. the previous item ID has been +// temporarily saved; uiRemapItem() would then be called after creating the +// new item using uiItem(). +OUI_EXPORT void uiRemapItem(UIcontext *ui_context, int olditem, int newitem); + +// returns the number if items that have been allocated in the last frame +OUI_EXPORT int uiGetLastItemCount(UIcontext *ui_context); + +#ifdef __cplusplus +}; +#endif + +#endif // _OUI_H_ + +#ifdef OUI_IMPLEMENTATION + +#include + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #pragma warning (disable: 4244) + #pragma warning (disable: 4305) + + #ifdef __cplusplus + #define UI_INLINE inline + #else + #define UI_INLINE + #endif +#else + #ifdef __clang__ + #define UI_INLINE static inline + #else + #define UI_INLINE inline + #endif +#endif + +#define UI_MAX_KIND 16 + +#define UI_ANY_BUTTON0_INPUT (UI_BUTTON0_DOWN \ + |UI_BUTTON0_UP \ + |UI_BUTTON0_HOT_UP \ + |UI_BUTTON0_CAPTURE) + +#define UI_ANY_BUTTON2_INPUT (UI_BUTTON2_DOWN) + +#define UI_ANY_MOUSE_INPUT (UI_ANY_BUTTON0_INPUT \ + |UI_ANY_BUTTON2_INPUT) + +#define UI_ANY_KEY_INPUT (UI_KEY_DOWN \ + |UI_KEY_UP \ + |UI_CHAR) + +#define UI_ANY_INPUT (UI_ANY_MOUSE_INPUT \ + |UI_ANY_KEY_INPUT) + +enum { + // extra item flags + + // bit 0-2 + UI_ITEM_BOX_MODEL_MASK = 0x000007, + // bit 0-4 + UI_ITEM_BOX_MASK = 0x00001F, + // bit 5-8 + UI_ITEM_LAYOUT_MASK = 0x0003E0, + // bit 9-18 + UI_ITEM_EVENT_MASK = 0x07FC00, + // item is frozen (bit 19) + UI_ITEM_FROZEN = 0x080000, + // item handle is pointer to data (bit 20) + UI_ITEM_DATA = 0x100000, + // item has been inserted (bit 21) + UI_ITEM_INSERTED = 0x200000, + // horizontal size has been explicitly set (bit 22) + UI_ITEM_HFIXED = 0x400000, + // vertical size has been explicitly set (bit 23) + UI_ITEM_VFIXED = 0x800000, + // bit 22-23 + UI_ITEM_FIXED_MASK = 0xC00000, + + // which flag bits will be compared + UI_ITEM_COMPARE_MASK = UI_ITEM_BOX_MODEL_MASK + | (UI_ITEM_LAYOUT_MASK & ~UI_BREAK) + | UI_ITEM_EVENT_MASK + | UI_USERMASK, +}; + +typedef struct UIitem { + // data handle + void *handle; + + // about 27 bits worth of flags + unsigned int flags; + + // index of first kid + // if old item: index of equivalent new item + int firstkid; + // index of next sibling with same parent + int nextitem; + + // margin offsets, interpretation depends on flags + // after layouting, the first two components are absolute coordinates + short margins[4]; + // size + short size[2]; +} UIitem; + +typedef enum UIstate { + UI_STATE_IDLE = 0, + UI_STATE_CAPTURE, +} UIstate; + +typedef enum UIstage { + UI_STAGE_LAYOUT = 0, + UI_STAGE_POST_LAYOUT, + UI_STAGE_PROCESS, +} UIstage; + +typedef struct UIhandleEntry { + unsigned int key; + int item; +} UIhandleEntry; + +typedef struct UIinputEvent { + unsigned int key; + unsigned int mod; + UIevent event; +} UIinputEvent; + +struct UIcontext { + unsigned int item_capacity; + unsigned int buffer_capacity; + + // handler + UIhandler handler; + // User data + void *handle; + + // button state in this frame + unsigned long long buttons; + // button state in the previous frame + unsigned long long last_buttons; + + // where the cursor was at the beginning of the active state + UIvec2 start_cursor; + // where the cursor was last frame + UIvec2 last_cursor; + // where the cursor is currently + UIvec2 cursor; + // accumulated scroll wheel offsets + UIvec2 scroll; + + int active_item; + int focus_item; + int last_hot_item; + int last_click_item; + int hot_item; + + UIstate state; + UIstage stage; + unsigned int active_key; + unsigned int active_modifier; + unsigned int active_button_modifier; + int last_timestamp; + int last_click_timestamp; + int clicks; + + int count; + int last_count; + int eventcount; + unsigned int datasize; + + UIitem *items; + unsigned char *data; + UIitem *last_items; + int *item_map; + UIinputEvent events[UI_MAX_INPUT_EVENTS]; +}; + +UI_INLINE int ui_max(int a, int b) { + return (a>b)?a:b; +} + +UI_INLINE int ui_min(int a, int b) { + return (ab)?a:b; +} + +UI_INLINE float ui_minf(float a, float b) { + return (alast_count = ui_context->count; + ui_context->count = 0; + ui_context->datasize = 0; + ui_context->hot_item = -1; + // swap buffers + UIitem *items = ui_context->items; + ui_context->items = ui_context->last_items; + ui_context->last_items = items; + for (i = 0; i < ui_context->last_count; ++i) { + ui_context->item_map[i] = -1; + } +} + +static UIcontext *uiInitializeContext( + UIcontext *ctx, + unsigned int item_capacity, + unsigned int buffer_capacity) { + memset(ctx, 0, sizeof(UIcontext)); + ctx->item_capacity = item_capacity; + ctx->buffer_capacity = buffer_capacity; + ctx->stage = UI_STAGE_PROCESS; + ctx->items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); + ctx->last_items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); + ctx->item_map = (int *)malloc(sizeof(int) * item_capacity); + if (buffer_capacity) { + ctx->data = (unsigned char *)malloc(buffer_capacity); + } + return ctx; +} + +UIcontext *uiCreateContext( + unsigned int item_capacity, + unsigned int buffer_capacity) { + assert(item_capacity); + UIcontext *ctx = (UIcontext *)malloc(sizeof(UIcontext)); + uiInitializeContext(ctx, item_capacity, buffer_capacity); + uiClear(ctx); + uiClearState(ctx); + return ctx; +} + +void uiDestroyContext(UIcontext *ctx) { + free(ctx->items); + free(ctx->last_items); + free(ctx->item_map); + free(ctx->data); + free(ctx); +} + +void uiSetContextHandle(UIcontext *ui_context, void *handle) { + assert(ui_context); + ui_context->handle = handle; +} + +void *uiGetContextHandle(UIcontext *ui_context) { + assert(ui_context); + return ui_context->handle; +} + + +void uiSetButton(UIcontext *ui_context, unsigned int button, unsigned int mod, bool enabled) { + assert(ui_context); + unsigned long long mask = 1ull<buttons = (enabled)? + (ui_context->buttons | mask): + (ui_context->buttons & ~mask); + ui_context->active_button_modifier = mod; +} + +static void uiAddInputEvent(UIcontext *ui_context, UIinputEvent event) { + assert(ui_context); + if (ui_context->eventcount == UI_MAX_INPUT_EVENTS) return; + ui_context->events[ui_context->eventcount++] = event; +} + +static void uiClearInputEvents(UIcontext *ui_context) { + assert(ui_context); + ui_context->eventcount = 0; + ui_context->scroll.x = 0; + ui_context->scroll.y = 0; +} + +void uiSetKey(UIcontext *ui_context, unsigned int key, unsigned int mod, bool enabled) { + assert(ui_context); + UIinputEvent event = { key, mod, enabled?UI_KEY_DOWN:UI_KEY_UP }; + uiAddInputEvent(ui_context, event); +} + +void uiSetChar(UIcontext *ui_context, unsigned int value) { + assert(ui_context); + UIinputEvent event = { value, 0, UI_CHAR }; + uiAddInputEvent(ui_context, event); +} + +void uiSetScroll(UIcontext *ui_context, int x, int y) { + assert(ui_context); + ui_context->scroll.x += x; + ui_context->scroll.y += y; +} + +UIvec2 uiGetScroll(UIcontext *ui_context) { + assert(ui_context); + return ui_context->scroll; +} + +int uiGetLastButton(UIcontext *ui_context, unsigned int button) { + assert(ui_context); + return (ui_context->last_buttons & (1ull<buttons & (1ull<cursor.x = x; + ui_context->cursor.y = y; +} + +UIvec2 uiGetCursor(UIcontext *ui_context) { + assert(ui_context); + return ui_context->cursor; +} + +UIvec2 uiGetCursorStart(UIcontext *ui_context) { + assert(ui_context); + return ui_context->start_cursor; +} + +UIvec2 uiGetCursorDelta(UIcontext *ui_context) { + assert(ui_context); + UIvec2 result = {{{ + ui_context->cursor.x - ui_context->last_cursor.x, + ui_context->cursor.y - ui_context->last_cursor.y + }}}; + return result; +} + +UIvec2 uiGetCursorStartDelta(UIcontext *ui_context) { + assert(ui_context); + UIvec2 result = {{{ + ui_context->cursor.x - ui_context->start_cursor.x, + ui_context->cursor.y - ui_context->start_cursor.y + }}}; + return result; +} + +unsigned int uiGetKey(UIcontext *ui_context) { + assert(ui_context); + return ui_context->active_key; +} + +unsigned int uiGetModifier(UIcontext *ui_context) { + assert(ui_context); + return ui_context->active_modifier; +} + +int uiGetItemCount(UIcontext *ui_context) { + assert(ui_context); + return ui_context->count; +} + +int uiGetLastItemCount(UIcontext *ui_context) { + assert(ui_context); + return ui_context->last_count; +} + +unsigned int uiGetAllocSize(UIcontext *ui_context) { + assert(ui_context); + return ui_context->datasize; +} + +UIitem *uiItemPtr(UIcontext *ui_context, int item) { + assert(ui_context && (item >= 0) && (item < ui_context->count)); + return ui_context->items + item; +} + +UIitem *uiLastItemPtr(UIcontext *ui_context, int item) { + assert(ui_context && (item >= 0) && (item < ui_context->last_count)); + return ui_context->last_items + item; +} + +int uiGetHotItem(UIcontext *ui_context) { + assert(ui_context); + return ui_context->hot_item; +} + +void uiFocus(UIcontext *ui_context, int item) { + assert(ui_context && (item >= -1) && (item < ui_context->count)); + assert(ui_context->stage != UI_STAGE_LAYOUT); + ui_context->focus_item = item; +} + +static void uiValidateStateItems(UIcontext *ui_context) { + assert(ui_context); + ui_context->last_hot_item = uiRecoverItem(ui_context, ui_context->last_hot_item); + ui_context->active_item = uiRecoverItem(ui_context, ui_context->active_item); + ui_context->focus_item = uiRecoverItem(ui_context, ui_context->focus_item); + ui_context->last_click_item = uiRecoverItem(ui_context, ui_context->last_click_item); +} + +int uiGetFocusedItem(UIcontext *ui_context) { + assert(ui_context); + return ui_context->focus_item; +} + + +void uiBeginLayout(UIcontext *ui_context) { + assert(ui_context); + assert(ui_context->stage == UI_STAGE_PROCESS); // must run uiEndLayout(), uiProcess() first + uiClear(ui_context); + ui_context->stage = UI_STAGE_LAYOUT; +} + +void uiClearState(UIcontext *ui_context) { + assert(ui_context); + ui_context->last_hot_item = -1; + ui_context->active_item = -1; + ui_context->focus_item = -1; + ui_context->last_click_item = -1; +} + +int uiItem(UIcontext *ui_context) { + assert(ui_context); + assert(ui_context->stage == UI_STAGE_LAYOUT); // must run between uiBeginLayout() and uiEndLayout() + assert(ui_context->count < (int)ui_context->item_capacity); + int idx = ui_context->count++; + UIitem *item = uiItemPtr(ui_context, idx); + memset(item, 0, sizeof(UIitem)); + item->firstkid = -1; + item->nextitem = -1; + return idx; +} + +void uiNotifyItem(UIcontext *ui_context, int item, UIevent event) { + assert(ui_context); + if (!ui_context->handler) + return; + assert((event & UI_ITEM_EVENT_MASK) == event); + UIitem *pitem = uiItemPtr(ui_context, item); + if (pitem->flags & event) { + ui_context->handler(ui_context, item, event); + } +} + +UI_INLINE int uiLastChild(UIcontext *ui_context, int item) { + item = uiFirstChild(ui_context, item); + if (item < 0) + return -1; + while (true) { + int nextitem = uiNextSibling(ui_context, item); + if (nextitem < 0) + return item; + item = nextitem; + } +} + +int uiAppend(UIcontext *ui_context, int item, int sibling) { + assert(sibling > 0); + UIitem *pitem = uiItemPtr(ui_context, item); + UIitem *psibling = uiItemPtr(ui_context, sibling); + assert(!(psibling->flags & UI_ITEM_INSERTED)); + psibling->nextitem = pitem->nextitem; + psibling->flags |= UI_ITEM_INSERTED; + pitem->nextitem = sibling; + return sibling; +} + +int uiInsert(UIcontext *ui_context, int item, int child) { + assert(child > 0); + UIitem *pparent = uiItemPtr(ui_context, item); + UIitem *pchild = uiItemPtr(ui_context, child); + assert(!(pchild->flags & UI_ITEM_INSERTED)); + if (pparent->firstkid < 0) { + pparent->firstkid = child; + pchild->flags |= UI_ITEM_INSERTED; + } else { + uiAppend(ui_context, uiLastChild(ui_context, item), child); + } + return child; +} + +int uiInsertFront(UIcontext *ui_context, int item, int child) { + return uiInsert(ui_context, item, child); +} + +int uiInsertBack(UIcontext *ui_context, int item, int child) { + assert(child > 0); + UIitem *pparent = uiItemPtr(ui_context, item); + UIitem *pchild = uiItemPtr(ui_context, child); + assert(!(pchild->flags & UI_ITEM_INSERTED)); + pchild->nextitem = pparent->firstkid; + pparent->firstkid = child; + pchild->flags |= UI_ITEM_INSERTED; + return child; +} + +void uiSetFrozen(UIcontext *ui_context, int item, bool enable) { + UIitem *pitem = uiItemPtr(ui_context, item); + if (enable) + pitem->flags |= UI_ITEM_FROZEN; + else + pitem->flags &= ~UI_ITEM_FROZEN; +} + +void uiSetSize(UIcontext *ui_context, int item, int w, int h) { + UIitem *pitem = uiItemPtr(ui_context, item); + pitem->size[0] = w; + pitem->size[1] = h; + if (!w) + pitem->flags &= ~UI_ITEM_HFIXED; + else + pitem->flags |= UI_ITEM_HFIXED; + if (!h) + pitem->flags &= ~UI_ITEM_VFIXED; + else + pitem->flags |= UI_ITEM_VFIXED; +} + +int uiGetWidth(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->size[0]; +} + +int uiGetHeight(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->size[1]; +} + +void uiSetLayout(UIcontext *ui_context, int item, unsigned int flags) { + UIitem *pitem = uiItemPtr(ui_context, item); + assert((flags & UI_ITEM_LAYOUT_MASK) == (unsigned int)flags); + pitem->flags &= ~UI_ITEM_LAYOUT_MASK; + pitem->flags |= flags & UI_ITEM_LAYOUT_MASK; +} + +unsigned int uiGetLayout(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->flags & UI_ITEM_LAYOUT_MASK; +} + +void uiSetBox(UIcontext *ui_context, int item, unsigned int flags) { + UIitem *pitem = uiItemPtr(ui_context, item); + assert((flags & UI_ITEM_BOX_MASK) == (unsigned int)flags); + pitem->flags &= ~UI_ITEM_BOX_MASK; + pitem->flags |= flags & UI_ITEM_BOX_MASK; +} + +unsigned int uiGetBox(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->flags & UI_ITEM_BOX_MASK; +} + +void uiSetMargins(UIcontext *ui_context, int item, short l, short t, short r, short b) { + UIitem *pitem = uiItemPtr(ui_context, item); + pitem->margins[0] = l; + pitem->margins[1] = t; + pitem->margins[2] = r; + pitem->margins[3] = b; +} + +short uiGetMarginLeft(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->margins[0]; +} +short uiGetMarginTop(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->margins[1]; +} +short uiGetMarginRight(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->margins[2]; +} +short uiGetMarginDown(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->margins[3]; +} + +// compute bounding box of all items super-imposed +UI_INLINE void uiComputeImposedSize(UIcontext *ui_context, UIitem *pitem, int dim) { + int wdim = dim+2; + // largest size is required size + short need_size = 0; + int kid = pitem->firstkid; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + // width = start margin + calculated width + end margin + int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + need_size = ui_max(need_size, kidsize); + kid = uiNextSibling(ui_context, kid); + } + pitem->size[dim] = need_size; +} + +// compute bounding box of all items stacked +UI_INLINE void uiComputeStackedSize(UIcontext *ui_context, UIitem *pitem, int dim) { + int wdim = dim+2; + short need_size = 0; + int kid = pitem->firstkid; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + // width += start margin + calculated width + end margin + need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + kid = uiNextSibling(ui_context, kid); + } + pitem->size[dim] = need_size; +} + +// compute bounding box of all items stacked, repeating when breaking +UI_INLINE void uiComputeWrappedStackedSize(UIcontext *ui_context, UIitem *pitem, int dim) { + int wdim = dim+2; + + short need_size = 0; + short need_size2 = 0; + int kid = pitem->firstkid; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + // if next position moved back, we assume a new line + if (pkid->flags & UI_BREAK) { + need_size2 = ui_max(need_size2, need_size); + // newline + need_size = 0; + } + + // width = start margin + calculated width + end margin + need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + kid = uiNextSibling(ui_context, kid); + } + pitem->size[dim] = ui_max(need_size2, need_size); +} + +// compute bounding box of all items stacked + wrapped +UI_INLINE void uiComputeWrappedSize(UIcontext *ui_context, UIitem *pitem, int dim) { + int wdim = dim+2; + + short need_size = 0; + short need_size2 = 0; + int kid = pitem->firstkid; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + // if next position moved back, we assume a new line + if (pkid->flags & UI_BREAK) { + need_size2 += need_size; + // newline + need_size = 0; + } + + // width = start margin + calculated width + end margin + int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + need_size = ui_max(need_size, kidsize); + kid = uiNextSibling(ui_context, kid); + } + pitem->size[dim] = need_size2 + need_size; +} + +static void uiComputeSize(UIcontext *ui_context, int item, int dim) { + UIitem *pitem = uiItemPtr(ui_context, item); + + // children expand the size + int kid = pitem->firstkid; + while (kid >= 0) { + uiComputeSize(ui_context, kid, dim); + kid = uiNextSibling(ui_context, kid); + } + + if (pitem->size[dim]) + return; + switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) { + case UI_COLUMN|UI_WRAP: { + // flex model + if (dim) // direction + uiComputeStackedSize(ui_context, pitem, 1); + else + uiComputeImposedSize(ui_context, pitem, 0); + } break; + case UI_ROW|UI_WRAP: { + // flex model + if (!dim) // direction + uiComputeWrappedStackedSize(ui_context, pitem, 0); + else + uiComputeWrappedSize(ui_context, pitem, 1); + } break; + case UI_COLUMN: + case UI_ROW: { + // flex model + if ((pitem->flags & 1) == (unsigned int)dim) // direction + uiComputeStackedSize(ui_context, pitem, dim); + else + uiComputeImposedSize(ui_context, pitem, dim); + } break; + default: { + // layout model + uiComputeImposedSize(ui_context, pitem, dim); + } break; + } +} + +// stack all items according to their alignment +UI_INLINE void uiArrangeStacked(UIcontext *ui_context, UIitem *pitem, int dim, bool wrap) { + int wdim = dim+2; + + short space = pitem->size[dim]; + float max_x2 = (float)pitem->margins[dim] + (float)space; + + int start_kid = pitem->firstkid; + while (start_kid >= 0) { + short used = 0; + + int count = 0; // count of fillers + int squeezed_count = 0; // count of squeezable elements + int total = 0; + bool hardbreak = false; + // first pass: count items that need to be expanded, + // and the space that is used + int kid = start_kid; + int end_kid = -1; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; + int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim; + short extend = used; + if ((flags & UI_HFILL) == UI_HFILL) { // grow + count++; + extend += pkid->margins[dim] + pkid->margins[wdim]; + } else { + if ((fflags & UI_ITEM_HFIXED) != UI_ITEM_HFIXED) + squeezed_count++; + extend += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + } + // wrap on end of line or manual flag + if (wrap && (total && ((extend > space) || (pkid->flags & UI_BREAK)))) { + end_kid = kid; + hardbreak = ((pkid->flags & UI_BREAK) == UI_BREAK); + // add marker for subsequent queries + pkid->flags |= UI_BREAK; + break; + } else { + used = extend; + kid = uiNextSibling(ui_context, kid); + } + total++; + } + + int extra_space = space - used; + float filler = 0.0f; + float spacer = 0.0f; + float extra_margin = 0.0f; + float eater = 0.0f; + + if (extra_space > 0) { + if (count) { + filler = (float)extra_space / (float)count; + } else if (total) { + switch(pitem->flags & UI_JUSTIFY) { + default: { + extra_margin = extra_space / 2.0f; + } break; + case UI_JUSTIFY: { + // justify when not wrapping or not in last line, + // or not manually breaking + if (!wrap || ((end_kid != -1) && !hardbreak)) + spacer = (float)extra_space / (float)(total-1); + } break; + case UI_START: { + } break; + case UI_END: { + extra_margin = extra_space; + } break; + } + } + } else if (!wrap && (extra_space < 0)) { + eater = (float)extra_space / (float)squeezed_count; + } + + // distribute width among items + float x = (float)pitem->margins[dim]; + float x1; + // second pass: distribute and rescale + kid = start_kid; + while (kid != end_kid) { + short ix0,ix1; + UIitem *pkid = uiItemPtr(ui_context, kid); + int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; + int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim; + + x += (float)pkid->margins[dim] + extra_margin; + if ((flags & UI_HFILL) == UI_HFILL) { // grow + x1 = x+filler; + } else if ((fflags & UI_ITEM_HFIXED) == UI_ITEM_HFIXED) { + x1 = x+(float)pkid->size[dim]; + } else { + // squeeze + x1 = x+ui_maxf(0.0f,(float)pkid->size[dim]+eater); + } + ix0 = (short)x; + if (wrap) + ix1 = (short)ui_minf(max_x2-(float)pkid->margins[wdim], x1); + else + ix1 = (short)x1; + pkid->margins[dim] = ix0; + pkid->size[dim] = ix1-ix0; + x = x1 + (float)pkid->margins[wdim]; + + kid = uiNextSibling(ui_context, kid); + extra_margin = spacer; + } + + start_kid = end_kid; + } +} + +// superimpose all items according to their alignment +UI_INLINE void uiArrangeImposedRange(UIcontext *ui_context, UIitem *pitem, int dim, + int start_kid, int end_kid, short offset, short space) { + int wdim = dim+2; + + int kid = start_kid; + while (kid != end_kid) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; + + switch(flags & UI_HFILL) { + default: break; + case UI_HCENTER: { + pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim]; + } break; + case UI_RIGHT: { + pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim]; + } break; + case UI_HFILL: { + pkid->size[dim] = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]); + } break; + } + pkid->margins[dim] += offset; + + kid = uiNextSibling(ui_context, kid); + } +} + +UI_INLINE void uiArrangeImposed(UIcontext *ui_context, UIitem *pitem, int dim) { + uiArrangeImposedRange(ui_context, pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]); +} + +// superimpose all items according to their alignment, +// squeeze items that expand the available space +UI_INLINE void uiArrangeImposedSqueezedRange(UIcontext *ui_context, UIitem *pitem, int dim, + int start_kid, int end_kid, short offset, short space) { + int wdim = dim+2; + + int kid = start_kid; + while (kid != end_kid) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; + + short min_size = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]); + switch(flags & UI_HFILL) { + default: { + pkid->size[dim] = ui_min(pkid->size[dim], min_size); + } break; + case UI_HCENTER: { + pkid->size[dim] = ui_min(pkid->size[dim], min_size); + pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim]; + } break; + case UI_RIGHT: { + pkid->size[dim] = ui_min(pkid->size[dim], min_size); + pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim]; + } break; + case UI_HFILL: { + pkid->size[dim] = min_size; + } break; + } + pkid->margins[dim] += offset; + + kid = uiNextSibling(ui_context, kid); + } +} + +UI_INLINE void uiArrangeImposedSqueezed(UIcontext *ui_context, UIitem *pitem, int dim) { + uiArrangeImposedSqueezedRange(ui_context, pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]); +} + +// superimpose all items according to their alignment +UI_INLINE short uiArrangeWrappedImposedSqueezed(UIcontext *ui_context, UIitem *pitem, int dim) { + int wdim = dim+2; + + short offset = pitem->margins[dim]; + + short need_size = 0; + int kid = pitem->firstkid; + int start_kid = kid; + while (kid >= 0) { + UIitem *pkid = uiItemPtr(ui_context, kid); + + if (pkid->flags & UI_BREAK) { + uiArrangeImposedSqueezedRange(ui_context, pitem, dim, start_kid, kid, offset, need_size); + offset += need_size; + start_kid = kid; + // newline + need_size = 0; + } + + // width = start margin + calculated width + end margin + int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; + need_size = ui_max(need_size, kidsize); + kid = uiNextSibling(ui_context, kid); + } + + uiArrangeImposedSqueezedRange(ui_context, pitem, dim, start_kid, -1, offset, need_size); + offset += need_size; + return offset; +} + +static void uiArrange(UIcontext *ui_context, int item, int dim) { + UIitem *pitem = uiItemPtr(ui_context, item); + + switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) { + case UI_COLUMN|UI_WRAP: { + // flex model, wrapping + if (dim) { // direction + uiArrangeStacked(ui_context, pitem, 1, true); + // this retroactive resize will not effect parent widths + short offset = uiArrangeWrappedImposedSqueezed(ui_context, pitem, 0); + pitem->size[0] = offset - pitem->margins[0]; + } + } break; + case UI_ROW|UI_WRAP: { + // flex model, wrapping + if (!dim) { // direction + uiArrangeStacked(ui_context, pitem, 0, true); + } else { + uiArrangeWrappedImposedSqueezed(ui_context, pitem, 1); + } + } break; + case UI_COLUMN: + case UI_ROW: { + // flex model + if ((pitem->flags & 1) == (unsigned int)dim) // direction + uiArrangeStacked(ui_context, pitem, dim, false); + else + uiArrangeImposedSqueezed(ui_context, pitem, dim); + } break; + default: { + // layout model + uiArrangeImposed(ui_context, pitem, dim); + } break; + } + + int kid = uiFirstChild(ui_context, item); + while (kid >= 0) { + uiArrange(ui_context, kid, dim); + kid = uiNextSibling(ui_context, kid); + } +} + +UI_INLINE bool uiCompareItems(UIcontext *ui_context, UIitem *item1, UIitem *item2) { + return ((item1->flags & UI_ITEM_COMPARE_MASK) == (item2->flags & UI_ITEM_COMPARE_MASK)); + +} + +static bool uiMapItems(UIcontext *ui_context, int item1, int item2) { + UIitem *pitem1 = uiLastItemPtr(ui_context, item1); + if (item2 == -1) { + return false; + } + + UIitem *pitem2 = uiItemPtr(ui_context, item2); + if (!uiCompareItems(ui_context, pitem1, pitem2)) { + return false; + } + + int count = 0; + int failed = 0; + int kid1 = pitem1->firstkid; + int kid2 = pitem2->firstkid; + while (kid1 != -1) { + UIitem *pkid1 = uiLastItemPtr(ui_context, kid1); + count++; + if (!uiMapItems(ui_context, kid1, kid2)) { + failed = count; + break; + } + kid1 = pkid1->nextitem; + if (kid2 != -1) { + kid2 = uiItemPtr(ui_context, kid2)->nextitem; + } + } + + if (count && (failed == 1)) { + return false; + } + + ui_context->item_map[item1] = item2; + return true; +} + +int uiRecoverItem(UIcontext *ui_context, int olditem) { + assert(ui_context); + assert((olditem >= -1) && (olditem < ui_context->last_count)); + if (olditem == -1) return -1; + return ui_context->item_map[olditem]; +} + +void uiRemapItem(UIcontext *ui_context, int olditem, int newitem) { + assert(ui_context); + assert((olditem >= 0) && (olditem < ui_context->last_count)); + assert((newitem >= -1) && (newitem < ui_context->count)); + ui_context->item_map[olditem] = newitem; +} + +void uiEndLayout(UIcontext *ui_context) { + assert(ui_context); + assert(ui_context->stage == UI_STAGE_LAYOUT); // must run uiBeginLayout() first + + if (ui_context->count) { + uiComputeSize(ui_context, 0, 0); + uiArrange(ui_context, 0, 0); + uiComputeSize(ui_context, 0, 1); + uiArrange(ui_context, 0, 1); + + if (ui_context->last_count) { + // map old item id to new item id + uiMapItems(ui_context, 0, 0); + } + } + + uiValidateStateItems(ui_context); + if (ui_context->count) { + // drawing routines may require this to be set already + uiUpdateHotItem(ui_context); + } + + ui_context->stage = UI_STAGE_POST_LAYOUT; +} + +UIrect uiGetRect(UIcontext *ui_context, int item) { + UIitem *pitem = uiItemPtr(ui_context, item); + UIrect rc = {{{ + pitem->margins[0], pitem->margins[1], + pitem->size[0], pitem->size[1] + }}}; + return rc; +} + +int uiFirstChild(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->firstkid; +} + +int uiNextSibling(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->nextitem; +} + +void *uiAllocHandle(UIcontext *ui_context, int item, unsigned int size) { + assert((size > 0) && (size < UI_MAX_DATASIZE)); + UIitem *pitem = uiItemPtr(ui_context, item); + assert(pitem->handle == NULL); + assert((ui_context->datasize+size) <= ui_context->buffer_capacity); + pitem->handle = ui_context->data + ui_context->datasize; + pitem->flags |= UI_ITEM_DATA; + ui_context->datasize += size; + return pitem->handle; +} + +void uiSetHandle(UIcontext *ui_context, int item, void *handle) { + UIitem *pitem = uiItemPtr(ui_context, item); + assert(pitem->handle == NULL); + pitem->handle = handle; +} + +void *uiGetHandle(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->handle; +} + +void uiSetHandler(UIcontext *ui_context, UIhandler handler) { + assert(ui_context); + ui_context->handler = handler; +} + +UIhandler uiGetHandler(UIcontext *ui_context) { + assert(ui_context); + return ui_context->handler; +} + +void uiSetEvents(UIcontext *ui_context, int item, unsigned int flags) { + UIitem *pitem = uiItemPtr(ui_context, item); + pitem->flags &= ~UI_ITEM_EVENT_MASK; + pitem->flags |= flags & UI_ITEM_EVENT_MASK; +} + +unsigned int uiGetEvents(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->flags & UI_ITEM_EVENT_MASK; +} + +void uiSetFlags(UIcontext *ui_context, int item, unsigned int flags) { + UIitem *pitem = uiItemPtr(ui_context, item); + pitem->flags &= ~UI_USERMASK; + pitem->flags |= flags & UI_USERMASK; +} + +unsigned int uiGetFlags(UIcontext *ui_context, int item) { + return uiItemPtr(ui_context, item)->flags & UI_USERMASK; +} + +int uiContains(UIcontext *ui_context, int item, int x, int y) { + UIrect rect = uiGetRect(ui_context, item); + x -= rect.x; + y -= rect.y; + if ((x>=0) + && (y>=0) + && (xflags & UI_ITEM_FROZEN) return -1; + if (uiContains(ui_context, item, x, y)) { + int best_hit = -1; + int kid = uiFirstChild(ui_context, item); + while (kid >= 0) { + int hit = uiFindItem(ui_context, kid, x, y, flags, mask); + if (hit >= 0) { + best_hit = hit; + } + kid = uiNextSibling(ui_context, kid); + } + if (best_hit >= 0) { + return best_hit; + } + if (((mask == UI_ANY) && ((flags == UI_ANY) + || (pitem->flags & flags))) + || ((pitem->flags & flags) == mask)) { + return item; + } + } + return -1; +} + +void uiUpdateHotItem(UIcontext *ui_context) { + assert(ui_context); + if (!ui_context->count) return; + ui_context->hot_item = uiFindItem(ui_context, 0, + ui_context->cursor.x, ui_context->cursor.y, + UI_ANY_MOUSE_INPUT, UI_ANY); +} + +int uiGetClicks(UIcontext *ui_context) { + return ui_context->clicks; +} + +void uiProcess(UIcontext *ui_context, int timestamp) { + assert(ui_context); + + assert(ui_context->stage != UI_STAGE_LAYOUT); // must run uiBeginLayout(), uiEndLayout() first + + if (ui_context->stage == UI_STAGE_PROCESS) { + uiUpdateHotItem(ui_context); + } + ui_context->stage = UI_STAGE_PROCESS; + + if (!ui_context->count) { + uiClearInputEvents(ui_context); + return; + } + + int hot_item = ui_context->last_hot_item; + int active_item = ui_context->active_item; + int focus_item = ui_context->focus_item; + + // send all keyboard events + if (focus_item >= 0) { + int i; + for (i = 0; i < ui_context->eventcount; ++i) { + ui_context->active_key = ui_context->events[i].key; + ui_context->active_modifier = ui_context->events[i].mod; + uiNotifyItem(ui_context, focus_item, ui_context->events[i].event); + } + } else { + ui_context->focus_item = -1; + } + if (ui_context->scroll.x || ui_context->scroll.y) { + int scroll_item = uiFindItem(ui_context, 0, + ui_context->cursor.x, ui_context->cursor.y, + UI_SCROLL, UI_ANY); + if (scroll_item >= 0) { + uiNotifyItem(ui_context, scroll_item, UI_SCROLL); + } + } + + uiClearInputEvents(ui_context); + + int hot = ui_context->hot_item; + + switch(ui_context->state) { + default: + case UI_STATE_IDLE: { + ui_context->start_cursor = ui_context->cursor; + if (uiGetButton(ui_context, 0)) { + hot_item = -1; + active_item = hot; + + if (active_item != focus_item) { + focus_item = -1; + ui_context->focus_item = -1; + } + + if (active_item >= 0) { + if ( + ((timestamp - ui_context->last_click_timestamp) > UI_CLICK_THRESHOLD) + || (ui_context->last_click_item != active_item)) { + ui_context->clicks = 0; + } + ui_context->clicks++; + + ui_context->last_click_timestamp = timestamp; + ui_context->last_click_item = active_item; + ui_context->active_modifier = ui_context->active_button_modifier; + uiNotifyItem(ui_context, active_item, UI_BUTTON0_DOWN); + } + ui_context->state = UI_STATE_CAPTURE; + } else if (uiGetButton(ui_context, 2) && !uiGetLastButton(ui_context, 2)) { + hot_item = -1; + hot = uiFindItem(ui_context, 0, ui_context->cursor.x, ui_context->cursor.y, + UI_BUTTON2_DOWN, UI_ANY); + if (hot >= 0) { + ui_context->active_modifier = ui_context->active_button_modifier; + uiNotifyItem(ui_context, hot, UI_BUTTON2_DOWN); + } + } else { + hot_item = hot; + } + } break; + case UI_STATE_CAPTURE: { + if (!uiGetButton(ui_context, 0)) { + if (active_item >= 0) { + ui_context->active_modifier = ui_context->active_button_modifier; + uiNotifyItem(ui_context, active_item, UI_BUTTON0_UP); + if (active_item == hot) { + uiNotifyItem(ui_context, active_item, UI_BUTTON0_HOT_UP); + } + } + active_item = -1; + ui_context->state = UI_STATE_IDLE; + } else { + if (active_item >= 0) { + ui_context->active_modifier = ui_context->active_button_modifier; + uiNotifyItem(ui_context, active_item, UI_BUTTON0_CAPTURE); + } + if (hot == active_item) + hot_item = hot; + else + hot_item = -1; + } + } break; + } + + ui_context->last_cursor = ui_context->cursor; + ui_context->last_hot_item = hot_item; + ui_context->active_item = active_item; + + ui_context->last_timestamp = timestamp; + ui_context->last_buttons = ui_context->buttons; +} + +static int uiIsActive(UIcontext *ui_context, int item) { + assert(ui_context); + return ui_context->active_item == item; +} + +static int uiIsHot(UIcontext *ui_context, int item) { + assert(ui_context); + return ui_context->last_hot_item == item; +} + +static int uiIsFocused(UIcontext *ui_context, int item) { + assert(ui_context); + return ui_context->focus_item == item; +} + +UIitemState uiGetState(UIcontext *ui_context, int item) { + UIitem *pitem = uiItemPtr(ui_context, item); + if (pitem->flags & UI_ITEM_FROZEN) return UI_FROZEN; + if (uiIsFocused(ui_context, item)) { + if (pitem->flags & (UI_KEY_DOWN|UI_CHAR|UI_KEY_UP)) return UI_ACTIVE; + } + if (uiIsActive(ui_context, item)) { + if (pitem->flags & (UI_BUTTON0_CAPTURE|UI_BUTTON0_UP)) return UI_ACTIVE; + if ((pitem->flags & UI_BUTTON0_HOT_UP) + && uiIsHot(ui_context, item)) return UI_ACTIVE; + return UI_COLD; + } else if (uiIsHot(ui_context, item)) { + return UI_HOT; + } + return UI_COLD; +} + +#endif // OUI_IMPLEMENTATION