1 module examples.sdl; 2 3 import std.datetime : Clock; 4 import arsd.nanovega; 5 import nanogui.sdlbackend : SdlBackend; 6 import nanogui.widget : Widget; 7 import nanogui.glcanvas : GLCanvas; 8 9 struct Vertex 10 { 11 import nanogui.common; 12 Vector3f position; 13 Vector3f color; 14 } 15 16 extern(C) 17 uint timer_callback(uint interval, void *param) nothrow 18 { 19 import gfm.sdl2; 20 21 SDL_Event event; 22 SDL_UserEvent userevent; 23 24 userevent.type = SDL_USEREVENT; 25 userevent.code = 0; 26 userevent.data1 = null; 27 userevent.data2 = null; 28 29 event.type = SDL_USEREVENT; 30 event.user = userevent; 31 32 SDL_PushEvent(&event); 33 return(interval); 34 } 35 36 class MyGlCanvas : GLCanvas 37 { 38 import std.typecons : scoped; 39 import gfm.opengl; 40 import gfm.math; 41 import nanogui.common; 42 43 this(Widget parent, OpenGL gl) 44 { 45 super(parent); 46 47 _gl = gl; 48 49 const program_source = 50 "#version 130 51 52 #if VERTEX_SHADER 53 uniform mat4 modelViewProj; 54 in vec3 position; 55 in vec3 color; 56 out vec4 frag_color; 57 void main() { 58 frag_color = modelViewProj * vec4(0.5 * color, 1.0); 59 gl_Position = modelViewProj * vec4(position / 2, 1.0); 60 } 61 #endif 62 63 #if FRAGMENT_SHADER 64 out vec4 color; 65 in vec4 frag_color; 66 void main() { 67 color = frag_color; 68 } 69 #endif"; 70 71 _program = new GLProgram(_gl, program_source); 72 assert(_program); 73 auto vert_spec = scoped!(VertexSpecification!Vertex)(_program); 74 _rotation = Vector3f(0.25f, 0.5f, 0.33f); 75 76 int[12*3] indices = 77 [ 78 0, 1, 3, 79 3, 2, 1, 80 3, 2, 6, 81 6, 7, 3, 82 7, 6, 5, 83 5, 4, 7, 84 4, 5, 1, 85 1, 0, 4, 86 4, 0, 3, 87 3, 7, 4, 88 5, 6, 2, 89 2, 1, 5, 90 ]; 91 92 auto vertices = 93 [ 94 Vertex(Vector3f(-1, 1, 1), Vector3f(1, 0, 0)), 95 Vertex(Vector3f(-1, 1, -1), Vector3f(0, 1, 0)), 96 Vertex(Vector3f( 1, 1, -1), Vector3f(1, 1, 0)), 97 Vertex(Vector3f( 1, 1, 1), Vector3f(0, 0, 1)), 98 Vertex(Vector3f(-1, -1, 1), Vector3f(1, 0, 1)), 99 Vertex(Vector3f(-1, -1, -1), Vector3f(0, 1, 1)), 100 Vertex(Vector3f( 1, -1, -1), Vector3f(1, 1, 1)), 101 Vertex(Vector3f( 1, -1, 1), Vector3f(0.5, 0.5, 0.5)), 102 ]; 103 104 auto vbo = scoped!GLBuffer(gl, GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices); 105 auto ibo = scoped!GLBuffer(gl, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, indices); 106 107 _vao = scoped!GLVAO(gl); 108 // prepare VAO 109 { 110 _vao.bind(); 111 vbo.bind(); 112 ibo.bind(); 113 vert_spec.use(); 114 _vao.unbind(); 115 } 116 117 { 118 import gfm.sdl2 : SDL_AddTimer; 119 uint delay = 40; 120 _timer_id = SDL_AddTimer(delay, &timer_callback, null); 121 } 122 } 123 124 ~this() 125 { 126 import gfm.sdl2 : SDL_RemoveTimer; 127 SDL_RemoveTimer(_timer_id); 128 } 129 130 override void drawGL() 131 { 132 static long start_time; 133 mat4f mvp; 134 mvp = mat4f.identity; 135 136 if (start_time == 0) 137 start_time = Clock.currTime.stdTime; 138 139 auto angle = (Clock.currTime.stdTime - start_time)/10_000_000.0; 140 mvp = mvp.rotation(angle, _rotation); 141 142 GLboolean depth_test_enabled; 143 glGetBooleanv(GL_DEPTH_TEST, &depth_test_enabled); 144 if (!depth_test_enabled) 145 glEnable(GL_DEPTH_TEST); 146 scope(exit) 147 { 148 if (!depth_test_enabled) 149 glDisable(GL_DEPTH_TEST); 150 } 151 152 _program.uniform("modelViewProj").set(mvp); 153 _program.use(); 154 scope(exit) _program.unuse(); 155 156 _vao.bind(); 157 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, cast(void *) 0); 158 _vao.unbind(); 159 } 160 161 private: 162 OpenGL _gl; 163 GLProgram _program; 164 Vector3f _rotation; 165 166 import gfm.sdl2 : SDL_TimerID; 167 SDL_TimerID _timer_id; 168 169 import std.typecons : scoped; 170 import gfm.opengl : GLVAO; 171 172 alias ScopedGLVAO = typeof(scoped!GLVAO(OpenGL.init)); 173 ScopedGLVAO _vao; 174 } 175 176 class MyGui : SdlBackend 177 { 178 this(int w, int h, string title) 179 { 180 super(w, h, title); 181 } 182 183 override void onVisibleForTheFirstTime() 184 { 185 import nanogui.screen : Screen; 186 import nanogui.widget, nanogui.theme, nanogui.checkbox, nanogui.label, 187 nanogui.common, nanogui.window, nanogui.layout, nanogui.button, 188 nanogui.popupbutton, nanogui.entypo, nanogui.popup, nanogui.vscrollpanel, 189 nanogui.combobox, nanogui.textbox; 190 191 { 192 auto window = new Window(screen, "Button demo"); 193 window.position(Vector2i(15, 15)); 194 window.size = Vector2i(screen.size.x - 30, screen.size.y - 30); 195 window.layout(new GroupLayout()); 196 197 new Label(window, "Push buttons", "sans-bold"); 198 199 auto checkbox = new CheckBox(window, "Checkbox #1", null); 200 checkbox.position = Vector2i(100, 190); 201 checkbox.size = checkbox.preferredSize(nvg); 202 checkbox.checked = true; 203 204 auto label = new Label(window, "Label"); 205 label.position = Vector2i(100, 300); 206 label.size = label.preferredSize(nvg); 207 208 Popup popup; 209 210 auto btn = new Button(window, "Button"); 211 btn.callback = () { 212 popup.children[0].visible = !popup.children[0].visible; 213 label.caption = popup.children[0].visible ? 214 "Popup label is visible" : "Popup label isn't visible"; 215 }; 216 217 auto popupBtn = new PopupButton(window, "PopupButton", Entypo.ICON_EXPORT); 218 popup = popupBtn.popup; 219 popup.layout(new GroupLayout()); 220 new Label(popup, "Arbitrary widgets can be placed here"); 221 new CheckBox(popup, "A check box", null); 222 223 window.tooltip = "Button demo tooltip"; 224 } 225 226 { 227 auto window = new Window(screen, "Button group example"); 228 window.position(Vector2i(220, 15)); 229 window.layout(new GroupLayout()); 230 231 auto buttonGroup = ButtonGroup(); 232 233 auto btn = new Button(window, "RadioButton1"); 234 btn.flags = Button.Flags.RadioButton; 235 btn.buttonGroup = buttonGroup; 236 btn.tooltip = "Radio button ONE"; 237 buttonGroup ~= btn; 238 239 btn = new Button(window, "RadioButton2"); 240 btn.flags = Button.Flags.RadioButton; 241 btn.buttonGroup = buttonGroup; 242 btn.tooltip = "Radio button TWO"; 243 buttonGroup ~= btn; 244 245 btn = new Button(window, "RadioButton3"); 246 btn.flags = Button.Flags.RadioButton; 247 btn.buttonGroup = buttonGroup; 248 btn.tooltip = "Radio button THREE"; 249 buttonGroup ~= btn; 250 251 window.tooltip = "Radio button group tooltip"; 252 } 253 254 { 255 auto window = new Window(screen, "Button with image window"); 256 window.position(Vector2i(400, 15)); 257 window.layout(new GroupLayout()); 258 259 auto image = nvg.createImage("resources/icons/start.jpeg", [NVGImageFlags.ClampToBorderX, NVGImageFlags.ClampToBorderY]); 260 auto btn = new Button(window, "Start", image); 261 // some optional height, not font size, not icon height 262 btn.fixedHeight = 130; 263 264 // yet another Button with the same image but default size 265 new Button(window, "Start", image); 266 267 window.tooltip = "Window with button that has image as an icon"; 268 } 269 270 { 271 auto window = new Window(screen, "Combobox window"); 272 window.position(Vector2i(600, 15)); 273 window.layout(new GroupLayout()); 274 275 new Label(window, "Message dialog", "sans-bold"); 276 import std.algorithm : map; 277 import std.range : iota; 278 import std.array : array; 279 import std.conv : text; 280 auto items = 15.iota.map!(a=>text("items", a)).array; 281 auto cb = new ComboBox(window, items); 282 cb.cursor = Cursor.Hand; 283 cb.tooltip = "This widget has custom cursor value - Cursor.Hand"; 284 285 window.tooltip = "Window with ComboBox tooltip"; 286 } 287 288 { 289 int width = 400; 290 int half_width = width / 2; 291 int height = 200; 292 293 auto window = new Window(screen, "All Icons"); 294 window.position(Vector2i(0, 400)); 295 window.fixedSize(Vector2i(width, height)); 296 297 // attach a vertical scroll panel 298 auto vscroll = new VScrollPanel(window); 299 vscroll.fixedSize(Vector2i(width, height)); 300 301 // vscroll should only have *ONE* child. this is what `wrapper` is for 302 auto wrapper = new Widget(vscroll); 303 wrapper.fixedSize(Vector2i(width, height)); 304 wrapper.layout(new GridLayout());// defaults: 2 columns 305 306 foreach(i; 0..100) 307 { 308 import std.conv : text; 309 auto item = new Button(wrapper, "item" ~ i.text, Entypo.ICON_AIRCRAFT_TAKE_OFF); 310 item.iconPosition(Button.IconPosition.Left); 311 item.fixedWidth(half_width); 312 } 313 } 314 315 { 316 auto asian_theme = new Theme(nvg); 317 318 { 319 // sorta hack because loading font in nvg results in 320 // conflicting font id 321 auto nvg2 = nvgCreateContext(NVGContextFlag.Debug); 322 scope(exit) nvg2.kill; 323 nvg2.createFont("chihaya", "./resources/fonts/n_chihaya_font.ttf"); 324 nvg.addFontsFrom(nvg2); 325 asian_theme.mFontNormal = nvg.findFont("chihaya"); 326 } 327 328 auto window = new Window(screen, "Textbox window"); 329 window.position = Vector2i(750, 15); 330 window.fixedSize = Vector2i(200, 350); 331 window.layout(new GroupLayout()); 332 window.tooltip = "Window with TextBoxes"; 333 334 auto tb = new TextBox(window, "Россия"); 335 tb.editable = true; 336 337 tb = new TextBox(window, "England"); 338 tb.editable = true; 339 340 tb = new TextBox(window, "日本"); 341 tb.theme = asian_theme; 342 tb.editable = true; 343 344 tb = new TextBox(window, "中国"); 345 tb.theme = asian_theme; 346 tb.editable = true; 347 } 348 349 { 350 auto window = new Window(screen, "GLCanvas Demo"); 351 window.position = Vector2i(450, 400); 352 window.layout = new GroupLayout(); 353 auto glcanvas = new MyGlCanvas(window, gl); 354 glcanvas.size = Vector2i(300, 300); 355 glcanvas.backgroundColor = Color(0.1f, 0.1f, 0.1f, 1.0f); 356 } 357 358 // now we should do layout manually yet 359 screen.performLayout(nvg); 360 } 361 } 362 363 void main () { 364 365 auto gui = new MyGui(1000, 800, "Nanogui using SDL2 backend"); 366 gui.run(); 367 }