1 /// DDUI SDL2 OpenGL1.1 example
2 module demo;
3 
4 import core.stdc.stdio : printf, sprintf, snprintf;
5 import core.stdc.string;
6 import core.stdc.ctype;
7 import core.stdc.stdarg;
8 import bindbc.opengl;
9 import bindbc.sdl, bindbc.sdl.dynload;
10 import ddui, stopwatch;
11 version (Demo_GL33)
12     import renderer.sdl2.gl33;
13 else
14     import renderer.sdl2.gl11;
15 
16 extern (C):
17 
18 // Start and runtime window dimension.
19 __gshared int window_width  = 700;
20 __gshared int window_height = 550;
21 __gshared SDL_Window *window;
22 __gshared SDL_GLContext glctx;
23 __gshared mu_Context uictx;
24 
25 void main(int argc, const(char) **args)
26 {
27     bool cli_debug;
28     
29     for (int argi = 1; argi < argc; ++argi)
30     {
31         const(char) *arg = args[argi];
32         
33         if (strcmp(arg, "--debug") == 0)
34             cli_debug = true;
35     }
36     
37     if (cli_debug)
38     {
39         printf("* mu_Context.sizeof: %zu\n", mu_Context.sizeof);
40         printf("* mu_Command.sizeof: %zu\n", mu_Command.sizeof);
41     }
42     
43     import std.compiler : version_major, version_minor;
44     printf("* COMPILER    : "~__VENDOR__~" v%u.%03u\n", version_major, version_minor);
45     
46     // Comment this section if you plan to use
47     // the bindbc-sdl:staticBC configuration.
48     version (Windows)
49     {
50         SDLSupport sdlstatus = loadSDL("sdl2.dll");
51     }
52     else
53     {
54         SDLSupport sdlstatus = loadSDL();
55     }
56     
57     switch (sdlstatus) with (SDLSupport) {
58     case noLibrary:  assert(0, "No SDL libraries found on system, aborting.");
59     case badLibrary: assert(0, "SDL library older than configuration, aborting.");
60     default:
61     }
62     
63     // Setup SDL
64     SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0");
65     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS);
66     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
67     version (GL33)
68     {
69         //SDL_GL_SetAttribute (SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
70         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
71         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
72     }
73     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
74     
75     // Initiate SDL window
76     window = SDL_CreateWindow("Demo",
77         SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
78         window_width, window_height,
79         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
80     glctx  = SDL_GL_CreateContext(window);
81     
82     // Print SDL version
83     SDL_version  sdlverconf = void, sdlverrt = void;
84     SDL_VERSION(&sdlverconf);
85     SDL_GetVersion(&sdlverrt);
86     printf("* SDL_VERSION : %u.%u.%u configured, %u.%u.%u running\n",
87         sdlverconf.major, sdlverconf.minor, sdlverconf.patch,
88         sdlverrt.major, sdlverrt.minor, sdlverrt.patch);
89     
90     // OpenGL setup
91     printf("* CONFIG      : "~CONFIGURATION~"\n");
92     initiate_renderer();
93     printf("* GL_RENDERER : %s\n", glGetString(GL_RENDERER));
94     printf("* GL_VERSION  : %s\n", glGetString(GL_VERSION));
95     
96     // Init UI
97     mu_init(&uictx);
98     uictx.text_width  = &text_width;
99     uictx.text_height = &text_height;
100     
101     stopwatch_t.setup();
102     
103     GAME: while (true)
104     {
105         // Transmit SDL input events to UI
106         SDL_Event e = void;
107         sw_input.start;
108         while (SDL_PollEvent(&e))
109         {
110             switch (e.type)
111             {
112                 case SDL_QUIT: break GAME;
113                 case SDL_MOUSEMOTION:
114                     mu_input_mousemove(&uictx, e.motion.x, e.motion.y);
115                     continue;
116                 case SDL_MOUSEWHEEL:
117                     mu_input_scroll(&uictx, 0, e.wheel.y * -30);
118                     continue;
119                 case SDL_TEXTINPUT:
120                     mu_input_text(&uictx, e.text.text.ptr);
121                     continue;
122 
123                 case SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP:
124                     int b = button_map[e.button.button & 0xff];
125                     if (!b) continue;
126                     switch (e.type) {
127                     case SDL_MOUSEBUTTONDOWN:
128                         mu_input_mousedown(&uictx, e.button.x, e.button.y, b);
129                         continue;
130                     case SDL_MOUSEBUTTONUP:
131                         mu_input_mouseup(&uictx, e.button.x, e.button.y, b);
132                         continue;
133                     default: continue;
134                     }
135 
136                 case SDL_KEYDOWN, SDL_KEYUP:
137                     int k = key_map[e.key.keysym.sym & 0xff];
138                     if (!k) continue;
139                     switch (e.type) {
140                     case SDL_KEYDOWN: mu_input_keydown(&uictx, k); continue;
141                     case SDL_KEYUP:   mu_input_keyup(&uictx, k);   continue;
142                     default:
143                     }
144                     continue;
145                 
146                 default:
147             }
148         }
149         sw_input.stop;
150         
151         // Process UI
152         sw_ui.stop;
153         mu_begin(&uictx);
154         if (cli_debug)
155         {
156             debug_window(&uictx);
157             log_window(&uictx);
158             style_window(&uictx);
159         }
160         else
161         {
162             log_window(&uictx);
163             test_window(&uictx);
164             style_window(&uictx);
165         }
166         mu_end(&uictx);
167         sw_ui.start;
168         
169         // Clear screen and process rendering commands from UI
170         stat_commands = uictx.command_list.idx;
171         stat_id       = uictx.id_stack.idx;
172         sw_commands.start;
173         r_clear(mu_Color(cast(ubyte)bg[0], cast(ubyte)bg[1], cast(ubyte)bg[2], 255));
174         foreach (ref mu_Command cmd ; mu_command_range(&uictx))
175         {
176             switch (cmd.type)
177             {
178                 case MU_COMMAND_TEXT: r_draw_text(cmd.text.str.ptr, cmd.text.pos, cmd.text.color); continue;
179                 case MU_COMMAND_RECT: r_draw_rect(cmd.rect.rect, cmd.rect.color); continue;
180                 case MU_COMMAND_ICON: r_draw_icon(cmd.icon.id, cmd.icon.rect, cmd.icon.color); continue;
181                 case MU_COMMAND_CLIP: r_set_clip_rect(cmd.clip.rect); continue;
182                 default: continue;
183             }
184         }
185         sw_commands.stop;
186     
187         // Render screen
188         sw_render.start;
189         r_present();
190         sw_render.stop;
191     }
192     
193     destroy_renderer();
194     SDL_GL_DeleteContext(glctx);
195     SDL_DestroyWindow(window);
196     SDL_Quit();
197 }
198 
199 private:
200 
201 __gshared stopwatch_t sw_input;
202 __gshared stopwatch_t sw_ui;
203 __gshared stopwatch_t sw_commands;
204 __gshared stopwatch_t sw_render;
205 
206 __gshared size_t stat_commands;
207 __gshared size_t stat_id;
208 
209 void debug_window(mu_Context *ctx)
210 {
211     if (mu_begin_window(ctx, "Debug", mu_Rect(40, 40, 300, 450)))
212     {
213         if (mu_header_ex(ctx, "Times", MU_OPT_EXPANDED))
214         {
215             enum bufsz = 32;
216             char[bufsz] buf = void;
217             static immutable const(char)* fmt = "%.3f ms";
218             
219             static immutable int[2] cols = [ 80, -1 ];
220             mu_layout_row(ctx, 2, cols.ptr, 0);
221             
222             snprintf(buf.ptr, bufsz, fmt, sw_input.ms);
223             mu_label(ctx, "Input:");
224             mu_label(ctx, buf.ptr);
225             
226             snprintf(buf.ptr, bufsz, fmt, sw_ui.ms);
227             mu_label(ctx, "UI:");
228             mu_label(ctx, buf.ptr);
229             
230             snprintf(buf.ptr, bufsz, fmt, sw_commands.ms);
231             mu_label(ctx, "Commands:");
232             mu_label(ctx, buf.ptr);
233             
234             snprintf(buf.ptr, bufsz, fmt, sw_render.ms);
235             mu_label(ctx, "Render:");
236             mu_label(ctx, buf.ptr);
237         }
238         
239         if (mu_header_ex(ctx, "Stats", MU_OPT_EXPANDED))
240         {
241             enum bufsz = 32;
242             char[bufsz] buf = void;
243             
244             static immutable int[2] cols = [ 80, -1 ];
245             mu_layout_row(ctx, 2, cols.ptr, 0);
246             
247             snprintf(buf.ptr, 32, "%zu", stat_commands);
248             mu_label(ctx, "Commands:");
249             mu_label(ctx, buf.ptr);
250             
251             snprintf(buf.ptr, 32, "%zu", stat_id);
252             mu_label(ctx, "IDs:");
253             mu_label(ctx, buf.ptr);
254         }
255         
256         mu_end_window(ctx);
257     }
258 }
259 
260 void test_window(mu_Context *ctx)
261 {
262     if (mu_begin_window(ctx, "Demo Window", mu_Rect(40, 40, 300, 450)))
263     {
264         mu_Container *win = mu_get_current_container(ctx);
265         win.rect.w = mu_max(win.rect.w, 240);
266         win.rect.h = mu_max(win.rect.h, 300);
267         
268         // window info
269         if (mu_header(ctx, "Window Info"))
270         {
271             mu_Container *win2 = mu_get_current_container(ctx);
272             char[64] buf = void;
273             static immutable int[2] cols = [ 54, -1 ];
274             mu_layout_row(ctx, 2, cols.ptr, 0);
275             mu_label(ctx, "Position:");
276             sprintf(buf.ptr, "%d, %d", win2.rect.x, win2.rect.y); mu_label(ctx, buf.ptr);
277             mu_label(ctx, "Size:");
278             sprintf(buf.ptr, "%d, %d", win2.rect.w, win2.rect.h); mu_label(ctx, buf.ptr);
279         }
280         
281         // labels + buttons
282         if (mu_header_ex(ctx, "Test Buttons", MU_OPT_EXPANDED))
283         {
284             static immutable int[3] cols = [ 86, -110, -1 ];
285             mu_layout_row(ctx, 3, cols.ptr, 0);
286             mu_label(ctx, "Test buttons 1:");
287             if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); }
288             if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); }
289             mu_label(ctx, "Test buttons 2:");
290             if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); }
291             if (mu_button(ctx, "Popup")) { mu_open_popup(ctx, "Test Popup"); }
292             if (mu_begin_popup(ctx, "Test Popup")) {
293                 mu_button(ctx, "Hello");
294                 mu_button(ctx, "World");
295                 mu_end_popup(ctx);
296             }
297         }
298         
299         // tree
300         if (mu_header_ex(ctx, "Tree and Text", MU_OPT_EXPANDED))
301         {
302             static immutable int[2] cols1 = [ 140, -1 ];
303             mu_layout_row(ctx, 2, cols1.ptr, 0);
304             mu_layout_begin_column(ctx);
305             if (mu_begin_treenode(ctx, "Test 1")) {
306                 if (mu_begin_treenode(ctx, "Test 1a")) {
307                     mu_label(ctx, "Hello");
308                     mu_label(ctx, "world");
309                     mu_end_treenode(ctx);
310                 }
311                 if (mu_begin_treenode(ctx, "Test 1b")) {
312                     if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); }
313                     if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); }
314                     mu_end_treenode(ctx);
315                 }
316                 mu_end_treenode(ctx);
317             }
318             if (mu_begin_treenode(ctx, "Test 2"))
319             {
320                 static immutable int[2] cols2 = [ 54, 54 ];
321                 mu_layout_row(ctx, 2, cols2.ptr, 0);
322                 if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); }
323                 if (mu_button(ctx, "Button 4")) { write_log("Pressed button 4"); }
324                 if (mu_button(ctx, "Button 5")) { write_log("Pressed button 5"); }
325                 if (mu_button(ctx, "Button 6")) { write_log("Pressed button 6"); }
326                 mu_end_treenode(ctx);
327             }
328             if (mu_begin_treenode(ctx, "Test 3"))
329             {
330                 __gshared int[3] checks = [ 1, 0, 1 ]; // Mutable
331                 mu_checkbox(ctx, "Checkbox 1", &checks[0]);
332                 mu_checkbox(ctx, "Checkbox 2", &checks[1]);
333                 mu_checkbox(ctx, "Checkbox 3", &checks[2]);
334                 mu_end_treenode(ctx);
335             }
336             mu_layout_end_column(ctx);
337             
338             mu_layout_begin_column(ctx);
339             static immutable int[1] cols3 = [ -1 ];
340             mu_layout_row(ctx, 1, cols3.ptr, 0);
341             mu_text(ctx, 
342                 "Lorem ipsum dolor sit amet, consectetur adipiscing "~
343                 "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus "~
344                 "ipsum, eu varius magna felis a nulla.");
345             mu_layout_end_column(ctx);
346         }
347         
348         /* background color sliders */
349         if (mu_header_ex(ctx, "Background Color", MU_OPT_EXPANDED))
350         {
351             static immutable int[2] cols4 = [ -78, -1 ];
352             mu_layout_row(ctx, 2, cols4.ptr, 74);
353             // sliders
354             mu_layout_begin_column(ctx);
355             static immutable int[2] cols5 = [ 46, -1 ];
356             mu_layout_row(ctx, 2, cols5.ptr, 0);
357             mu_label(ctx, "Red:");   mu_slider(ctx, &bg[0], 0, 255);
358             mu_label(ctx, "Green:"); mu_slider(ctx, &bg[1], 0, 255);
359             mu_label(ctx, "Blue:");  mu_slider(ctx, &bg[2], 0, 255);
360             mu_layout_end_column(ctx);
361             // color preview
362             mu_Rect r = mu_layout_next(ctx);
363             mu_draw_rect(ctx, r,
364                 mu_Color(cast(ubyte)bg[0], cast(ubyte)bg[1], cast(ubyte)bg[2], 255));
365             char[32] buf = void;
366             sprintf(buf.ptr, "#%02X%02X%02X",
367                 cast(int)bg[0], cast(int)bg[1], cast(int)bg[2]);
368             mu_draw_control_text(ctx, buf.ptr, r, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER);
369         }
370 
371         mu_end_window(ctx);
372     }
373 }
374 
375 int uint8_slider(mu_Context *ctx, ubyte *value, int low, int high)
376 {
377     mu_push_id(ctx, &value, value.sizeof);
378     float tmp = *value;
379     int res = mu_slider_ex(ctx, &tmp, low, high, 0, "%.0f", MU_OPT_ALIGNCENTER);
380     *value = cast(ubyte)tmp;
381     mu_pop_id(ctx);
382     return res;
383 }
384 
385 struct color_t
386 {
387     const(char) *label;
388     int idx;
389 }
390 
391 immutable color_t[] colors = [
392     { "text",         MU_COLOR_TEXT        },
393     { "border",       MU_COLOR_BORDER      },
394     { "windowbg",     MU_COLOR_WINDOWBG    },
395     { "titlebg",      MU_COLOR_TITLEBG     },
396     { "titletext",    MU_COLOR_TITLETEXT   },
397     { "panelbg",      MU_COLOR_PANELBG     },
398     { "button",       MU_COLOR_BUTTON      },
399     { "buttonhover",  MU_COLOR_BUTTONHOVER },
400     { "buttonfocus",  MU_COLOR_BUTTONFOCUS },
401     { "base",         MU_COLOR_BASE        },
402     { "basehover",    MU_COLOR_BASEHOVER   },
403     { "basefocus",    MU_COLOR_BASEFOCUS   },
404     { "scrollbase",   MU_COLOR_SCROLLBASE  },
405     { "scrollthumb",  MU_COLOR_SCROLLTHUMB },
406     { null }
407 ];
408 
409 void style_window(mu_Context *ctx)
410 {
411     if (mu_begin_window(ctx, "Style Editor", mu_Rect(350, 250, 300, 240))) 
412     {
413         int sw = cast(int)(mu_get_current_container(ctx).body_.w * 0.14f); // ~1/5
414         int[6] r = [ 80, sw, sw, sw, sw, -1 ];
415         mu_layout_row(ctx, 6, r.ptr, 0);
416         for (size_t i; colors[i].label; ++i)
417         {
418             mu_label(ctx, colors[i].label);
419             uint8_slider(ctx, &ctx.style.colors[i].r, 0, 255);
420             uint8_slider(ctx, &ctx.style.colors[i].g, 0, 255);
421             uint8_slider(ctx, &ctx.style.colors[i].b, 0, 255);
422             uint8_slider(ctx, &ctx.style.colors[i].a, 0, 255);
423             mu_draw_rect(ctx, mu_layout_next(ctx), ctx.style.colors[i]);
424         }
425         mu_end_window(ctx);
426     }
427 }
428 
429 __gshared char[64000] logbuf = 0; // char.init == 0xff, confuses strlen
430 __gshared      size_t logi;
431 __gshared         int logbuf_updated = 0;
432 __gshared    float[3] bg = [ 90, 95, 100 ];
433 
434 void write_log(const(char) *text)
435 {
436     size_t l = strlen(text);
437     if (l == 0) return;
438     memcpy(logbuf.ptr + logi, text, l);
439     logi += l;
440     logbuf[logi] = '\n';
441     logbuf[++logi] = 0;
442     logbuf_updated = 1;
443 }
444 
445 void log_window(mu_Context *ctx)
446 {
447     if (mu_begin_window(ctx, "Console", mu_Rect(350, 40, 300, 200)))
448     {
449         /* output text panel */
450         static immutable int[1] cols1 = [ -1 ]; // (int[]) { -1 }
451         mu_layout_row(ctx, 1, cols1.ptr, -25);
452         mu_begin_panel(ctx, "Log Output");
453         mu_Container *panel = mu_get_current_container(ctx);
454         mu_layout_row(ctx, 1, cols1.ptr, -1);
455         mu_text(ctx, logbuf.ptr);
456         mu_end_panel(ctx);
457         if (logbuf_updated) {
458             panel.scroll.y = panel.content_size.y;
459             logbuf_updated = 0;
460         }
461         
462         /* input textbox + submit button */
463         static immutable int[2] cols2 = [ -70, -1 ];
464         __gshared char[128] buf = [ 0 ]; // textbox buffer
465         int submitted = 0;
466         mu_layout_row(ctx, 2, cols2.ptr, 0);
467         if (mu_textbox(ctx, buf.ptr, 128) & MU_RES_SUBMIT) {
468             mu_set_focus(ctx, ctx.last_id);
469             submitted = 1;
470         }
471         if (mu_button(ctx, "Submit")) { submitted = 1; }
472         if (submitted) {
473             write_log(buf.ptr);
474             buf[0] = '\0';
475         }
476         
477         mu_end_window(ctx);
478     }
479 }
480 
481 /// SDL-DDUI mouse button mapping
482 immutable const(ubyte)[256] button_map = [
483     SDL_BUTTON_LEFT   & 0xff :  MU_MOUSE_LEFT,
484     SDL_BUTTON_RIGHT  & 0xff :  MU_MOUSE_RIGHT,
485     SDL_BUTTON_MIDDLE & 0xff :  MU_MOUSE_MIDDLE,
486 ];
487 
488 /// SDL-DDUI keyboard key mapping
489 immutable const(ubyte)[256] key_map = [
490     SDLK_LSHIFT       & 0xff : MU_KEY_SHIFT,
491     SDLK_RSHIFT       & 0xff : MU_KEY_SHIFT,
492     SDLK_LCTRL        & 0xff : MU_KEY_CTRL,
493     SDLK_RCTRL        & 0xff : MU_KEY_CTRL,
494     SDLK_LALT         & 0xff : MU_KEY_ALT,
495     SDLK_RALT         & 0xff : MU_KEY_ALT,
496     SDLK_RETURN       & 0xff : MU_KEY_RETURN,
497     SDLK_KP_ENTER     & 0xff : MU_KEY_RETURN,
498     SDLK_BACKSPACE    & 0xff : MU_KEY_BACKSPACE,
499 ];
500 
501 int text_width(mu_Font font, const(char) *text, int len)
502 {
503     if (len == -1) { len = cast(int)strlen(text); }
504     return r_get_text_width(text, len);
505 }
506 
507 int text_height(mu_Font font)
508 {
509     return r_get_text_height();
510 }