commit 912c95c0a7e469b4b382ce4827e334bd6bc2b4b4
parent 0de67b19990a00a54d2cf345c94d1018789b8956
Author: falkTX <falktx@gmail.com>
Date: Sat, 2 May 2015 20:29:22 +0200
Add blendish code
Diffstat:
3 files changed, 4445 insertions(+), 0 deletions(-)
diff --git a/dgl/src/oui-blendish/LICENSE 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 <leonard.ritter@duangle.com>
+
+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
@@ -0,0 +1,2399 @@
+/*
+Blendish - Blender 2.5 UI based theming functions for NanoVG
+
+Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com>
+
+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 <iconid> 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 <iconid> 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 <iconid>, 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 <memory.h>
+#include <math.h>
+
+#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 <float.h>
+
+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<color1.a)?color0.a:color1.a;
+ nvgStrokeColor(ctx, colorw);
+ nvgStrokeWidth(ctx, BND_NODE_WIRE_OUTLINE_WIDTH);
+ nvgStroke(ctx);
+ nvgStrokePaint(ctx, nvgLinearGradient(ctx,
+ x0, y0, x1, y1,
+ color0,
+ color1));
+ nvgStrokeWidth(ctx,BND_NODE_WIRE_WIDTH);
+ nvgStroke(ctx);
+}
+
+void bndNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1,
+ BNDwidgetState state0, BNDwidgetState state1) {
+ bndColoredNodeWire(ctx, x0, y0, x1, y1,
+ bndNodeWireColor(&bnd_theme.nodeTheme, state0),
+ bndNodeWireColor(&bnd_theme.nodeTheme, state1));
+}
+
+void bndNodeBackground(NVGcontext *ctx, float x, float y, float w, float h,
+ BNDwidgetState state, int iconid, const char *label, NVGcolor titleColor) {
+ bndInnerBox(ctx,x,y,w,BND_NODE_TITLE_HEIGHT+2,
+ BND_NODE_RADIUS,BND_NODE_RADIUS,0,0,
+ bndTransparent(bndOffsetColor(titleColor, BND_BEVEL_SHADE)),
+ bndTransparent(titleColor));
+ bndInnerBox(ctx,x,y+BND_NODE_TITLE_HEIGHT-1,w,h+2-BND_NODE_TITLE_HEIGHT,
+ 0,0,BND_NODE_RADIUS,BND_NODE_RADIUS,
+ bndTransparent(bnd_theme.nodeTheme.nodeBackdropColor),
+ bndTransparent(bnd_theme.nodeTheme.nodeBackdropColor));
+ bndNodeIconLabel(ctx,
+ x+BND_NODE_ARROW_AREA_WIDTH,y,
+ w-BND_NODE_ARROW_AREA_WIDTH-BND_NODE_MARGIN_SIDE,BND_NODE_TITLE_HEIGHT,
+ iconid, bnd_theme.regularTheme.textColor,
+ bndOffsetColor(titleColor, BND_BEVEL_SHADE),
+ BND_LEFT, BND_LABEL_FONT_SIZE, label);
+ /*NVGcolor arrowColor;*/
+ NVGcolor borderColor;
+ switch(state) {
+ default:
+ case BND_DEFAULT: {
+ borderColor = nvgRGBf(0,0,0);
+ /*arrowColor = bndOffsetColor(titleColor, -BND_BEVEL_SHADE);*/
+ } break;
+ case BND_HOVER: {
+ borderColor = bnd_theme.nodeTheme.nodeSelectedColor;
+ /*arrowColor = bnd_theme.nodeTheme.nodeSelectedColor;*/
+ } break;
+ case BND_ACTIVE: {
+ borderColor = bnd_theme.nodeTheme.activeNodeColor;
+ /*arrowColor = bnd_theme.nodeTheme.nodeSelectedColor;*/
+ } break;
+ }
+ bndOutlineBox(ctx,x,y,w,h+1,
+ BND_NODE_RADIUS,BND_NODE_RADIUS,BND_NODE_RADIUS,BND_NODE_RADIUS,
+ bndTransparent(borderColor));
+ /*
+ bndNodeArrowDown(ctx,
+ x + BND_NODE_MARGIN_SIDE, y + BND_NODE_TITLE_HEIGHT-4,
+ BND_NODE_ARROW_SIZE, arrowColor);
+ */
+ bndDropShadow(ctx,x,y,w,h,BND_NODE_RADIUS,
+ BND_SHADOW_FEATHER,BND_SHADOW_ALPHA);
+}
+
+void bndSplitterWidgets(NVGcontext *ctx, float x, float y, float w, float h) {
+ NVGcolor insetLight = bndTransparent(
+ bndOffsetColor(bnd_theme.backgroundColor, BND_SPLITTER_SHADE));
+ NVGcolor insetDark = bndTransparent(
+ bndOffsetColor(bnd_theme.backgroundColor, -BND_SPLITTER_SHADE));
+ NVGcolor inset = bndTransparent(bnd_theme.backgroundColor);
+
+ float x2 = x+w;
+ float y2 = y+h;
+
+ nvgBeginPath(ctx);
+ nvgMoveTo(ctx, x, y2-13);
+ nvgLineTo(ctx, x+13, y2);
+ nvgMoveTo(ctx, x, y2-9);
+ nvgLineTo(ctx, x+9, y2);
+ nvgMoveTo(ctx, x, y2-5);
+ nvgLineTo(ctx, x+5, y2);
+
+ nvgMoveTo(ctx, x2-11, y);
+ nvgLineTo(ctx, x2, y+11);
+ nvgMoveTo(ctx, x2-7, y);
+ nvgLineTo(ctx, x2, y+7);
+ nvgMoveTo(ctx, x2-3, y);
+ nvgLineTo(ctx, x2, y+3);
+
+ nvgStrokeColor(ctx, insetDark);
+ nvgStroke(ctx);
+
+ nvgBeginPath(ctx);
+ nvgMoveTo(ctx, x, y2-11);
+ nvgLineTo(ctx, x+11, y2);
+ nvgMoveTo(ctx, x, y2-7);
+ nvgLineTo(ctx, x+7, y2);
+ nvgMoveTo(ctx, x, y2-3);
+ nvgLineTo(ctx, x+3, y2);
+
+ nvgMoveTo(ctx, x2-13, y);
+ nvgLineTo(ctx, x2, y+13);
+ nvgMoveTo(ctx, x2-9, y);
+ nvgLineTo(ctx, x2, y+9);
+ nvgMoveTo(ctx, x2-5, y);
+ nvgLineTo(ctx, x2, y+5);
+
+ nvgStrokeColor(ctx, insetLight);
+ nvgStroke(ctx);
+
+ nvgBeginPath(ctx);
+ nvgMoveTo(ctx, x, y2-12);
+ nvgLineTo(ctx, x+12, y2);
+ nvgMoveTo(ctx, x, y2-8);
+ nvgLineTo(ctx, x+8, y2);
+ nvgMoveTo(ctx, x, y2-4);
+ nvgLineTo(ctx, x+4, y2);
+
+ nvgMoveTo(ctx, x2-12, y);
+ nvgLineTo(ctx, x2, y+12);
+ nvgMoveTo(ctx, x2-8, y);
+ nvgLineTo(ctx, x2, y+8);
+ nvgMoveTo(ctx, x2-4, y);
+ nvgLineTo(ctx, x2, y+4);
+
+ nvgStrokeColor(ctx, inset);
+ nvgStroke(ctx);
+}
+
+void bndJoinAreaOverlay(NVGcontext *ctx, float x, float y, float w, float h,
+ int vertical, int mirror) {
+
+ if (vertical) {
+ float u = w;
+ w = h; h = u;
+ }
+
+ float s = (w<h)?w:h;
+
+ float x0,y0,x1,y1;
+ if (mirror) {
+ x0 = w;
+ y0 = h;
+ x1 = 0;
+ y1 = 0;
+ s = -s;
+ } else {
+ x0 = 0;
+ y0 = 0;
+ x1 = w;
+ y1 = h;
+ }
+
+ float yc = (y0+y1)*0.5f;
+ float s2 = s/2.0f;
+ float s4 = s/4.0f;
+ float s8 = s/8.0f;
+ float x4 = x0+s4;
+
+ float points[][2] = {
+ { x0,y0 },
+ { x1,y0 },
+ { x1,y1 },
+ { x0,y1 },
+ { x0,yc+s8 },
+ { x4,yc+s8 },
+ { x4,yc+s4 },
+ { x0+s2,yc },
+ { x4,yc-s4 },
+ { x4,yc-s8 },
+ { x0,yc-s8 }
+ };
+
+ nvgBeginPath(ctx);
+ int count = sizeof(points) / (sizeof(float)*2);
+ nvgMoveTo(ctx,x+points[0][vertical&1],y+points[0][(vertical&1)^1]);
+ int i;
+ for (i = 1; i < count; ++i) {
+ nvgLineTo(ctx,x+points[i][vertical&1],y+points[i][(vertical&1)^1]);
+ }
+
+ nvgFillColor(ctx, nvgRGBAf(0,0,0,0.3));
+ nvgFill(ctx);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+float bndLabelWidth(NVGcontext *ctx, int iconid, const char *label) {
+ int w = BND_PAD_LEFT + BND_PAD_RIGHT;
+ if (iconid >= 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
@@ -0,0 +1,2025 @@
+/*
+OUI - A minimal semi-immediate GUI handling & layouting library
+
+Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com>
+
+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 <assert.h>
+
+#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 (a<b)?a:b;
+}
+
+UI_INLINE float ui_maxf(float a, float b) {
+ return (a>b)?a:b;
+}
+
+UI_INLINE float ui_minf(float a, float b) {
+ return (a<b)?a:b;
+}
+
+void uiClear(UIcontext *ui_context) {
+ int i;
+ ui_context->last_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<<button;
+ // set new bit
+ ui_context->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<<button))?1:0;
+}
+
+int uiGetButton(UIcontext *ui_context, unsigned int button) {
+ assert(ui_context);
+ return (ui_context->buttons & (1ull<<button))?1:0;
+}
+
+int uiButtonPressed(UIcontext *ui_context, unsigned int button) {
+ assert(ui_context);
+ return !uiGetLastButton(ui_context, button) && uiGetButton(ui_context, button);
+}
+
+int uiButtonReleased(UIcontext *ui_context, unsigned int button) {
+ assert(ui_context);
+ return uiGetLastButton(ui_context, button) && !uiGetButton(ui_context, button);
+}
+
+void uiSetCursor(UIcontext *ui_context, int x, int y) {
+ assert(ui_context);
+ ui_context->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)
+ && (x<rect.w)
+ && (y<rect.h)) return 1;
+ return 0;
+}
+
+int uiFindItem(UIcontext *ui_context, int item, int x, int y, unsigned int flags, unsigned int mask) {
+ UIitem *pitem = uiItemPtr(ui_context, item);
+ if (pitem->flags & 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