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