You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1227 lines
40KB

  1. /*
  2. Blendish - Blender 2.5 UI based theming functions for NanoVG
  3. Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. #include <memory.h>
  21. #include <math.h>
  22. #include <blendish.h>
  23. #ifdef _MSC_VER
  24. #pragma warning (disable: 4996) // Switch off security warnings
  25. #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
  26. #pragma warning (disable: 4244)
  27. #pragma warning (disable: 4305)
  28. #ifdef __cplusplus
  29. #define BND_INLINE inline
  30. #else
  31. #define BND_INLINE
  32. #endif
  33. #include <float.h>
  34. static float bnd_fminf ( float a, float b )
  35. {
  36. return _isnan(a) ? b : ( _isnan(b) ? a : ((a < b) ? a : b));
  37. }
  38. static float bnd_fmaxf ( float a, float b )
  39. {
  40. return _isnan(a) ? b : ( _isnan(b) ? a : ((a > b) ? a : b));
  41. }
  42. static double bnd_fmin ( double a, double b )
  43. {
  44. return _isnan(a) ? b : ( _isnan(b) ? a : ((a < b) ? a : b));
  45. }
  46. static double bnd_fmax ( double a, double b )
  47. {
  48. return _isnan(a) ? b : ( _isnan(b) ? a : ((a > b) ? a : b));
  49. }
  50. #else
  51. #define BND_INLINE inline
  52. #define bnd_fminf(a, b) fminf(a, b)
  53. #define bnd_fmaxf(a, b) fmaxf(a, b)
  54. #define bnd_fmin(a, b) fmin(a, b)
  55. #define bnd_fmax(a, b) fmax(a, b)
  56. #endif
  57. ////////////////////////////////////////////////////////////////////////////////
  58. BND_INLINE float bnd_clamp(float v, float mn, float mx) {
  59. return (v > mx)?mx:(v < mn)?mn:v;
  60. }
  61. ////////////////////////////////////////////////////////////////////////////////
  62. // the initial theme
  63. static BNDtheme bnd_theme = {
  64. // backgroundColor
  65. {{{ 0.447, 0.447, 0.447, 1.0 }}},
  66. // regularTheme
  67. {
  68. {{{ 0.098,0.098,0.098,1 }}}, // color_outline
  69. {{{ 0.098,0.098,0.098,1 }}}, // color_item
  70. {{{ 0.6,0.6,0.6,1 }}}, // color_inner
  71. {{{ 0.392,0.392,0.392,1 }}}, // color_inner_selected
  72. BND_COLOR_TEXT, // color_text
  73. BND_COLOR_TEXT_SELECTED, // color_text_selected
  74. 0, // shade_top
  75. 0, // shade_down
  76. },
  77. // toolTheme
  78. {
  79. {{{ 0.098,0.098,0.098,1 }}}, // color_outline
  80. {{{ 0.098,0.098,0.098,1 }}}, // color_item
  81. {{{ 0.6,0.6,0.6,1 }}}, // color_inner
  82. {{{ 0.392,0.392,0.392,1 }}}, // color_inner_selected
  83. BND_COLOR_TEXT, // color_text
  84. BND_COLOR_TEXT_SELECTED, // color_text_selected
  85. 15, // shade_top
  86. -15, // shade_down
  87. },
  88. // radioTheme
  89. {
  90. {{{ 0,0,0,1 }}}, // color_outline
  91. {{{ 1,1,1,1 }}}, // color_item
  92. {{{ 0.275,0.275,0.275,1 }}}, // color_inner
  93. {{{ 0.337,0.502,0.761,1 }}}, // color_inner_selected
  94. BND_COLOR_TEXT_SELECTED, // color_text
  95. BND_COLOR_TEXT, // color_text_selected
  96. 15, // shade_top
  97. -15, // shade_down
  98. },
  99. // textFieldTheme
  100. {
  101. {{{ 0.098,0.098,0.098,1 }}}, // color_outline
  102. {{{ 0.353, 0.353, 0.353,1 }}}, // color_item
  103. {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner
  104. {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected
  105. BND_COLOR_TEXT, // color_text
  106. BND_COLOR_TEXT_SELECTED, // color_text_selected
  107. 0, // shade_top
  108. 25, // shade_down
  109. },
  110. // optionTheme
  111. {
  112. {{{ 0,0,0,1 }}}, // color_outline
  113. {{{ 1,1,1,1 }}}, // color_item
  114. {{{ 0.275,0.275,0.275,1 }}}, // color_inner
  115. {{{ 0.275,0.275,0.275,1 }}}, // color_inner_selected
  116. BND_COLOR_TEXT, // color_text
  117. BND_COLOR_TEXT_SELECTED, // color_text_selected
  118. 15, // shade_top
  119. -15, // shade_down
  120. },
  121. // choiceTheme
  122. {
  123. {{{ 0,0,0,1 }}}, // color_outline
  124. {{{ 1,1,1,1 }}}, // color_item
  125. {{{ 0.275,0.275,0.275,1 }}}, // color_inner
  126. {{{ 0.275,0.275,0.275,1 }}}, // color_inner_selected
  127. BND_COLOR_TEXT_SELECTED, // color_text
  128. {{{ 0.8,0.8,0.8,1 }}}, // color_text_selected
  129. 15, // shade_top
  130. -15, // shade_down
  131. },
  132. // numberFieldTheme
  133. {
  134. {{{ 0.098,0.098,0.098,1 }}}, // color_outline
  135. {{{ 0.353, 0.353, 0.353,1 }}}, // color_item
  136. {{{ 0.706, 0.706, 0.706,1 }}}, // color_inner
  137. {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected
  138. BND_COLOR_TEXT, // color_text
  139. BND_COLOR_TEXT_SELECTED, // color_text_selected
  140. -20, // shade_top
  141. 0, // shade_down
  142. },
  143. // sliderTheme
  144. {
  145. {{{ 0.098,0.098,0.098,1 }}}, // color_outline
  146. {{{ 0.502,0.502,0.502,1 }}}, // color_item
  147. {{{ 0.706, 0.706, 0.706,1 }}}, // color_inner
  148. {{{ 0.6, 0.6, 0.6,1 }}}, // color_inner_selected
  149. BND_COLOR_TEXT, // color_text
  150. BND_COLOR_TEXT_SELECTED, // color_text_selected
  151. -20, // shade_top
  152. 0, // shade_down
  153. },
  154. // scrollBarTheme
  155. {
  156. {{{ 0.196,0.196,0.196,1 }}}, // color_outline
  157. {{{ 0.502,0.502,0.502,1 }}}, // color_item
  158. {{{ 0.314, 0.314, 0.314,0.706 }}}, // color_inner
  159. {{{ 0.392, 0.392, 0.392,0.706 }}}, // color_inner_selected
  160. BND_COLOR_TEXT, // color_text
  161. BND_COLOR_TEXT_SELECTED, // color_text_selected
  162. 5, // shade_top
  163. -5, // shade_down
  164. },
  165. // tooltipTheme
  166. {
  167. {{{ 0,0,0,1 }}}, // color_outline
  168. {{{ 0.392,0.392,0.392,1 }}}, // color_item
  169. {{{ 0.098, 0.098, 0.098, 0.902 }}}, // color_inner
  170. {{{ 0.176, 0.176, 0.176, 0.902 }}}, // color_inner_selected
  171. {{{ 0.627, 0.627, 0.627, 1 }}}, // color_text
  172. BND_COLOR_TEXT_SELECTED, // color_text_selected
  173. 0, // shade_top
  174. 0, // shade_down
  175. },
  176. // menuTheme
  177. {
  178. {{{ 0,0,0,1 }}}, // color_outline
  179. {{{ 0.392,0.392,0.392,1 }}}, // color_item
  180. {{{ 0.098, 0.098, 0.098, 0.902 }}}, // color_inner
  181. {{{ 0.176, 0.176, 0.176, 0.902 }}}, // color_inner_selected
  182. {{{ 0.627, 0.627, 0.627, 1 }}}, // color_text
  183. BND_COLOR_TEXT_SELECTED, // color_text_selected
  184. 0, // shade_top
  185. 0, // shade_down
  186. },
  187. // menuItemTheme
  188. {
  189. {{{ 0,0,0,1 }}}, // color_outline
  190. {{{ 0.675,0.675,0.675,0.502 }}}, // color_item
  191. {{{ 0,0,0,0 }}}, // color_inner
  192. {{{ 0.337,0.502,0.761,1 }}}, // color_inner_selected
  193. BND_COLOR_TEXT_SELECTED, // color_text
  194. BND_COLOR_TEXT, // color_text_selected
  195. 38, // shade_top
  196. 0, // shade_down
  197. },
  198. // nodeTheme
  199. {
  200. {{{ 0.945,0.345,0,1 }}}, // nodeSelectedColor
  201. {{{ 0,0,0,1 }}}, // wiresColor
  202. {{{ 0.498,0.439,0.439,1 }}}, // textSelectedColor
  203. {{{ 1,0.667,0.251,1 }}}, // activeNodeColor
  204. {{{ 1,1,1,1 }}}, // wireSelectColor
  205. {{{ 0.608,0.608,0.608,0.627 }}}, // nodeBackdropColor
  206. 5, // noodleCurving
  207. },
  208. };
  209. ////////////////////////////////////////////////////////////////////////////////
  210. void bndSetTheme(BNDtheme theme) {
  211. bnd_theme = theme;
  212. }
  213. const BNDtheme *bndGetTheme() {
  214. return &bnd_theme;
  215. }
  216. // the handle to the image containing the icon sheet
  217. static int bnd_icon_image = -1;
  218. void bndSetIconImage(int image) {
  219. bnd_icon_image = image;
  220. }
  221. // the handle to the UI font
  222. static int bnd_font = -1;
  223. void bndSetFont(int font) {
  224. bnd_font = font;
  225. }
  226. ////////////////////////////////////////////////////////////////////////////////
  227. void bndLabel(NVGcontext *ctx,
  228. float x, float y, float w, float h, int iconid, const char *label) {
  229. bndIconLabelValue(ctx,x,y,w,h,iconid,
  230. bnd_theme.regularTheme.textColor, BND_LEFT,
  231. BND_LABEL_FONT_SIZE, label, NULL);
  232. }
  233. void bndToolButton(NVGcontext *ctx,
  234. float x, float y, float w, float h, int flags, BNDwidgetState state,
  235. int iconid, const char *label) {
  236. float cr[4];
  237. NVGcolor shade_top, shade_down;
  238. bndSelectCorners(cr, BND_TOOL_RADIUS, flags);
  239. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  240. bndInnerColors(&shade_top, &shade_down, &bnd_theme.toolTheme, state, 1);
  241. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  242. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  243. bndTransparent(bnd_theme.toolTheme.outlineColor));
  244. bndIconLabelValue(ctx,x,y,w,h,iconid,
  245. bndTextColor(&bnd_theme.toolTheme, state), BND_CENTER,
  246. BND_LABEL_FONT_SIZE, label, NULL);
  247. }
  248. void bndRadioButton(NVGcontext *ctx,
  249. float x, float y, float w, float h, int flags, BNDwidgetState state,
  250. int iconid, const char *label) {
  251. float cr[4];
  252. NVGcolor shade_top, shade_down;
  253. bndSelectCorners(cr, BND_OPTION_RADIUS, flags);
  254. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  255. bndInnerColors(&shade_top, &shade_down, &bnd_theme.radioTheme, state, 1);
  256. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  257. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  258. bndTransparent(bnd_theme.radioTheme.outlineColor));
  259. bndIconLabelValue(ctx,x,y,w,h,iconid,
  260. bndTextColor(&bnd_theme.radioTheme, state), BND_CENTER,
  261. BND_LABEL_FONT_SIZE, label, NULL);
  262. }
  263. int bndTextFieldTextPosition(NVGcontext *ctx, float x, float y, float w, float h,
  264. int iconid, const char *text, int px, int py) {
  265. return bndIconLabelTextPosition(ctx, x, y, w, h,
  266. iconid, BND_LABEL_FONT_SIZE, text, px, py);
  267. }
  268. void bndTextField(NVGcontext *ctx,
  269. float x, float y, float w, float h, int flags, BNDwidgetState state,
  270. int iconid, const char *text, int cbegin, int cend) {
  271. float cr[4];
  272. NVGcolor shade_top, shade_down;
  273. bndSelectCorners(cr, BND_TEXT_RADIUS, flags);
  274. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  275. bndInnerColors(&shade_top, &shade_down, &bnd_theme.textFieldTheme, state, 0);
  276. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  277. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  278. bndTransparent(bnd_theme.textFieldTheme.outlineColor));
  279. if (state != BND_ACTIVE) {
  280. cend = -1;
  281. }
  282. bndIconLabelCaret(ctx,x,y,w,h,iconid,
  283. bndTextColor(&bnd_theme.textFieldTheme, state), BND_LABEL_FONT_SIZE,
  284. text, bnd_theme.textFieldTheme.itemColor, cbegin, cend);
  285. }
  286. void bndOptionButton(NVGcontext *ctx,
  287. float x, float y, float w, float h, BNDwidgetState state,
  288. const char *label) {
  289. float ox, oy;
  290. NVGcolor shade_top, shade_down;
  291. ox = x;
  292. oy = y+h-BND_OPTION_HEIGHT-3;
  293. bndBevelInset(ctx,ox,oy,
  294. BND_OPTION_WIDTH,BND_OPTION_HEIGHT,
  295. BND_OPTION_RADIUS,BND_OPTION_RADIUS);
  296. bndInnerColors(&shade_top, &shade_down, &bnd_theme.optionTheme, state, 1);
  297. bndInnerBox(ctx,ox,oy,
  298. BND_OPTION_WIDTH,BND_OPTION_HEIGHT,
  299. BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,
  300. shade_top, shade_down);
  301. bndOutlineBox(ctx,ox,oy,
  302. BND_OPTION_WIDTH,BND_OPTION_HEIGHT,
  303. BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,BND_OPTION_RADIUS,
  304. bndTransparent(bnd_theme.optionTheme.outlineColor));
  305. if (state == BND_ACTIVE) {
  306. bndCheck(ctx,ox,oy, bndTransparent(bnd_theme.optionTheme.itemColor));
  307. }
  308. bndIconLabelValue(ctx,x+12,y,w-12,h,-1,
  309. bndTextColor(&bnd_theme.optionTheme, state), BND_LEFT,
  310. BND_LABEL_FONT_SIZE, label, NULL);
  311. }
  312. void bndChoiceButton(NVGcontext *ctx,
  313. float x, float y, float w, float h, int flags, BNDwidgetState state,
  314. int iconid, const char *label) {
  315. float cr[4];
  316. NVGcolor shade_top, shade_down;
  317. bndSelectCorners(cr, BND_OPTION_RADIUS, flags);
  318. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  319. bndInnerColors(&shade_top, &shade_down, &bnd_theme.choiceTheme, state, 1);
  320. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  321. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  322. bndTransparent(bnd_theme.choiceTheme.outlineColor));
  323. bndIconLabelValue(ctx,x,y,w,h,iconid,
  324. bndTextColor(&bnd_theme.choiceTheme, state), BND_LEFT,
  325. BND_LABEL_FONT_SIZE, label, NULL);
  326. bndUpDownArrow(ctx,x+w-10,y+10,5,
  327. bndTransparent(bnd_theme.choiceTheme.itemColor));
  328. }
  329. void bndColorButton(NVGcontext *ctx,
  330. float x, float y, float w, float h, int flags, NVGcolor color) {
  331. float cr[4];
  332. bndSelectCorners(cr, BND_TOOL_RADIUS, flags);
  333. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  334. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], color, color);
  335. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  336. bndTransparent(bnd_theme.toolTheme.outlineColor));
  337. }
  338. void bndNumberField(NVGcontext *ctx,
  339. float x, float y, float w, float h, int flags, BNDwidgetState state,
  340. const char *label, const char *value) {
  341. float cr[4];
  342. NVGcolor shade_top, shade_down;
  343. bndSelectCorners(cr, BND_NUMBER_RADIUS, flags);
  344. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  345. bndInnerColors(&shade_top, &shade_down, &bnd_theme.numberFieldTheme, state, 0);
  346. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  347. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  348. bndTransparent(bnd_theme.numberFieldTheme.outlineColor));
  349. bndIconLabelValue(ctx,x,y,w,h,-1,
  350. bndTextColor(&bnd_theme.numberFieldTheme, state), BND_CENTER,
  351. BND_LABEL_FONT_SIZE, label, value);
  352. bndArrow(ctx,x+8,y+10,-BND_NUMBER_ARROW_SIZE,
  353. bndTransparent(bnd_theme.numberFieldTheme.itemColor));
  354. bndArrow(ctx,x+w-8,y+10,BND_NUMBER_ARROW_SIZE,
  355. bndTransparent(bnd_theme.numberFieldTheme.itemColor));
  356. }
  357. void bndSlider(NVGcontext *ctx,
  358. float x, float y, float w, float h, int flags, BNDwidgetState state,
  359. float progress, const char *label, const char *value) {
  360. float cr[4];
  361. NVGcolor shade_top, shade_down;
  362. bndSelectCorners(cr, BND_NUMBER_RADIUS, flags);
  363. bndBevelInset(ctx,x,y,w,h,cr[2],cr[3]);
  364. bndInnerColors(&shade_top, &shade_down, &bnd_theme.sliderTheme, state, 0);
  365. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  366. if (state == BND_ACTIVE) {
  367. shade_top = bndOffsetColor(
  368. bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeTop);
  369. shade_down = bndOffsetColor(
  370. bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeDown);
  371. } else {
  372. shade_top = bndOffsetColor(
  373. bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeDown);
  374. shade_down = bndOffsetColor(
  375. bnd_theme.sliderTheme.itemColor, bnd_theme.sliderTheme.shadeTop);
  376. }
  377. nvgScissor(ctx,x,y,8+(w-8)*bnd_clamp(progress,0,1),h);
  378. bndInnerBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  379. nvgResetScissor(ctx);
  380. bndOutlineBox(ctx,x,y,w,h,cr[0],cr[1],cr[2],cr[3],
  381. bndTransparent(bnd_theme.sliderTheme.outlineColor));
  382. bndIconLabelValue(ctx,x,y,w,h,-1,
  383. bndTextColor(&bnd_theme.sliderTheme, state), BND_CENTER,
  384. BND_LABEL_FONT_SIZE, label, value);
  385. }
  386. void bndScrollBar(NVGcontext *ctx,
  387. float x, float y, float w, float h, BNDwidgetState state,
  388. float offset, float size) {
  389. bndBevelInset(ctx,x,y,w,h,
  390. BND_SCROLLBAR_RADIUS, BND_SCROLLBAR_RADIUS);
  391. bndInnerBox(ctx,x,y,w,h,
  392. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  393. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  394. bndOffsetColor(
  395. bnd_theme.scrollBarTheme.innerColor, 3*bnd_theme.scrollBarTheme.shadeDown),
  396. bndOffsetColor(
  397. bnd_theme.scrollBarTheme.innerColor, 3*bnd_theme.scrollBarTheme.shadeTop));
  398. bndOutlineBox(ctx,x,y,w,h,
  399. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  400. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  401. bndTransparent(bnd_theme.scrollBarTheme.outlineColor));
  402. NVGcolor itemColor = bndOffsetColor(
  403. bnd_theme.scrollBarTheme.itemColor,
  404. (state == BND_ACTIVE)?BND_SCROLLBAR_ACTIVE_SHADE:0);
  405. bndScrollHandleRect(&x,&y,&w,&h,offset,size);
  406. bndInnerBox(ctx,x,y,w,h,
  407. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  408. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  409. bndOffsetColor(
  410. itemColor, 3*bnd_theme.scrollBarTheme.shadeTop),
  411. bndOffsetColor(
  412. itemColor, 3*bnd_theme.scrollBarTheme.shadeDown));
  413. bndOutlineBox(ctx,x,y,w,h,
  414. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  415. BND_SCROLLBAR_RADIUS,BND_SCROLLBAR_RADIUS,
  416. bndTransparent(bnd_theme.scrollBarTheme.outlineColor));
  417. }
  418. void bndMenuBackground(NVGcontext *ctx,
  419. float x, float y, float w, float h, int flags) {
  420. float cr[4];
  421. NVGcolor shade_top, shade_down;
  422. bndSelectCorners(cr, BND_MENU_RADIUS, flags);
  423. bndInnerColors(&shade_top, &shade_down, &bnd_theme.menuTheme,
  424. BND_DEFAULT, 0);
  425. bndInnerBox(ctx,x,y,w,h+1,cr[0],cr[1],cr[2],cr[3], shade_top, shade_down);
  426. bndOutlineBox(ctx,x,y,w,h+1,cr[0],cr[1],cr[2],cr[3],
  427. bndTransparent(bnd_theme.menuTheme.outlineColor));
  428. bndDropShadow(ctx,x,y,w,h,BND_MENU_RADIUS,
  429. BND_SHADOW_FEATHER,BND_SHADOW_ALPHA);
  430. }
  431. void bndTooltipBackground(NVGcontext *ctx, float x, float y, float w, float h) {
  432. NVGcolor shade_top, shade_down;
  433. bndInnerColors(&shade_top, &shade_down, &bnd_theme.tooltipTheme,
  434. BND_DEFAULT, 0);
  435. bndInnerBox(ctx,x,y,w,h+1,
  436. BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,
  437. shade_top, shade_down);
  438. bndOutlineBox(ctx,x,y,w,h+1,
  439. BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,BND_MENU_RADIUS,
  440. bndTransparent(bnd_theme.tooltipTheme.outlineColor));
  441. bndDropShadow(ctx,x,y,w,h,BND_MENU_RADIUS,
  442. BND_SHADOW_FEATHER,BND_SHADOW_ALPHA);
  443. }
  444. void bndMenuLabel(NVGcontext *ctx,
  445. float x, float y, float w, float h, int iconid, const char *label) {
  446. bndIconLabelValue(ctx,x,y,w,h,iconid,
  447. bnd_theme.menuTheme.textColor, BND_LEFT,
  448. BND_LABEL_FONT_SIZE, label, NULL);
  449. }
  450. void bndMenuItem(NVGcontext *ctx,
  451. float x, float y, float w, float h, BNDwidgetState state,
  452. int iconid, const char *label) {
  453. if (state != BND_DEFAULT) {
  454. bndInnerBox(ctx,x,y,w,h,0,0,0,0,
  455. bndOffsetColor(bnd_theme.menuItemTheme.innerSelectedColor,
  456. bnd_theme.menuItemTheme.shadeTop),
  457. bndOffsetColor(bnd_theme.menuItemTheme.innerSelectedColor,
  458. bnd_theme.menuItemTheme.shadeDown));
  459. state = BND_ACTIVE;
  460. }
  461. bndIconLabelValue(ctx,x,y,w,h,iconid,
  462. bndTextColor(&bnd_theme.menuItemTheme, state), BND_LEFT,
  463. BND_LABEL_FONT_SIZE, label, NULL);
  464. }
  465. void bndNodePort(NVGcontext *ctx, float x, float y, BNDwidgetState state,
  466. NVGcolor color) {
  467. nvgBeginPath(ctx);
  468. nvgCircle(ctx, x, y, BND_NODE_PORT_RADIUS);
  469. nvgStrokeColor(ctx,bnd_theme.nodeTheme.wiresColor);
  470. nvgStrokeWidth(ctx,1.0f);
  471. nvgStroke(ctx);
  472. nvgFillColor(ctx,(state != BND_DEFAULT)?
  473. bndOffsetColor(color, BND_HOVER_SHADE):color);
  474. nvgFill(ctx);
  475. }
  476. void bndColoredNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1,
  477. NVGcolor color0, NVGcolor color1) {
  478. float length = bnd_fmaxf(fabsf(x1 - x0),fabsf(y1 - y0));
  479. float delta = length*(float)bnd_theme.nodeTheme.noodleCurving/10.0f;
  480. nvgBeginPath(ctx);
  481. nvgMoveTo(ctx, x0, y0);
  482. nvgBezierTo(ctx,
  483. x0 + delta, y0,
  484. x1 - delta, y1,
  485. x1, y1);
  486. NVGcolor colorw = bnd_theme.nodeTheme.wiresColor;
  487. colorw.a = (color0.a<color1.a)?color0.a:color1.a;
  488. nvgStrokeColor(ctx, colorw);
  489. nvgStrokeWidth(ctx, BND_NODE_WIRE_OUTLINE_WIDTH);
  490. nvgStroke(ctx);
  491. nvgStrokePaint(ctx, nvgLinearGradient(ctx,
  492. x0, y0, x1, y1,
  493. color0,
  494. color1));
  495. nvgStrokeWidth(ctx,BND_NODE_WIRE_WIDTH);
  496. nvgStroke(ctx);
  497. }
  498. void bndNodeWire(NVGcontext *ctx, float x0, float y0, float x1, float y1,
  499. BNDwidgetState state0, BNDwidgetState state1) {
  500. bndColoredNodeWire(ctx, x0, y0, x1, y1,
  501. bndNodeWireColor(&bnd_theme.nodeTheme, state0),
  502. bndNodeWireColor(&bnd_theme.nodeTheme, state1));
  503. }
  504. void bndNodeBackground(NVGcontext *ctx, float x, float y, float w, float h,
  505. BNDwidgetState state, int iconid, const char *label, NVGcolor titleColor) {
  506. bndInnerBox(ctx,x,y,w,BND_NODE_TITLE_HEIGHT+2,
  507. BND_NODE_RADIUS,BND_NODE_RADIUS,0,0,
  508. bndTransparent(bndOffsetColor(titleColor, BND_BEVEL_SHADE)),
  509. bndTransparent(titleColor));
  510. bndInnerBox(ctx,x,y+BND_NODE_TITLE_HEIGHT-1,w,h+2-BND_NODE_TITLE_HEIGHT,
  511. 0,0,BND_NODE_RADIUS,BND_NODE_RADIUS,
  512. bndTransparent(bnd_theme.nodeTheme.nodeBackdropColor),
  513. bndTransparent(bnd_theme.nodeTheme.nodeBackdropColor));
  514. bndNodeIconLabel(ctx,
  515. x+BND_NODE_ARROW_AREA_WIDTH,y,
  516. w-BND_NODE_ARROW_AREA_WIDTH-BND_NODE_MARGIN_SIDE,BND_NODE_TITLE_HEIGHT,
  517. iconid, bnd_theme.regularTheme.textColor,
  518. bndOffsetColor(titleColor, BND_BEVEL_SHADE),
  519. BND_LEFT, BND_LABEL_FONT_SIZE, label);
  520. NVGcolor arrowColor;
  521. NVGcolor borderColor;
  522. switch(state) {
  523. default:
  524. case BND_DEFAULT: {
  525. borderColor = nvgRGBf(0,0,0);
  526. arrowColor = bndOffsetColor(titleColor, -BND_BEVEL_SHADE);
  527. } break;
  528. case BND_HOVER: {
  529. borderColor = bnd_theme.nodeTheme.nodeSelectedColor;
  530. arrowColor = bnd_theme.nodeTheme.nodeSelectedColor;
  531. } break;
  532. case BND_ACTIVE: {
  533. borderColor = bnd_theme.nodeTheme.activeNodeColor;
  534. arrowColor = bnd_theme.nodeTheme.nodeSelectedColor;
  535. } break;
  536. }
  537. bndOutlineBox(ctx,x,y,w,h+1,
  538. BND_NODE_RADIUS,BND_NODE_RADIUS,BND_NODE_RADIUS,BND_NODE_RADIUS,
  539. bndTransparent(borderColor));
  540. /*
  541. bndNodeArrowDown(ctx,
  542. x + BND_NODE_MARGIN_SIDE, y + BND_NODE_TITLE_HEIGHT-4,
  543. BND_NODE_ARROW_SIZE, arrowColor);
  544. */
  545. bndDropShadow(ctx,x,y,w,h,BND_NODE_RADIUS,
  546. BND_SHADOW_FEATHER,BND_SHADOW_ALPHA);
  547. }
  548. void bndSplitterWidgets(NVGcontext *ctx, float x, float y, float w, float h) {
  549. NVGcolor insetLight = bndTransparent(
  550. bndOffsetColor(bnd_theme.backgroundColor, BND_SPLITTER_SHADE));
  551. NVGcolor insetDark = bndTransparent(
  552. bndOffsetColor(bnd_theme.backgroundColor, -BND_SPLITTER_SHADE));
  553. NVGcolor inset = bndTransparent(bnd_theme.backgroundColor);
  554. float x2 = x+w;
  555. float y2 = y+h;
  556. nvgBeginPath(ctx);
  557. nvgMoveTo(ctx, x, y2-13);
  558. nvgLineTo(ctx, x+13, y2);
  559. nvgMoveTo(ctx, x, y2-9);
  560. nvgLineTo(ctx, x+9, y2);
  561. nvgMoveTo(ctx, x, y2-5);
  562. nvgLineTo(ctx, x+5, y2);
  563. nvgMoveTo(ctx, x2-11, y);
  564. nvgLineTo(ctx, x2, y+11);
  565. nvgMoveTo(ctx, x2-7, y);
  566. nvgLineTo(ctx, x2, y+7);
  567. nvgMoveTo(ctx, x2-3, y);
  568. nvgLineTo(ctx, x2, y+3);
  569. nvgStrokeColor(ctx, insetDark);
  570. nvgStroke(ctx);
  571. nvgBeginPath(ctx);
  572. nvgMoveTo(ctx, x, y2-11);
  573. nvgLineTo(ctx, x+11, y2);
  574. nvgMoveTo(ctx, x, y2-7);
  575. nvgLineTo(ctx, x+7, y2);
  576. nvgMoveTo(ctx, x, y2-3);
  577. nvgLineTo(ctx, x+3, y2);
  578. nvgMoveTo(ctx, x2-13, y);
  579. nvgLineTo(ctx, x2, y+13);
  580. nvgMoveTo(ctx, x2-9, y);
  581. nvgLineTo(ctx, x2, y+9);
  582. nvgMoveTo(ctx, x2-5, y);
  583. nvgLineTo(ctx, x2, y+5);
  584. nvgStrokeColor(ctx, insetLight);
  585. nvgStroke(ctx);
  586. nvgBeginPath(ctx);
  587. nvgMoveTo(ctx, x, y2-12);
  588. nvgLineTo(ctx, x+12, y2);
  589. nvgMoveTo(ctx, x, y2-8);
  590. nvgLineTo(ctx, x+8, y2);
  591. nvgMoveTo(ctx, x, y2-4);
  592. nvgLineTo(ctx, x+4, y2);
  593. nvgMoveTo(ctx, x2-12, y);
  594. nvgLineTo(ctx, x2, y+12);
  595. nvgMoveTo(ctx, x2-8, y);
  596. nvgLineTo(ctx, x2, y+8);
  597. nvgMoveTo(ctx, x2-4, y);
  598. nvgLineTo(ctx, x2, y+4);
  599. nvgStrokeColor(ctx, inset);
  600. nvgStroke(ctx);
  601. }
  602. void bndJoinAreaOverlay(NVGcontext *ctx, float x, float y, float w, float h,
  603. int vertical, int mirror) {
  604. if (vertical) {
  605. float u = w;
  606. w = h; h = u;
  607. }
  608. float s = (w<h)?w:h;
  609. float x0,y0,x1,y1;
  610. if (mirror) {
  611. x0 = w;
  612. y0 = h;
  613. x1 = 0;
  614. y1 = 0;
  615. s = -s;
  616. } else {
  617. x0 = 0;
  618. y0 = 0;
  619. x1 = w;
  620. y1 = h;
  621. }
  622. float yc = (y0+y1)*0.5f;
  623. float s2 = s/2.0f;
  624. float s4 = s/4.0f;
  625. float s8 = s/8.0f;
  626. float x4 = x0+s4;
  627. float points[][2] = {
  628. { x0,y0 },
  629. { x1,y0 },
  630. { x1,y1 },
  631. { x0,y1 },
  632. { x0,yc+s8 },
  633. { x4,yc+s8 },
  634. { x4,yc+s4 },
  635. { x0+s2,yc },
  636. { x4,yc-s4 },
  637. { x4,yc-s8 },
  638. { x0,yc-s8 }
  639. };
  640. nvgBeginPath(ctx);
  641. int count = sizeof(points) / (sizeof(float)*2);
  642. nvgMoveTo(ctx,x+points[0][vertical&1],y+points[0][(vertical&1)^1]);
  643. for (int i = 1; i < count; ++i) {
  644. nvgLineTo(ctx,x+points[i][vertical&1],y+points[i][(vertical&1)^1]);
  645. }
  646. nvgFillColor(ctx, nvgRGBAf(0,0,0,0.3));
  647. nvgFill(ctx);
  648. }
  649. ////////////////////////////////////////////////////////////////////////////////
  650. float bndLabelWidth(NVGcontext *ctx, int iconid, const char *label) {
  651. int w = BND_PAD_LEFT + BND_PAD_RIGHT;
  652. if (iconid >= 0) {
  653. w += BND_ICON_SHEET_RES;
  654. }
  655. if (label && (bnd_font >= 0)) {
  656. nvgFontFaceId(ctx, bnd_font);
  657. nvgFontSize(ctx, BND_LABEL_FONT_SIZE);
  658. float bounds[4];
  659. nvgTextBoxBounds(ctx, 1, 1, INFINITY, label, NULL, bounds);
  660. w += bounds[2];
  661. }
  662. return w;
  663. }
  664. float bndLabelHeight(NVGcontext *ctx, int iconid, const char *label, float width) {
  665. int h = BND_WIDGET_HEIGHT;
  666. width -= BND_TEXT_RADIUS*2;
  667. if (iconid >= 0) {
  668. width -= BND_ICON_SHEET_RES;
  669. }
  670. if (label && (bnd_font >= 0)) {
  671. nvgFontFaceId(ctx, bnd_font);
  672. nvgFontSize(ctx, BND_LABEL_FONT_SIZE);
  673. float bounds[4];
  674. nvgTextBoxBounds(ctx, 1, 1, width, label, NULL, bounds);
  675. int bh = (int)(bounds[3] - bounds[1]) + BND_TEXT_PAD_DOWN;
  676. if (bh > h)
  677. h = bh;
  678. }
  679. return h;
  680. }
  681. ////////////////////////////////////////////////////////////////////////////////
  682. void bndRoundedBox(NVGcontext *ctx, float x, float y, float w, float h,
  683. float cr0, float cr1, float cr2, float cr3) {
  684. float d;
  685. w = bnd_fmaxf(0, w);
  686. h = bnd_fmaxf(0, h);
  687. d = bnd_fminf(w, h);
  688. nvgMoveTo(ctx, x,y+h*0.5f);
  689. nvgArcTo(ctx, x,y, x+w,y, bnd_fminf(cr0, d/2));
  690. nvgArcTo(ctx, x+w,y, x+w,y+h, bnd_fminf(cr1, d/2));
  691. nvgArcTo(ctx, x+w,y+h, x,y+h, bnd_fminf(cr2, d/2));
  692. nvgArcTo(ctx, x,y+h, x,y, bnd_fminf(cr3, d/2));
  693. nvgClosePath(ctx);
  694. }
  695. NVGcolor bndTransparent(NVGcolor color) {
  696. color.a *= BND_TRANSPARENT_ALPHA;
  697. return color;
  698. }
  699. NVGcolor bndOffsetColor(NVGcolor color, int delta) {
  700. float offset = (float)delta / 255.0f;
  701. return delta?(
  702. nvgRGBAf(
  703. bnd_clamp(color.r+offset,0,1),
  704. bnd_clamp(color.g+offset,0,1),
  705. bnd_clamp(color.b+offset,0,1),
  706. color.a)
  707. ):color;
  708. }
  709. void bndBevel(NVGcontext *ctx, float x, float y, float w, float h) {
  710. // Disable bevel
  711. return;
  712. nvgStrokeWidth(ctx, 1);
  713. x += 0.5f;
  714. y += 0.5f;
  715. w -= 1;
  716. h -= 1;
  717. nvgBeginPath(ctx);
  718. nvgMoveTo(ctx, x, y+h);
  719. nvgLineTo(ctx, x+w, y+h);
  720. nvgLineTo(ctx, x+w, y);
  721. nvgStrokeColor(ctx, bndTransparent(
  722. bndOffsetColor(bnd_theme.backgroundColor, -BND_BEVEL_SHADE)));
  723. nvgStroke(ctx);
  724. nvgBeginPath(ctx);
  725. nvgMoveTo(ctx, x, y+h);
  726. nvgLineTo(ctx, x, y);
  727. nvgLineTo(ctx, x+w, y);
  728. nvgStrokeColor(ctx, bndTransparent(
  729. bndOffsetColor(bnd_theme.backgroundColor, BND_BEVEL_SHADE)));
  730. nvgStroke(ctx);
  731. }
  732. void bndBevelInset(NVGcontext *ctx, float x, float y, float w, float h,
  733. float cr2, float cr3) {
  734. // Disable bevel
  735. return;
  736. float d;
  737. y -= 0.5f;
  738. d = bnd_fminf(w, h);
  739. cr2 = bnd_fminf(cr2, d/2);
  740. cr3 = bnd_fminf(cr3, d/2);
  741. nvgBeginPath(ctx);
  742. nvgMoveTo(ctx, x+w,y+h-cr2);
  743. nvgArcTo(ctx, x+w,y+h, x,y+h, cr2);
  744. nvgArcTo(ctx, x,y+h, x,y, cr3);
  745. NVGcolor bevelColor = bndOffsetColor(bnd_theme.backgroundColor,
  746. BND_INSET_BEVEL_SHADE);
  747. nvgStrokeWidth(ctx, 1);
  748. nvgStrokePaint(ctx,
  749. nvgLinearGradient(ctx,
  750. x,y+h-bnd_fmaxf(cr2,cr3)-1,
  751. x,y+h-1,
  752. nvgRGBAf(bevelColor.r, bevelColor.g, bevelColor.b, 0),
  753. bevelColor));
  754. nvgStroke(ctx);
  755. }
  756. void bndBackground(NVGcontext *ctx, float x, float y, float w, float h) {
  757. nvgBeginPath(ctx);
  758. nvgRect(ctx, x, y, w, h);
  759. nvgFillColor(ctx, bnd_theme.backgroundColor);
  760. nvgFill(ctx);
  761. }
  762. void bndIcon(NVGcontext *ctx, float x, float y, int iconid) {
  763. int ix, iy, u, v;
  764. if (bnd_icon_image < 0) return; // no icons loaded
  765. ix = iconid & 0xff;
  766. iy = (iconid>>8) & 0xff;
  767. u = BND_ICON_SHEET_OFFSET_X + ix*BND_ICON_SHEET_GRID;
  768. v = BND_ICON_SHEET_OFFSET_Y + iy*BND_ICON_SHEET_GRID;
  769. nvgBeginPath(ctx);
  770. nvgRect(ctx,x,y,BND_ICON_SHEET_RES,BND_ICON_SHEET_RES);
  771. nvgFillPaint(ctx,
  772. nvgImagePattern(ctx,x-u,y-v,
  773. BND_ICON_SHEET_WIDTH,
  774. BND_ICON_SHEET_HEIGHT,
  775. 0,bnd_icon_image,1));
  776. nvgFill(ctx);
  777. }
  778. void bndDropShadow(NVGcontext *ctx, float x, float y, float w, float h,
  779. float r, float feather, float alpha) {
  780. nvgBeginPath(ctx);
  781. y += feather;
  782. h -= feather;
  783. nvgMoveTo(ctx, x-feather, y-feather);
  784. nvgLineTo(ctx, x, y-feather);
  785. nvgLineTo(ctx, x, y+h-feather);
  786. nvgArcTo(ctx, x,y+h,x+r,y+h,r);
  787. nvgArcTo(ctx, x+w,y+h,x+w,y+h-r,r);
  788. nvgLineTo(ctx, x+w, y-feather);
  789. nvgLineTo(ctx, x+w+feather, y-feather);
  790. nvgLineTo(ctx, x+w+feather, y+h+feather);
  791. nvgLineTo(ctx, x-feather, y+h+feather);
  792. nvgClosePath(ctx);
  793. nvgFillPaint(ctx, nvgBoxGradient(ctx,
  794. x - feather*0.5f,y - feather*0.5f,
  795. w + feather,h+feather,
  796. r+feather*0.5f,
  797. feather,
  798. nvgRGBAf(0,0,0,alpha*alpha),
  799. nvgRGBAf(0,0,0,0)));
  800. nvgFill(ctx);
  801. }
  802. void bndInnerBox(NVGcontext *ctx, float x, float y, float w, float h,
  803. float cr0, float cr1, float cr2, float cr3,
  804. NVGcolor shade_top, NVGcolor shade_down) {
  805. nvgBeginPath(ctx);
  806. bndRoundedBox(ctx,x+1,y+1,w-2,h-3,bnd_fmaxf(0,cr0-1),
  807. bnd_fmaxf(0,cr1-1),bnd_fmaxf(0,cr2-1),bnd_fmaxf(0,cr3-1));
  808. nvgFillPaint(ctx,((h-2)>w)?
  809. nvgLinearGradient(ctx,x,y,x+w,y,shade_top,shade_down):
  810. nvgLinearGradient(ctx,x,y,x,y+h,shade_top,shade_down));
  811. nvgFill(ctx);
  812. }
  813. void bndOutlineBox(NVGcontext *ctx, float x, float y, float w, float h,
  814. float cr0, float cr1, float cr2, float cr3, NVGcolor color) {
  815. nvgBeginPath(ctx);
  816. bndRoundedBox(ctx,x+0.5f,y+0.5f,w-1,h-2,cr0,cr1,cr2,cr3);
  817. nvgStrokeColor(ctx,color);
  818. nvgStrokeWidth(ctx,1);
  819. nvgStroke(ctx);
  820. }
  821. void bndSelectCorners(float *radiuses, float r, int flags) {
  822. radiuses[0] = (flags & BND_CORNER_TOP_LEFT)?0:r;
  823. radiuses[1] = (flags & BND_CORNER_TOP_RIGHT)?0:r;
  824. radiuses[2] = (flags & BND_CORNER_DOWN_RIGHT)?0:r;
  825. radiuses[3] = (flags & BND_CORNER_DOWN_LEFT)?0:r;
  826. }
  827. void bndInnerColors(
  828. NVGcolor *shade_top, NVGcolor *shade_down,
  829. const BNDwidgetTheme *theme, BNDwidgetState state, int flipActive) {
  830. switch(state) {
  831. default:
  832. case BND_DEFAULT: {
  833. *shade_top = bndOffsetColor(theme->innerColor, theme->shadeTop);
  834. *shade_down = bndOffsetColor(theme->innerColor, theme->shadeDown);
  835. } break;
  836. case BND_HOVER: {
  837. NVGcolor color = bndOffsetColor(theme->innerColor, BND_HOVER_SHADE);
  838. *shade_top = bndOffsetColor(color, theme->shadeTop);
  839. *shade_down = bndOffsetColor(color, theme->shadeDown);
  840. } break;
  841. case BND_ACTIVE: {
  842. *shade_top = bndOffsetColor(theme->innerSelectedColor,
  843. flipActive?theme->shadeDown:theme->shadeTop);
  844. *shade_down = bndOffsetColor(theme->innerSelectedColor,
  845. flipActive?theme->shadeTop:theme->shadeDown);
  846. } break;
  847. }
  848. }
  849. NVGcolor bndTextColor(const BNDwidgetTheme *theme, BNDwidgetState state) {
  850. return (state == BND_ACTIVE)?theme->textSelectedColor:theme->textColor;
  851. }
  852. void bndIconLabelValue(NVGcontext *ctx, float x, float y, float w, float h,
  853. int iconid, NVGcolor color, int align, float fontsize, const char *label,
  854. const char *value) {
  855. float pleft = BND_PAD_LEFT;
  856. if (label) {
  857. if (iconid >= 0) {
  858. bndIcon(ctx,x+4,y+2,iconid);
  859. pleft += BND_ICON_SHEET_RES;
  860. }
  861. if (bnd_font < 0) return;
  862. nvgFontFaceId(ctx, bnd_font);
  863. nvgFontSize(ctx, fontsize);
  864. nvgBeginPath(ctx);
  865. nvgFillColor(ctx, color);
  866. if (value) {
  867. float label_width = nvgTextBounds(ctx, 1, 1, label, NULL, NULL);
  868. float sep_width = nvgTextBounds(ctx, 1, 1,
  869. BND_LABEL_SEPARATOR, NULL, NULL);
  870. nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
  871. x += pleft;
  872. if (align == BND_CENTER) {
  873. float width = label_width + sep_width
  874. + nvgTextBounds(ctx, 1, 1, value, NULL, NULL);
  875. x += ((w-BND_PAD_RIGHT-pleft)-width)*0.5f;
  876. }
  877. y += BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN;
  878. nvgText(ctx, x, y, label, NULL);
  879. x += label_width;
  880. nvgText(ctx, x, y, BND_LABEL_SEPARATOR, NULL);
  881. x += sep_width;
  882. nvgText(ctx, x, y, value, NULL);
  883. } else {
  884. nvgTextAlign(ctx,
  885. (align==BND_LEFT)?(NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE):
  886. (NVG_ALIGN_CENTER|NVG_ALIGN_BASELINE));
  887. nvgTextBox(ctx,x+pleft,y+BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN,
  888. w-BND_PAD_RIGHT-pleft,label, NULL);
  889. }
  890. } else if (iconid >= 0) {
  891. bndIcon(ctx,x+2,y+2,iconid);
  892. }
  893. }
  894. void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h,
  895. int iconid, NVGcolor color, NVGcolor shadowColor,
  896. int align, float fontsize, const char *label) {
  897. if (label && (bnd_font >= 0)) {
  898. nvgFontFaceId(ctx, bnd_font);
  899. nvgFontSize(ctx, fontsize);
  900. nvgBeginPath(ctx);
  901. nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
  902. nvgFillColor(ctx, shadowColor);
  903. nvgFontBlur(ctx, BND_NODE_TITLE_FEATHER);
  904. nvgTextBox(ctx,x+1,y+h+3-BND_TEXT_PAD_DOWN,
  905. w,label, NULL);
  906. nvgFillColor(ctx, color);
  907. nvgFontBlur(ctx, 0);
  908. nvgTextBox(ctx,x,y+h+2-BND_TEXT_PAD_DOWN,
  909. w,label, NULL);
  910. }
  911. if (iconid >= 0) {
  912. bndIcon(ctx,x+w-BND_ICON_SHEET_RES,y+3,iconid);
  913. }
  914. }
  915. int bndIconLabelTextPosition(NVGcontext *ctx, float x, float y, float w, float h,
  916. int iconid, float fontsize, const char *label, int px, int py) {
  917. float bounds[4];
  918. float pleft = BND_TEXT_RADIUS;
  919. if (!label) return -1;
  920. if (iconid >= 0)
  921. pleft += BND_ICON_SHEET_RES;
  922. if (bnd_font < 0) return -1;
  923. x += pleft;
  924. y += BND_WIDGET_HEIGHT - BND_TEXT_PAD_DOWN;
  925. nvgFontFaceId(ctx, bnd_font);
  926. nvgFontSize(ctx, fontsize);
  927. nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE);
  928. w -= BND_TEXT_RADIUS + pleft;
  929. float asc, desc, lh;
  930. static NVGtextRow rows[BND_MAX_ROWS];
  931. int nrows = nvgTextBreakLines(
  932. ctx, label, NULL, w, rows, BND_MAX_ROWS);
  933. if (nrows == 0) return 0;
  934. nvgTextBoxBounds(ctx, x, y, w, label, NULL, bounds);
  935. nvgTextMetrics(ctx, &asc, &desc, &lh);
  936. // calculate vertical position
  937. int row = bnd_clamp((int)((float)(py - bounds[1]) / lh), 0, nrows - 1);
  938. // search horizontal position
  939. static NVGglyphPosition glyphs[BND_MAX_GLYPHS];
  940. int nglyphs = nvgTextGlyphPositions(
  941. ctx, x, y, rows[row].start, rows[row].end + 1, glyphs, BND_MAX_GLYPHS);
  942. int col, p = 0;
  943. for (col = 0; col < nglyphs && glyphs[col].x < px; ++col)
  944. p = glyphs[col].str - label;
  945. // see if we should move one character further
  946. if (col > 0 && col < nglyphs && glyphs[col].x - px < px - glyphs[col - 1].x)
  947. p = glyphs[col].str - label;
  948. return p;
  949. }
  950. static void bndCaretPosition(NVGcontext *ctx, float x, float y,
  951. float desc, float lineHeight, const char *caret, NVGtextRow *rows,int nrows,
  952. int *cr, float *cx, float *cy) {
  953. static NVGglyphPosition glyphs[BND_MAX_GLYPHS];
  954. int r,nglyphs;
  955. for (r=0; r < nrows-1 && rows[r].end < caret; ++r);
  956. *cr = r;
  957. *cx = x;
  958. *cy = y-lineHeight-desc + r*lineHeight;
  959. if (nrows == 0) return;
  960. *cx = rows[r].minx;
  961. nglyphs = nvgTextGlyphPositions(
  962. ctx, x, y, rows[r].start, rows[r].end+1, glyphs, BND_MAX_GLYPHS);
  963. for (int i=0; i < nglyphs; ++i) {
  964. *cx=glyphs[i].x;
  965. if (glyphs[i].str == caret) break;
  966. }
  967. }
  968. void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h,
  969. int iconid, NVGcolor color, float fontsize, const char *label,
  970. NVGcolor caretcolor, int cbegin, int cend) {
  971. float pleft = BND_TEXT_RADIUS;
  972. if (!label) return;
  973. if (iconid >= 0) {
  974. bndIcon(ctx,x+4,y+2,iconid);
  975. pleft += BND_ICON_SHEET_RES;
  976. }
  977. if (bnd_font < 0) return;
  978. x+=pleft;
  979. y+=BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN;
  980. nvgFontFaceId(ctx, bnd_font);
  981. nvgFontSize(ctx, fontsize);
  982. nvgTextAlign(ctx, NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE);
  983. w -= BND_TEXT_RADIUS+pleft;
  984. if (cend >= cbegin) {
  985. int c0r,c1r;
  986. float c0x,c0y,c1x,c1y;
  987. float desc,lh;
  988. static NVGtextRow rows[BND_MAX_ROWS];
  989. int nrows = nvgTextBreakLines(
  990. ctx, label, label+cend+1, w, rows, BND_MAX_ROWS);
  991. nvgTextMetrics(ctx, NULL, &desc, &lh);
  992. bndCaretPosition(ctx, x, y, desc, lh, label+cbegin,
  993. rows, nrows, &c0r, &c0x, &c0y);
  994. bndCaretPosition(ctx, x, y, desc, lh, label+cend,
  995. rows, nrows, &c1r, &c1x, &c1y);
  996. nvgBeginPath(ctx);
  997. if (cbegin == cend) {
  998. nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761));
  999. nvgRect(ctx, c0x-1, c0y, 2, lh+1);
  1000. } else {
  1001. nvgFillColor(ctx, caretcolor);
  1002. if (c0r == c1r) {
  1003. nvgRect(ctx, c0x-1, c0y, c1x-c0x+1, lh+1);
  1004. } else {
  1005. int blk=c1r-c0r-1;
  1006. nvgRect(ctx, c0x-1, c0y, x+w-c0x+1, lh+1);
  1007. nvgRect(ctx, x, c1y, c1x-x+1, lh+1);
  1008. if (blk)
  1009. nvgRect(ctx, x, c0y+lh, w, blk*lh+1);
  1010. }
  1011. }
  1012. nvgFill(ctx);
  1013. }
  1014. nvgBeginPath(ctx);
  1015. nvgFillColor(ctx, color);
  1016. nvgTextBox(ctx,x,y,w,label, NULL);
  1017. }
  1018. void bndCheck(NVGcontext *ctx, float ox, float oy, NVGcolor color) {
  1019. nvgBeginPath(ctx);
  1020. nvgStrokeWidth(ctx,2);
  1021. nvgStrokeColor(ctx,color);
  1022. nvgLineCap(ctx,NVG_BUTT);
  1023. nvgLineJoin(ctx,NVG_MITER);
  1024. nvgMoveTo(ctx,ox+4,oy+5);
  1025. nvgLineTo(ctx,ox+7,oy+8);
  1026. nvgLineTo(ctx,ox+14,oy+1);
  1027. nvgStroke(ctx);
  1028. }
  1029. void bndArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color) {
  1030. nvgBeginPath(ctx);
  1031. nvgMoveTo(ctx,x,y);
  1032. nvgLineTo(ctx,x-s,y+s);
  1033. nvgLineTo(ctx,x-s,y-s);
  1034. nvgClosePath(ctx);
  1035. nvgFillColor(ctx,color);
  1036. nvgFill(ctx);
  1037. }
  1038. void bndUpDownArrow(NVGcontext *ctx, float x, float y, float s, NVGcolor color) {
  1039. float w;
  1040. nvgBeginPath(ctx);
  1041. w = 1.1f*s;
  1042. nvgMoveTo(ctx,x,y-1);
  1043. nvgLineTo(ctx,x+0.5*w,y-s-1);
  1044. nvgLineTo(ctx,x+w,y-1);
  1045. nvgClosePath(ctx);
  1046. nvgMoveTo(ctx,x,y+1);
  1047. nvgLineTo(ctx,x+0.5*w,y+s+1);
  1048. nvgLineTo(ctx,x+w,y+1);
  1049. nvgClosePath(ctx);
  1050. nvgFillColor(ctx,color);
  1051. nvgFill(ctx);
  1052. }
  1053. void bndNodeArrowDown(NVGcontext *ctx, float x, float y, float s, NVGcolor color) {
  1054. float w;
  1055. nvgBeginPath(ctx);
  1056. w = 1.0f*s;
  1057. nvgMoveTo(ctx,x,y);
  1058. nvgLineTo(ctx,x+0.5*w,y-s);
  1059. nvgLineTo(ctx,x-0.5*w,y-s);
  1060. nvgClosePath(ctx);
  1061. nvgFillColor(ctx,color);
  1062. nvgFill(ctx);
  1063. }
  1064. void bndScrollHandleRect(float *x, float *y, float *w, float *h,
  1065. float offset, float size) {
  1066. size = bnd_clamp(size,0,1);
  1067. offset = bnd_clamp(offset,0,1);
  1068. if ((*h) > (*w)) {
  1069. float hs = bnd_fmaxf(size*(*h), (*w)+1);
  1070. *y = (*y) + ((*h)-hs)*offset;
  1071. *h = hs;
  1072. } else {
  1073. float ws = bnd_fmaxf(size*(*w), (*h)-1);
  1074. *x = (*x) + ((*w)-ws)*offset;
  1075. *w = ws;
  1076. }
  1077. }
  1078. NVGcolor bndNodeWireColor(const BNDnodeTheme *theme, BNDwidgetState state) {
  1079. switch(state) {
  1080. default:
  1081. case BND_DEFAULT: return nvgRGBf(0.5f,0.5f,0.5f);
  1082. case BND_HOVER: return theme->wireSelectColor;
  1083. case BND_ACTIVE: return theme->activeNodeColor;
  1084. }
  1085. }