1 //
2 // Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
3 //
4 // This software is provided 'as-is', without any express or implied
5 // warranty.  In no event will the authors be held liable for any damages
6 // arising from the use of this software.
7 // Permission is granted to anyone to use this software for any purpose,
8 // including commercial applications, and to alter it and redistribute it
9 // freely, subject to the following restrictions:
10 // 1. The origin of this software must not be misrepresented; you must not
11 //    claim that you wrote the original software. If you use this software
12 //    in a product, an acknowledgment in the product documentation would be
13 //    appreciated but is not required.
14 // 2. Altered source versions must be plainly marked as such, and must not be
15 //    misrepresented as being the original software.
16 // 3. This notice may not be removed or altered from any source distribution.
17 //
18 
19 #include <stdio.h>
20 #include <string.h>
21 #define _USE_MATH_DEFINES
22 #include <math.h>
23 #include "imgui.h"
24 
25 #ifdef WIN32
26 #	define snprintf _snprintf
27 #endif
28 
29 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30 
31 static const unsigned TEXT_POOL_SIZE = 50000;
32 static char g_textPool[TEXT_POOL_SIZE];
33 static unsigned g_textPoolSize = 0;
allocText(const char * text)34 static const char* allocText(const char* text)
35 {
36 	unsigned len = static_cast<unsigned>(strlen(text)+1);
37 	if (g_textPoolSize + len >= TEXT_POOL_SIZE)
38 		return 0;
39 	char* dst = &g_textPool[g_textPoolSize];
40 	memcpy(dst, text, len);
41 	g_textPoolSize += len;
42 	return dst;
43 }
44 
45 static const unsigned GFXCMD_QUEUE_SIZE = 5000;
46 static imguiGfxCmd g_gfxCmdQueue[GFXCMD_QUEUE_SIZE];
47 static unsigned g_gfxCmdQueueSize = 0;
48 
resetGfxCmdQueue()49 static void resetGfxCmdQueue()
50 {
51 	g_gfxCmdQueueSize = 0;
52 	g_textPoolSize = 0;
53 }
54 
addGfxCmdScissor(int x,int y,int w,int h)55 static void addGfxCmdScissor(int x, int y, int w, int h)
56 {
57 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
58 		return;
59 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
60 	cmd.type = IMGUI_GFXCMD_SCISSOR;
61 	cmd.flags = x < 0 ? 0 : 1;	// on/off flag.
62 	cmd.col = 0;
63 	cmd.rect.x = (short)x;
64 	cmd.rect.y = (short)y;
65 	cmd.rect.w = (short)w;
66 	cmd.rect.h = (short)h;
67 }
68 
addGfxCmdRect(float x,float y,float w,float h,unsigned int color)69 static void addGfxCmdRect(float x, float y, float w, float h, unsigned int color)
70 {
71 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
72 		return;
73 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
74 	cmd.type = IMGUI_GFXCMD_RECT;
75 	cmd.flags = 0;
76 	cmd.col = color;
77 	cmd.rect.x = (short)(x*8.0f);
78 	cmd.rect.y = (short)(y*8.0f);
79 	cmd.rect.w = (short)(w*8.0f);
80 	cmd.rect.h = (short)(h*8.0f);
81 	cmd.rect.r = 0;
82 }
83 
addGfxCmdLine(float x0,float y0,float x1,float y1,float r,unsigned int color)84 static void addGfxCmdLine(float x0, float y0, float x1, float y1, float r, unsigned int color)
85 {
86 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
87 		return;
88 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
89 	cmd.type = IMGUI_GFXCMD_LINE;
90 	cmd.flags = 0;
91 	cmd.col = color;
92 	cmd.line.x0 = (short)(x0*8.0f);
93 	cmd.line.y0 = (short)(y0*8.0f);
94 	cmd.line.x1 = (short)(x1*8.0f);
95 	cmd.line.y1 = (short)(y1*8.0f);
96 	cmd.line.r = (short)(r*8.0f);
97 }
98 
addGfxCmdRoundedRect(float x,float y,float w,float h,float r,unsigned int color)99 static void addGfxCmdRoundedRect(float x, float y, float w, float h, float r, unsigned int color)
100 {
101 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
102 		return;
103 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
104 	cmd.type = IMGUI_GFXCMD_RECT;
105 	cmd.flags = 0;
106 	cmd.col = color;
107 	cmd.rect.x = (short)(x*8.0f);
108 	cmd.rect.y = (short)(y*8.0f);
109 	cmd.rect.w = (short)(w*8.0f);
110 	cmd.rect.h = (short)(h*8.0f);
111 	cmd.rect.r = (short)(r*8.0f);
112 }
113 
addGfxCmdTriangle(int x,int y,int w,int h,int flags,unsigned int color)114 static void addGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color)
115 {
116 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
117 		return;
118 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
119 	cmd.type = IMGUI_GFXCMD_TRIANGLE;
120 	cmd.flags = (char)flags;
121 	cmd.col = color;
122 	cmd.rect.x = (short)(x*8.0f);
123 	cmd.rect.y = (short)(y*8.0f);
124 	cmd.rect.w = (short)(w*8.0f);
125 	cmd.rect.h = (short)(h*8.0f);
126 }
127 
addGfxCmdText(int x,int y,int align,const char * text,unsigned int color)128 static void addGfxCmdText(int x, int y, int align, const char* text, unsigned int color)
129 {
130 	if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
131 		return;
132 	imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
133 	cmd.type = IMGUI_GFXCMD_TEXT;
134 	cmd.flags = 0;
135 	cmd.col = color;
136 	cmd.text.x = (short)x;
137 	cmd.text.y = (short)y;
138 	cmd.text.align = (short)align;
139 	cmd.text.text = allocText(text);
140 }
141 
142 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
143 struct GuiState
144 {
GuiStateGuiState145 	GuiState() :
146 		left(false), leftPressed(false), leftReleased(false),
147 		mx(-1), my(-1), scroll(0),
148 		active(0), hot(0), hotToBe(0), isHot(false), isActive(false), wentActive(false),
149 		dragX(0), dragY(0), dragOrig(0), widgetX(0), widgetY(0), widgetW(100),
150 		insideCurrentScroll(false),  areaId(0), widgetId(0)
151 	{
152 	}
153 
154 	bool left;
155 	bool leftPressed, leftReleased;
156 	int mx,my;
157 	int scroll;
158 	unsigned int active;
159 	unsigned int hot;
160 	unsigned int hotToBe;
161 	bool isHot;
162 	bool isActive;
163 	bool wentActive;
164 	int dragX, dragY;
165 	float dragOrig;
166 	int widgetX, widgetY, widgetW;
167 	bool insideCurrentScroll;
168 
169 	unsigned int areaId;
170 	unsigned int widgetId;
171 };
172 
173 static GuiState g_state;
174 
anyActive()175 inline bool anyActive()
176 {
177 	return g_state.active != 0;
178 }
179 
isActive(unsigned int id)180 inline bool isActive(unsigned int id)
181 {
182 	return g_state.active == id;
183 }
184 
isHot(unsigned int id)185 inline bool isHot(unsigned int id)
186 {
187 	return g_state.hot == id;
188 }
189 
inRect(int x,int y,int w,int h,bool checkScroll=true)190 inline bool inRect(int x, int y, int w, int h, bool checkScroll = true)
191 {
192    return (!checkScroll || g_state.insideCurrentScroll) && g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h;
193 }
194 
clearInput()195 inline void clearInput()
196 {
197 	g_state.leftPressed = false;
198 	g_state.leftReleased = false;
199 	g_state.scroll = 0;
200 }
201 
clearActive()202 inline void clearActive()
203 {
204 	g_state.active = 0;
205 	// mark all UI for this frame as processed
206 	clearInput();
207 }
208 
setActive(unsigned int id)209 inline void setActive(unsigned int id)
210 {
211 	g_state.active = id;
212 	g_state.wentActive = true;
213 }
214 
setHot(unsigned int id)215 inline void setHot(unsigned int id)
216 {
217    g_state.hotToBe = id;
218 }
219 
220 
buttonLogic(unsigned int id,bool over)221 static bool buttonLogic(unsigned int id, bool over)
222 {
223 	bool res = false;
224 	// process down
225 	if (!anyActive())
226 	{
227 		if (over)
228 			setHot(id);
229 		if (isHot(id) && g_state.leftPressed)
230 			setActive(id);
231 	}
232 
233 	// if button is active, then react on left up
234 	if (isActive(id))
235 	{
236 		g_state.isActive = true;
237 		if (over)
238 			setHot(id);
239 		if (g_state.leftReleased)
240 		{
241 			if (isHot(id))
242 				res = true;
243 			clearActive();
244 		}
245 	}
246 
247 	if (isHot(id))
248 		g_state.isHot = true;
249 
250 	return res;
251 }
252 
updateInput(int mx,int my,unsigned char mbut,int scroll)253 static void updateInput(int mx, int my, unsigned char mbut, int scroll)
254 {
255 	bool left = (mbut & IMGUI_MBUT_LEFT) != 0;
256 
257 	g_state.mx = mx;
258 	g_state.my = my;
259 	g_state.leftPressed = !g_state.left && left;
260 	g_state.leftReleased = g_state.left && !left;
261 	g_state.left = left;
262 
263 	g_state.scroll = scroll;
264 }
265 
imguiBeginFrame(int mx,int my,unsigned char mbut,int scroll)266 void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll)
267 {
268 	updateInput(mx,my,mbut,scroll);
269 
270 	g_state.hot = g_state.hotToBe;
271 	g_state.hotToBe = 0;
272 
273 	g_state.wentActive = false;
274 	g_state.isActive = false;
275 	g_state.isHot = false;
276 
277 	g_state.widgetX = 0;
278 	g_state.widgetY = 0;
279 	g_state.widgetW = 0;
280 
281 	g_state.areaId = 1;
282 	g_state.widgetId = 1;
283 
284 	resetGfxCmdQueue();
285 }
286 
imguiEndFrame()287 void imguiEndFrame()
288 {
289 	clearInput();
290 }
291 
imguiGetRenderQueue()292 const imguiGfxCmd* imguiGetRenderQueue()
293 {
294 	return g_gfxCmdQueue;
295 }
296 
imguiGetRenderQueueSize()297 int imguiGetRenderQueueSize()
298 {
299 	return g_gfxCmdQueueSize;
300 }
301 
302 
303 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
304 static const int BUTTON_HEIGHT = 20;
305 static const int SLIDER_HEIGHT = 20;
306 static const int SLIDER_MARKER_WIDTH = 10;
307 static const int CHECK_SIZE = 8;
308 static const int DEFAULT_SPACING = 4;
309 static const int TEXT_HEIGHT = 8;
310 static const int SCROLL_AREA_PADDING = 6;
311 static const int INDENT_SIZE = 16;
312 static const int AREA_HEADER = 28;
313 
314 static int g_scrollTop = 0;
315 static int g_scrollBottom = 0;
316 static int g_scrollRight = 0;
317 static int g_scrollAreaTop = 0;
318 static int* g_scrollVal = 0;
319 static int g_focusTop = 0;
320 static int g_focusBottom = 0;
321 static unsigned int g_scrollId = 0;
322 static bool g_insideScrollArea = false;
323 
imguiBeginScrollArea(const char * name,int x,int y,int w,int h,int * scroll)324 bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll)
325 {
326 	g_state.areaId++;
327 	g_state.widgetId = 0;
328 	g_scrollId = (g_state.areaId<<16) | g_state.widgetId;
329 
330 	g_state.widgetX = x + SCROLL_AREA_PADDING;
331 	g_state.widgetY = y+h-AREA_HEADER + (*scroll);
332 	g_state.widgetW = w - SCROLL_AREA_PADDING*4;
333 	g_scrollTop = y-AREA_HEADER+h;
334 	g_scrollBottom = y+SCROLL_AREA_PADDING;
335 	g_scrollRight = x+w - SCROLL_AREA_PADDING*3;
336 	g_scrollVal = scroll;
337 
338 	g_scrollAreaTop = g_state.widgetY;
339 
340 	g_focusTop = y-AREA_HEADER;
341 	g_focusBottom = y-AREA_HEADER+h;
342 
343 	g_insideScrollArea = inRect(x, y, w, h, false);
344 	g_state.insideCurrentScroll = g_insideScrollArea;
345 
346 	addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 6, imguiRGBA(0,0,0,192));
347 
348 	addGfxCmdText(x+AREA_HEADER/2, y+h-AREA_HEADER/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, name, imguiRGBA(255,255,255,128));
349 
350 	addGfxCmdScissor(x+SCROLL_AREA_PADDING, y+SCROLL_AREA_PADDING, w-SCROLL_AREA_PADDING*4, h-AREA_HEADER-SCROLL_AREA_PADDING);
351 
352 	return g_insideScrollArea;
353 }
354 
imguiEndScrollArea()355 void imguiEndScrollArea()
356 {
357 	// Disable scissoring.
358 	addGfxCmdScissor(-1,-1,-1,-1);
359 
360 	// Draw scroll bar
361 	int x = g_scrollRight+SCROLL_AREA_PADDING/2;
362 	int y = g_scrollBottom;
363 	int w = SCROLL_AREA_PADDING*2;
364 	int h = g_scrollTop - g_scrollBottom;
365 
366 	int stop = g_scrollAreaTop;
367 	int sbot = g_state.widgetY;
368 	int sh = stop - sbot; // The scrollable area height.
369 
370 	float barHeight = (float)h/(float)sh;
371 
372 	if (barHeight < 1)
373 	{
374 		float barY = (float)(y - sbot)/(float)sh;
375 		if (barY < 0) barY = 0;
376 		if (barY > 1) barY = 1;
377 
378 		// Handle scroll bar logic.
379 		unsigned int hid = g_scrollId;
380 		int hx = x;
381 		int hy = y + (int)(barY*h);
382 		int hw = w;
383 		int hh = (int)(barHeight*h);
384 
385 		const int range = h - (hh-1);
386 		bool over = inRect(hx, hy, hw, hh);
387 		buttonLogic(hid, over);
388 		if (isActive(hid))
389 		{
390 			float u = (float)(hy-y) / (float)range;
391 			if (g_state.wentActive)
392 			{
393 				g_state.dragY = g_state.my;
394 				g_state.dragOrig = u;
395 			}
396 			if (g_state.dragY != g_state.my)
397 			{
398 				u = g_state.dragOrig + (g_state.my - g_state.dragY) / (float)range;
399 				if (u < 0) u = 0;
400 				if (u > 1) u = 1;
401 				*g_scrollVal = (int)((1-u) * (sh - h));
402 			}
403 		}
404 
405 		// BG
406 		addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)w/2-1, imguiRGBA(0,0,0,196));
407 		// Bar
408 		if (isActive(hid))
409 			addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, imguiRGBA(255,196,0,196));
410 		else
411 			addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, isHot(hid) ? imguiRGBA(255,196,0,96) : imguiRGBA(255,255,255,64));
412 
413 		// Handle mouse scrolling.
414 		if (g_insideScrollArea) // && !anyActive())
415 		{
416 			if (g_state.scroll)
417 			{
418 				*g_scrollVal += 20*g_state.scroll;
419 				if (*g_scrollVal < 0) *g_scrollVal = 0;
420 				if (*g_scrollVal > (sh - h)) *g_scrollVal = (sh - h);
421 			}
422 		}
423 	}
424 	g_state.insideCurrentScroll = false;
425 }
426 
imguiButton(const char * text,bool enabled)427 bool imguiButton(const char* text, bool enabled)
428 {
429 	g_state.widgetId++;
430 	unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
431 
432 	int x = g_state.widgetX;
433 	int y = g_state.widgetY - BUTTON_HEIGHT;
434 	int w = g_state.widgetW;
435 	int h = BUTTON_HEIGHT;
436 	g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
437 
438 	bool over = enabled && inRect(x, y, w, h);
439 	bool res = buttonLogic(id, over);
440 
441 	addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)BUTTON_HEIGHT/2-1, imguiRGBA(128,128,128, isActive(id)?196:96));
442 	if (enabled)
443 		addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
444 	else
445 		addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
446 
447 	return res;
448 }
449 
imguiItem(const char * text,bool enabled)450 bool imguiItem(const char* text, bool enabled)
451 {
452 	g_state.widgetId++;
453 	unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
454 
455 	int x = g_state.widgetX;
456 	int y = g_state.widgetY - BUTTON_HEIGHT;
457 	int w = g_state.widgetW;
458 	int h = BUTTON_HEIGHT;
459 	g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
460 
461 	bool over = enabled && inRect(x, y, w, h);
462 	bool res = buttonLogic(id, over);
463 
464 	if (isHot(id))
465 		addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 2.0f, imguiRGBA(255,196,0,isActive(id)?196:96));
466 
467 	if (enabled)
468 		addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,200));
469 	else
470 		addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
471 
472 	return res;
473 }
474 
imguiCheck(const char * text,bool checked,bool enabled)475 bool imguiCheck(const char* text, bool checked, bool enabled)
476 {
477 	g_state.widgetId++;
478 	unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
479 
480 	int x = g_state.widgetX;
481 	int y = g_state.widgetY - BUTTON_HEIGHT;
482 	int w = g_state.widgetW;
483 	int h = BUTTON_HEIGHT;
484 	g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
485 
486 	bool over = enabled && inRect(x, y, w, h);
487 	bool res = buttonLogic(id, over);
488 
489 	const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
490 	const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
491 	addGfxCmdRoundedRect((float)cx-3, (float)cy-3, (float)CHECK_SIZE+6, (float)CHECK_SIZE+6, 4, imguiRGBA(128,128,128, isActive(id)?196:96));
492 	if (checked)
493 	{
494 		if (enabled)
495 			addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(255,255,255,isActive(id)?255:200));
496 		else
497 			addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(128,128,128,200));
498 	}
499 
500 	if (enabled)
501 		addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
502 	else
503 		addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
504 
505 	return res;
506 }
507 
imguiCollapse(const char * text,const char * subtext,bool checked,bool enabled)508 bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled)
509 {
510 	g_state.widgetId++;
511 	unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
512 
513 	int x = g_state.widgetX;
514 	int y = g_state.widgetY - BUTTON_HEIGHT;
515 	int w = g_state.widgetW;
516 	int h = BUTTON_HEIGHT;
517 	g_state.widgetY -= BUTTON_HEIGHT; // + DEFAULT_SPACING;
518 
519 	const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
520 	const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
521 
522 	bool over = enabled && inRect(x, y, w, h);
523 	bool res = buttonLogic(id, over);
524 
525 	if (checked)
526 		addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 2, imguiRGBA(255,255,255,isActive(id)?255:200));
527 	else
528 		addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 1, imguiRGBA(255,255,255,isActive(id)?255:200));
529 
530 	if (enabled)
531 		addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
532 	else
533 		addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
534 
535 	if (subtext)
536 		addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, subtext, imguiRGBA(255,255,255,128));
537 
538 	return res;
539 }
540 
imguiLabel(const char * text)541 void imguiLabel(const char* text)
542 {
543 	int x = g_state.widgetX;
544 	int y = g_state.widgetY - BUTTON_HEIGHT;
545 	g_state.widgetY -= BUTTON_HEIGHT;
546 	addGfxCmdText(x, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,255));
547 }
548 
imguiValue(const char * text)549 void imguiValue(const char* text)
550 {
551 	const int x = g_state.widgetX;
552 	const int y = g_state.widgetY - BUTTON_HEIGHT;
553 	const int w = g_state.widgetW;
554 	g_state.widgetY -= BUTTON_HEIGHT;
555 
556 	addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, text, imguiRGBA(255,255,255,200));
557 }
558 
imguiSlider(const char * text,float * val,float vmin,float vmax,float vinc,bool enabled)559 bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled)
560 {
561 	g_state.widgetId++;
562 	unsigned int id = (g_state.areaId<<16) | g_state.widgetId;
563 
564 	int x = g_state.widgetX;
565 	int y = g_state.widgetY - BUTTON_HEIGHT;
566 	int w = g_state.widgetW;
567 	int h = SLIDER_HEIGHT;
568 	g_state.widgetY -= SLIDER_HEIGHT + DEFAULT_SPACING;
569 
570 	addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 4.0f, imguiRGBA(0,0,0,128));
571 
572 	const int range = w - SLIDER_MARKER_WIDTH;
573 
574 	float u = (*val - vmin) / (vmax-vmin);
575 	if (u < 0) u = 0;
576 	if (u > 1) u = 1;
577 	int m = (int)(u * range);
578 
579 	bool over = enabled && inRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT);
580 	bool res = buttonLogic(id, over);
581 	bool valChanged = false;
582 
583 	if (isActive(id))
584 	{
585 		if (g_state.wentActive)
586 		{
587 			g_state.dragX = g_state.mx;
588 			g_state.dragOrig = u;
589 		}
590 		if (g_state.dragX != g_state.mx)
591 		{
592 			u = g_state.dragOrig + (float)(g_state.mx - g_state.dragX) / (float)range;
593 			if (u < 0) u = 0;
594 			if (u > 1) u = 1;
595 			*val = vmin + u*(vmax-vmin);
596 			*val = floorf(*val/vinc+0.5f)*vinc; // Snap to vinc
597 			m = (int)(u * range);
598 			valChanged = true;
599 		}
600 	}
601 
602 	if (isActive(id))
603 		addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, imguiRGBA(255,255,255,255));
604 	else
605 		addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, isHot(id) ? imguiRGBA(255,196,0,128) : imguiRGBA(255,255,255,64));
606 
607 	// TODO: fix this, take a look at 'nicenum'.
608 	int digits = (int)(ceilf(log10f(vinc)));
609 	char fmt[16];
610 	snprintf(fmt, 16, "%%.%df", digits >= 0 ? 0 : -digits);
611 	char msg[128];
612 	snprintf(msg, 128, fmt, *val);
613 
614 	if (enabled)
615 	{
616 		addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
617 		addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200));
618 	}
619 	else
620 	{
621 		addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200));
622 		addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, imguiRGBA(128,128,128,200));
623 	}
624 
625 	return res || valChanged;
626 }
627 
628 
imguiIndent()629 void imguiIndent()
630 {
631 	g_state.widgetX += INDENT_SIZE;
632 	g_state.widgetW -= INDENT_SIZE;
633 }
634 
imguiUnindent()635 void imguiUnindent()
636 {
637 	g_state.widgetX -= INDENT_SIZE;
638 	g_state.widgetW += INDENT_SIZE;
639 }
640 
imguiSeparator()641 void imguiSeparator()
642 {
643 	g_state.widgetY -= DEFAULT_SPACING*3;
644 }
645 
imguiSeparatorLine()646 void imguiSeparatorLine()
647 {
648 	int x = g_state.widgetX;
649 	int y = g_state.widgetY - DEFAULT_SPACING*2;
650 	int w = g_state.widgetW;
651 	int h = 1;
652 	g_state.widgetY -= DEFAULT_SPACING*4;
653 
654 	addGfxCmdRect((float)x, (float)y, (float)w, (float)h, imguiRGBA(255,255,255,32));
655 }
656 
imguiDrawText(int x,int y,int align,const char * text,unsigned int color)657 void imguiDrawText(int x, int y, int align, const char* text, unsigned int color)
658 {
659 	addGfxCmdText(x, y, align, text, color);
660 }
661 
imguiDrawLine(float x0,float y0,float x1,float y1,float r,unsigned int color)662 void imguiDrawLine(float x0, float y0, float x1, float y1, float r, unsigned int color)
663 {
664 	addGfxCmdLine(x0, y0, x1, y1, r, color);
665 }
666 
imguiDrawRect(float x,float y,float w,float h,unsigned int color)667 void imguiDrawRect(float x, float y, float w, float h, unsigned int color)
668 {
669 	addGfxCmdRect(x, y, w, h, color);
670 }
671 
imguiDrawRoundedRect(float x,float y,float w,float h,float r,unsigned int color)672 void imguiDrawRoundedRect(float x, float y, float w, float h, float r, unsigned int color)
673 {
674 	addGfxCmdRoundedRect(x, y, w, h, r, color);
675 }
676 
677