├── .gitignore ├── README.md ├── docks_and_divs.md ├── example ├── example.c ├── example2.c ├── example3.c ├── fonts │ ├── Roboto-Bold.ttf │ ├── Roboto-Italic.ttf │ └── Roboto-Regular.ttf └── icons │ ├── README.md │ ├── arrow-combo.svg │ ├── cancel-circled.svg │ ├── check.svg │ ├── plus.svg │ ├── search.svg │ └── tools.svg ├── flux.txt ├── idea.md ├── lib ├── nanosvg │ └── nanosvg.h └── nanovg │ ├── fontstash.h │ ├── glnanovg.h │ ├── nanovg.c │ ├── nanovg.h │ ├── nanovg_gl.h │ ├── stb_image.c │ └── stb_truetype.h ├── premake4.lua ├── src ├── mgui.c ├── mgui.h ├── milli.c ├── milli.h ├── milli2.c └── milli2.h └── todo.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Compiled source # 2 | *.com 3 | *.class 4 | *.dll 5 | *.exe 6 | *.o 7 | *.so 8 | test 9 | 10 | ## Logs and databases # 11 | *.log 12 | *.sql 13 | *.sqlite 14 | 15 | ## OS generated files # 16 | .DS_Store 17 | .DS_Store? 18 | ._* 19 | .Spotlight-V100 20 | .Trashes 21 | ehthumbs.db 22 | Thumbs.db 23 | 24 | ## Build dir 25 | build/* 26 | backup/* 27 | 28 | ## xcode specific 29 | *xcuserdata* 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yet another imgui library in the making... -------------------------------------------------------------------------------- /docks_and_divs.md: -------------------------------------------------------------------------------- 1 | Docks and Divs 2 | ============== 3 | 4 | Here's what we try to do. 5 | ``` 6 | +----------------------+ 7 | | Materials | 8 | | __________________ | 9 | |?(__________________)x| 10 | |----------------------| 11 | | /¨\ #| 12 | |( ) Gold #| 13 | |( ) Metal #| 14 | | \_/ #| 15 | |.....................#| 16 | | /¨\ #| 17 | |( ) Matte Plastic #| 18 | |( ) Phong #| 19 | | \_/ | 20 | |......................| 21 | | /¨\ | 22 | |( ) Glass | 23 | |----------------------| 24 | | (+Add) (Remove) | 25 | +----------------------+ 26 | ``` 27 | 28 | Here's how we do it: 29 | ```C 30 | miPanelBegin(50,50, 250,450); 31 | 32 | // Header 33 | miDockBegin(MI_TOP_BOTTOM); 34 | miText("Materials"); 35 | MIhandle searchInput, searchClear; 36 | float cols[3] = {MI_ICON_WIDTH, MI_FLEX, MI_ICON_WIDTH}; 37 | miDivsBegin(MI_LEFT_RIGHT, 3, cols); 38 | miRowHeight(MI_INPUT_HEIGHT); 39 | miIcon("search"); 40 | searchInput = miInput(searchText, sizeof(searchText)); 41 | searchClear = miIcon("cross"); 42 | miDivsEnd(); 43 | if (miChanged(searchInput)) 44 | setFilter(filter, searchText); 45 | if (miClicked(searchClear)) 46 | strcpy(searchText, ""); 47 | miDockEnd(); 48 | 49 | // Footer 50 | miDockBegin(MI_BOTTOM_TOP); 51 | MIhandle add, del; 52 | float cols2[3] = {MI_FLEX, MI_ICONBUTTON_WIDTH, MI_BUTTON_WIDTH}; 53 | miDivsBegin(MI_LEFT_RIGHT, 3, cols2); 54 | miRowHeight(MI_BUTTON_HEIGHT); 55 | miSpacer(); 56 | add = miIconButton("Add”, "plus"); 57 | del = miButton("Delete"); 58 | miDivsEnd(); 59 | if (miClicked(add)) { 60 | selected = addMaterial(); 61 | } 62 | if (miClicked(add)) { 63 | if (selected != -1) 64 | deleteMaterial(selected); 65 | selected = -1; 66 | } 67 | 68 | miDockEnd(); 69 | 70 | // Scroll view 71 | miDockBegin(MI_FILLY); 72 | miOverflow(MI_SCROLL); 73 | for (i = 0; i < materialCount; i++) { 74 | Material* mat = materials[i]; 75 | if (!passFilter(filter, mat->name)) 76 | continue; 77 | // Material 78 | miLayoutBegin(MI_LEFT_RIGHT); 79 | MIhandle item = miSelectableBegin(i == selected); 80 | miRowHeight(MI_THUMBNAIL_SIZE); 81 | miThumbnail(material->image); 82 | float rows[4] = {MI_FLEX, MI_TEXT_HEIGHT, MI_LABEL_HEIGHT, MI_FLEX}; 83 | miDivsBegin(MI_TOP_BOTTOM, 4, rows); 84 | miSpacer(); 85 | miText(mat->name); 86 | miText(mat->type); 87 | miDivsEnd(); 88 | miSelectableEnd(); 89 | if (miClicked(item)) { 90 | selected = i; 91 | setMaterialPreview(mat); 92 | } 93 | miLayoutEnd(); 94 | } 95 | miDockEnd(); 96 | 97 | miPanelEnd(); 98 | ``` 99 | 100 | 101 | Docks 102 | ----- 103 | 104 | A Dock arranges widgets in specified direction starting from the edge of the free space. Widgets are packed along the specified direction, and the space allocation stretches as new widgets are added. Each widget is given full width on the cross direction of the layout. 105 | 106 | Let's take a look at the main structure of the dialog. 107 | 108 | ```C 109 | // Header 110 | miDockBegin(MI_TOP_BOTTOM); 111 | ... 112 | miDockEnd(); 113 | 114 | // Footer 115 | miDockBegin(MI_BOTTOM_TOP); 116 | ... 117 | miDockEnd(); 118 | 119 | // Scroll view 120 | miDockBegin(MI_FILLY); 121 | ... 122 | miDockEnd(); 123 | ``` 124 | 125 | This divides the panel as follows: 126 | ``` 127 | +-----------+ 128 | | header v | 129 | |...........| 130 | | scroll | 131 | | | 132 | |...........| 133 | | footer ^ | 134 | +-----------+ 135 | ``` 136 | Initially the free space is the whole dialog. Header is docked at the top of the free space and widgets inside the header flow down, each widget taking a space that is full width of the available space and the height is determined by the widget content size. On `miDockEnd()`, the free space of the parent dialog is shrank based on the contents in the header. This space is available on further docks. 137 | 138 | Footer is handled the same way, but the widgets grow upwards. 139 | 140 | If we used `MI_LEFT_RIGHT` on the footer, it would look on high level like this instead: 141 | 142 | ``` 143 | +-----------+ 144 | | header v | 145 | |...........| 146 | |foo: scroll| 147 | |> : | 148 | | : | 149 | | : | 150 | +-----------+ 151 | ``` 152 | 153 | 154 | Divs 155 | ---- 156 | 157 | Divs divide the remaining free space into rows or columns; or divs. Widgets are assigned to one div at a time. When all the divs are full, new line is formed and divs filled again in order. The size of the divs can be fixed, or you can use `MI_FLEX` in which case the size is the remaining space divided equally across all flex elements. The div sizes are adjusted so that they always take the full space available. 158 | 159 | ```C 160 | float cols2[3] = {MI_FLEX, MI_ICONBUTTON_WIDTH, MI_BUTTON_WIDTH}; 161 | miDivsBegin(MI_LEFT_RIGHT, 3, cols2); 162 | miRowHeight(MI_BUTTON_HEIGHT); 163 | miSpacer(); 164 | miIconButton("Add”, “plus”); 165 | miButton("Delete"); 166 | miDivsEnd(); 167 | ``` 168 | ``` 169 | | | 170 | |......................| 171 | | : +Add : Del | 172 | +----------------------+ 173 | : flex : ibut : but : 174 | ``` 175 | In this example the buttons are right aligned using a flex and spacer. Note that by default the divs fill the whole free space horizontally and vertically. You can use row height and column width to limit the size of the row. 176 | 177 | If we added one more button to the above layout, it would look like this: 178 | 179 | ``` 180 | | | 181 | |......................| 182 | | More : | 183 | |......................| 184 | | : +Add : Del | 185 | +----------------------+ 186 | ``` 187 | 188 | Since the parent dock was flowing, up the new line added follows that convention too. 189 | -------------------------------------------------------------------------------- /example/example.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 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 20 | #include 21 | #include 22 | #include 23 | #define GLFW_INCLUDE_GLCOREARB 24 | #include 25 | #include "nanovg.h" 26 | #define NANOVG_GL3_IMPLEMENTATION 27 | #include "nanovg_gl.h" 28 | #include "mgui.h" 29 | #define NANOSVG_IMPLEMENTATION 1 30 | #include "nanosvg.h" 31 | 32 | struct MGinputState input = { 0 }; 33 | 34 | void errorcb(int error, const char* desc) 35 | { 36 | printf("GLFW error %d: %s\n", error, desc); 37 | } 38 | 39 | 40 | static int isPrintable(int key) 41 | { 42 | switch (key) { 43 | case GLFW_KEY_ESCAPE: 44 | case GLFW_KEY_ENTER: 45 | case GLFW_KEY_TAB: 46 | case GLFW_KEY_BACKSPACE: 47 | case GLFW_KEY_INSERT: 48 | case GLFW_KEY_DELETE: 49 | case GLFW_KEY_RIGHT: 50 | case GLFW_KEY_LEFT: 51 | case GLFW_KEY_DOWN: 52 | case GLFW_KEY_UP: 53 | case GLFW_KEY_PAGE_UP: 54 | case GLFW_KEY_PAGE_DOWN: 55 | case GLFW_KEY_HOME: 56 | case GLFW_KEY_END: 57 | case GLFW_KEY_CAPS_LOCK: 58 | case GLFW_KEY_SCROLL_LOCK: 59 | case GLFW_KEY_NUM_LOCK: 60 | case GLFW_KEY_PRINT_SCREEN: 61 | case GLFW_KEY_PAUSE: 62 | case GLFW_KEY_F1: 63 | case GLFW_KEY_F2: 64 | case GLFW_KEY_F3: 65 | case GLFW_KEY_F4: 66 | case GLFW_KEY_F5: 67 | case GLFW_KEY_F6: 68 | case GLFW_KEY_F7: 69 | case GLFW_KEY_F8: 70 | case GLFW_KEY_F9: 71 | case GLFW_KEY_F10: 72 | case GLFW_KEY_F11: 73 | case GLFW_KEY_F12: 74 | case GLFW_KEY_F13: 75 | case GLFW_KEY_F14: 76 | case GLFW_KEY_F15: 77 | case GLFW_KEY_F16: 78 | case GLFW_KEY_F17: 79 | case GLFW_KEY_F18: 80 | case GLFW_KEY_F19: 81 | case GLFW_KEY_F20: 82 | case GLFW_KEY_F21: 83 | case GLFW_KEY_F22: 84 | case GLFW_KEY_F23: 85 | case GLFW_KEY_F24: 86 | case GLFW_KEY_F25: 87 | case GLFW_KEY_KP_ENTER: 88 | case GLFW_KEY_LEFT_SHIFT: 89 | case GLFW_KEY_LEFT_CONTROL: 90 | case GLFW_KEY_LEFT_ALT: 91 | case GLFW_KEY_LEFT_SUPER: 92 | case GLFW_KEY_RIGHT_SHIFT: 93 | case GLFW_KEY_RIGHT_CONTROL: 94 | case GLFW_KEY_RIGHT_ALT: 95 | case GLFW_KEY_RIGHT_SUPER: 96 | case GLFW_KEY_MENU: 97 | return 0; 98 | } 99 | return 1; 100 | } 101 | 102 | int printable = 0; 103 | 104 | static void keycb(GLFWwindow* window, int key, int scancode, int action, int mods) 105 | { 106 | (void)scancode; 107 | (void)mods; 108 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 109 | glfwSetWindowShouldClose(window, GL_TRUE); 110 | if (action == GLFW_PRESS) 111 | printable = isPrintable(key); 112 | 113 | if (action == GLFW_PRESS) { 114 | if (!isPrintable(key)) { 115 | if (input.nkeys < MG_MAX_INPUTKEYS) { 116 | input.keys[input.nkeys].type = MG_KEYPRESSED; 117 | input.keys[input.nkeys].code = key; 118 | input.keys[input.nkeys].mods = mods; 119 | input.nkeys++; 120 | } 121 | } 122 | } 123 | if (action == GLFW_RELEASE) { 124 | if (!isPrintable(key)) { 125 | if (input.nkeys < MG_MAX_INPUTKEYS) { 126 | input.keys[input.nkeys].type = MG_KEYRELEASED; 127 | input.keys[input.nkeys].code = key; 128 | input.keys[input.nkeys].mods = mods; 129 | input.nkeys++; 130 | } 131 | } 132 | } 133 | } 134 | 135 | static void charcb(GLFWwindow* window, unsigned int codepoint) 136 | { 137 | (void)window; 138 | 139 | if (printable) { 140 | if (input.nkeys < MG_MAX_INPUTKEYS) { 141 | input.keys[input.nkeys].type = MG_CHARTYPED; 142 | input.keys[input.nkeys].code = codepoint; 143 | input.keys[input.nkeys].mods = 0; 144 | input.nkeys++; 145 | } 146 | } 147 | } 148 | 149 | static void buttoncb(GLFWwindow* window, int button, int action, int mods) 150 | { 151 | (void)window; 152 | (void)mods; 153 | if (button == GLFW_MOUSE_BUTTON_LEFT ) { 154 | if (action == GLFW_PRESS) { 155 | input.mbut |= MG_MOUSE_PRESSED; 156 | } 157 | if (action == GLFW_RELEASE) { 158 | input.mbut |= MG_MOUSE_RELEASED; 159 | } 160 | } 161 | } 162 | 163 | 164 | static float sqr(float x) { return x*x; } 165 | 166 | int main() 167 | { 168 | GLFWwindow* window; 169 | struct NVGcontext* vg = NULL; 170 | int blending = 0; 171 | float opacity = 0.5f; 172 | float position[3] = {100.0f, 120.0f, 234.0f}; 173 | float color[4] = {255/255.0f,192/255.0f,0/255.0f,255/255.0f}; 174 | float iterations = 42; 175 | int cull = 1; 176 | char name[32] = "Mikko"; 177 | const char* choices[] = { "Normal", "Minimum Color", "Screen Door", "Maximum Velocity" }; 178 | float scroll = 30; 179 | double t = 0; 180 | 181 | printf("start\n"); 182 | 183 | if (!glfwInit()) { 184 | printf("Failed to init GLFW."); 185 | return -1; 186 | } 187 | 188 | glfwSetErrorCallback(errorcb); 189 | 190 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 191 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 192 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 193 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 194 | 195 | window = glfwCreateWindow(1000, 600, "mg", NULL, NULL); 196 | if (!window) { 197 | glfwTerminate(); 198 | return -1; 199 | } 200 | 201 | glfwSetKeyCallback(window, keycb); 202 | glfwSetCharCallback(window, charcb); 203 | glfwSetMouseButtonCallback(window, buttoncb); 204 | 205 | glfwMakeContextCurrent(window); 206 | 207 | vg = nvgCreateGL3(512, 512, NVG_ANTIALIAS); 208 | if (vg == NULL) { 209 | printf("Could not init nanovg.\n"); 210 | return -1; 211 | } 212 | if (nvgCreateFont(vg, "sans", "../example/fonts/Roboto-Regular.ttf") == -1) { 213 | printf("Could not add font italic.\n"); 214 | return -1; 215 | } 216 | if (nvgCreateFont(vg, "sans-bold", "../example/fonts/Roboto-Bold.ttf") == -1) { 217 | printf("Could not add font bold.\n"); 218 | return -1; 219 | } 220 | 221 | mgInit(); 222 | 223 | if (mgCreateIcon("check", "../example/icons/check.svg")) { 224 | printf("Could not create icon 'check'.\n"); 225 | return -1; 226 | } 227 | if (mgCreateIcon("arrow-combo", "../example/icons/arrow-combo.svg")) { 228 | printf("Could not create icon 'arrow-combo'.\n"); 229 | return -1; 230 | } 231 | if (mgCreateIcon("tools", "../example/icons/tools.svg")) { 232 | printf("Could not create icon 'tool'.\n"); 233 | return -1; 234 | } 235 | 236 | 237 | mgCreateStyle("menubar", mgOpts( 238 | mgFillColor(255,255,255,32) 239 | ), mgOpts(), mgOpts(), mgOpts()); 240 | 241 | mgCreateStyle("menu1", mgOpts( 242 | mgAlign(MG_JUSTIFY), 243 | mgPropPosition(MG_START,MG_START,0.0f,1.0f), 244 | mgFillColor(255,255,255,120) 245 | ), mgOpts(), mgOpts(), mgOpts()); 246 | 247 | mgCreateStyle("menu2", mgOpts( 248 | mgAlign(MG_JUSTIFY), 249 | mgPropPosition(MG_START,MG_START,1.0f,0.0f), 250 | mgFillColor(255,255,255,120) 251 | ), mgOpts(), mgOpts(), mgOpts()); 252 | 253 | mgCreateStyle("dialog", mgOpts( 254 | mgFillColor(255,255,255,32), 255 | mgCornerRadius(4) 256 | ), mgOpts(), mgOpts(), mgOpts()); 257 | 258 | glfwSetTime(0); 259 | 260 | while (!glfwWindowShouldClose(window)) 261 | { 262 | double mx, my; 263 | int winWidth, winHeight; 264 | int fbWidth, fbHeight; 265 | float pxRatio; 266 | double tt, dt; 267 | 268 | unsigned int build = 0; 269 | unsigned int fileOpen = 0; 270 | unsigned int file = 0; 271 | unsigned int edit = 0; 272 | unsigned int tools = 0, toolsAlign = 0; 273 | unsigned int view = 0; 274 | 275 | tt = glfwGetTime(); 276 | dt = tt - t; 277 | t = tt; 278 | 279 | // float t = glfwGetTime(); 280 | // float x,y,popy; 281 | // struct MGhit* hit; 282 | 283 | glfwGetCursorPos(window, &mx, &my); 284 | glfwGetWindowSize(window, &winWidth, &winHeight); 285 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 286 | // Calculate pixel ration for hi-dpi devices. 287 | pxRatio = (float)fbWidth / (float)winWidth; 288 | 289 | // Update and render 290 | glViewport(0, 0, fbWidth, fbHeight); 291 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 292 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 293 | glEnable(GL_BLEND); 294 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 295 | glDisable(GL_DEPTH_TEST); 296 | 297 | /* glMatrixMode(GL_PROJECTION); 298 | glLoadIdentity(); 299 | glOrtho(0,width,height,0,-1,1); 300 | 301 | glMatrixMode(GL_MODELVIEW); 302 | glLoadIdentity(); 303 | glDisable(GL_DEPTH_TEST); 304 | glColor4ub(255,255,255,255); 305 | glEnable(GL_BLEND); 306 | glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);*/ 307 | 308 | 309 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio, NVG_STRAIGHT_ALPHA); 310 | 311 | input.mx = mx; 312 | input.my = my; 313 | mgFrameBegin(vg, winWidth, winHeight, &input, dt); 314 | input.nkeys = 0; 315 | input.mbut = 0; 316 | 317 | 318 | /* 319 | 320 | - logic 321 | 322 | - popover / lightbox 323 | - click to activate 324 | - click outside to close (or close with button) 325 | 326 | - menu / popup 327 | - press to activate 328 | - if keep pressing, click in release 329 | - click outside to close (or close with button) 330 | 331 | - sub menu 332 | - hover + wait to activate 333 | - click outside to close (or close with button) 334 | - hover outside the hierarchy to close 335 | 336 | - tooltip 337 | - hover + wait to activate 338 | - exit to close 339 | 340 | 341 | but = mgButton("Popover", mgOpt()); 342 | if (mgLogic(but, MG_TOGGLE, MG_CLICKED, MG_MODAL)) { 343 | mgBeginPopup(but, MG_COL, mgOpt()); 344 | if (mgButton("Press", mgOpt())) { 345 | mgCloseLogic(but); 346 | } 347 | mgEndPopup(); 348 | } 349 | 350 | 351 | but = mgButton("Menu", mgOpt()); 352 | if (mgLogic(but, MG_MENU, MG_PRESSED, MG_CLICKTHROUGH)) { 353 | mgBeginPopup(but, MG_COL, mgOpt()); 354 | if (mgButton("Press", mgOpt())) { 355 | mgCloseLogic(but); 356 | } 357 | mgEndPopup(); 358 | } 359 | 360 | 361 | but = mgButton("Tooltip", mgOpt()); 362 | if (mgLogic(but, MG_WAIT, MG_HOVERED, 0)) { 363 | mgBeginPopup(but, MG_COL, mgOpt()); 364 | mgLabel("Tipping tools.", mgOpt()); 365 | mgEndPopup(); 366 | } 367 | 368 | */ 369 | 370 | 371 | // Menu bar 372 | // mgBeginPanel("Menu", 0,0, winWidth, 30, MG_ROW, MG_JUSTIFY, 0, 0,0); 373 | 374 | mgPanelBegin(MG_ROW, 0,0, 0, mgOpts(mgWidth(winWidth), mgHeight(30), mgTag("menubar"), mgAlign(MG_JUSTIFY))); 375 | file = mgItem("File", mgOpts()); 376 | mgPopupBegin(file, MG_ACTIVE, MG_COL, mgOpts(mgTag("menu1"))); 377 | fileOpen = mgItem("Open...", mgOpts()); 378 | mgItem("Save", mgOpts()); 379 | mgItem("Save As...", mgOpts()); 380 | mgItem("Close", mgOpts()); 381 | mgPopupEnd(); 382 | 383 | edit = mgItem("Edit", mgOpts()); 384 | mgPopupBegin(edit, MG_ACTIVE, MG_COL, mgOpts(mgTag("menu1"))); 385 | mgItem("Undo", mgOpts()); 386 | mgItem("Redo", mgOpts()); 387 | mgItem("Cut", mgOpts()); 388 | mgItem("Copy", mgOpts()); 389 | mgItem("Paste", mgOpts()); 390 | mgPopupEnd(); 391 | 392 | tools = mgItem("Tools", mgOpts()); 393 | mgPopupBegin(tools, MG_ACTIVE, MG_COL, mgOpts(mgTag("menu1"))); 394 | mgItem("Build", mgOpts()); 395 | mgItem("Clear", mgOpts()); 396 | toolsAlign = mgItem("Align", mgOpts()); 397 | mgPopupBegin(toolsAlign, MG_HOVER, MG_COL, mgOpts(mgTag("menu2"))); 398 | mgItem("Left", mgOpts()); 399 | mgItem("Center", mgOpts()); 400 | mgItem("Right", mgOpts()); 401 | mgPopupEnd(); 402 | mgPopupEnd(); 403 | 404 | view = mgItem("View", mgOpts()); 405 | mgPopupBegin(view, MG_ACTIVE, MG_COL, mgOpts(mgTag("menu1"))); 406 | mgItem("Sidebar", mgOpts()); 407 | mgItem("Minimap", mgOpts()); 408 | mgItem("Tabs", mgOpts()); 409 | mgPopupEnd(); 410 | mgPanelEnd(); 411 | 412 | if (mgClicked(fileOpen)) { 413 | printf("Open!!\n"); 414 | } 415 | 416 | // mgBeginPanel("NavMesh Options", 20,50, 250, MG_AUTO, MG_COL, MG_JUSTIFY, 0, 5); 417 | // mgBeginPanel("NavMesh Options", 20,50, 250, winHeight - 50, MG_COL, MG_JUSTIFY, MG_SCROLL, 5,5); 418 | mgPanelBegin(MG_COL, 20,50, 0, mgOpts(mgWidth(250), /*mgHeight(MG_AUTO_SIZE),*/ mgTag("dialog"), mgAlign(MG_JUSTIFY), mgOverflow(MG_SCROLL), mgPadding(10,10))); 419 | 420 | // mgText("NavMesh Options", ); 421 | 422 | mgLabel("Blending", mgOpts()); 423 | mgSelect(&blending, choices, 4, mgOpts()); 424 | 425 | mgLabel("Opacity", mgOpts()); 426 | mgBoxBegin(MG_ROW, mgOpts(mgAlign(MG_CENTER))); 427 | if (mgChanged(mgSlider(&opacity, 0.0f, 1.0f, mgOpts(mgGrow(1))))) { 428 | printf("opacity = %f\n", opacity); 429 | } 430 | mgNumber(&opacity, mgOpts()); 431 | mgBoxEnd(); 432 | 433 | /* 434 | // TODO: different in/out? 435 | float newOpacity = opacity; 436 | slider = mgSlider(opacity, &newOpacity, 0.0f, 1.0f, mgOpts(mgName("opacity"))); 437 | if (mgChanged(slider)) 438 | opacity = newOpacity; 439 | if (mgCommited(slider)) 440 | saveUndoState(); 441 | */ 442 | 443 | mgLabel("Iterations", mgOpts()); 444 | mgNumber(&iterations, mgOpts()); 445 | 446 | mgLabel("Position", mgOpts()); 447 | mgNumber3(&position[0], &position[1], &position[2], "mm", mgOpts()); 448 | 449 | mgLabel("Color", mgOpts()); 450 | mgColor(&color[0], &color[1], &color[2], &color[3], mgOpts()); 451 | 452 | mgCheckBox("Cull Enabled", &cull, mgOpts()); 453 | mgLabel("Name", mgOpts()); 454 | 455 | mgInput(name, sizeof(name), mgOpts()); 456 | 457 | build = mgIconButton("tools", "Build", mgOpts()); 458 | if (mgClicked(build)) { 459 | printf("Build!!\n"); 460 | } 461 | mgTooltip(build, "Press Build to build.", mgOpts()); 462 | 463 | mgBoxBegin(MG_ROW, mgOpts()); 464 | mgBoxBegin(MG_COL, mgOpts(mgGrow(1), mgSpacing(5), mgAlign(MG_JUSTIFY))); 465 | mgParagraph("She was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party.", mgOpts()); 466 | mgBoxEnd(); 467 | mgButton("Build4", mgOpts()); 468 | mgBoxEnd(); 469 | 470 | mgProgress(sqr(sinf(glfwGetTime()*0.3f)), mgOpts()); 471 | 472 | // scroll = (200 - 45) * sqr(sinf(glfwGetTime()*0.3f)); 473 | mgScrollBar(&scroll, 200, 45, mgOpts()); 474 | 475 | /* mgBoxBegin(MG_ROW, mgOpts()); 476 | mgBoxBegin(MG_COL, mgOpts(mgGrow(1), mgSpacing(5), mgAlign(MG_JUSTIFY))); 477 | mgButton("Build1", mgOpts()); 478 | mgButton("Build2", mgOpts()); 479 | mgButton("Build3", mgOpts()); 480 | mgBoxEnd(); 481 | mgButton("Build4", mgOpts()); 482 | mgBoxEnd();*/ 483 | 484 | mgPanelEnd(); 485 | 486 | mgPanelBegin(MG_COL, winWidth-40-150, 50, 0, mgOpts(mgWidth(150), mgTag("dialog"), mgAlign(MG_JUSTIFY), mgOverflow(MG_SCROLL), mgPadding(10,10))); 487 | mgParagraph("Headline with verylingtestindeed", mgOpts(mgFontSize(32), mgLineHeight(0.8f))); 488 | mgParagraph("This is longer chunk of text.\nWould have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party.", mgOpts()); 489 | mgPanelEnd(); 490 | 491 | mgFrameEnd(); 492 | 493 | /* nvgBeginPath(vg); 494 | nvgRoundedRect(vg, 20, 20, 200, 30, 5); 495 | nvgFillColor(vg, nvgRGBA(255,192,0,255)); 496 | nvgFill(vg); 497 | 498 | nvgFontFace(vg, "sans"); 499 | nvgFontSize(vg, 32); 500 | nvgFillColor(vg, nvgRGBA(255,255,255,255)); 501 | nvgTextAlign(vg, NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 502 | nvgText(vg, 100, 100, "Mikko", NULL); 503 | 504 | nvgText(vg, 102, 102, "Mikko", NULL);*/ 505 | 506 | nvgEndFrame(vg); 507 | 508 | glEnable(GL_DEPTH_TEST); 509 | 510 | glfwSwapBuffers(window); 511 | glfwPollEvents(); 512 | } 513 | 514 | nvgDeleteGL3(vg); 515 | 516 | mgTerminate(); 517 | 518 | glfwTerminate(); 519 | return 0; 520 | } 521 | -------------------------------------------------------------------------------- /example/example2.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 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 20 | #include 21 | #include 22 | #include 23 | #define GLFW_INCLUDE_GLCOREARB 24 | #include 25 | #include "nanovg.h" 26 | #define NANOVG_GL3_IMPLEMENTATION 27 | #include "nanovg_gl.h" 28 | #include "milli.h" 29 | #define NANOSVG_IMPLEMENTATION 1 30 | #include "nanosvg.h" 31 | 32 | struct MIinputState input = { 0 }; 33 | 34 | void errorcb(int error, const char* desc) 35 | { 36 | printf("GLFW error %d: %s\n", error, desc); 37 | } 38 | 39 | 40 | static int isPrintable(int key) 41 | { 42 | switch (key) { 43 | case GLFW_KEY_ESCAPE: 44 | case GLFW_KEY_ENTER: 45 | case GLFW_KEY_TAB: 46 | case GLFW_KEY_BACKSPACE: 47 | case GLFW_KEY_INSERT: 48 | case GLFW_KEY_DELETE: 49 | case GLFW_KEY_RIGHT: 50 | case GLFW_KEY_LEFT: 51 | case GLFW_KEY_DOWN: 52 | case GLFW_KEY_UP: 53 | case GLFW_KEY_PAGE_UP: 54 | case GLFW_KEY_PAGE_DOWN: 55 | case GLFW_KEY_HOME: 56 | case GLFW_KEY_END: 57 | case GLFW_KEY_CAPS_LOCK: 58 | case GLFW_KEY_SCROLL_LOCK: 59 | case GLFW_KEY_NUM_LOCK: 60 | case GLFW_KEY_PRINT_SCREEN: 61 | case GLFW_KEY_PAUSE: 62 | case GLFW_KEY_F1: 63 | case GLFW_KEY_F2: 64 | case GLFW_KEY_F3: 65 | case GLFW_KEY_F4: 66 | case GLFW_KEY_F5: 67 | case GLFW_KEY_F6: 68 | case GLFW_KEY_F7: 69 | case GLFW_KEY_F8: 70 | case GLFW_KEY_F9: 71 | case GLFW_KEY_F10: 72 | case GLFW_KEY_F11: 73 | case GLFW_KEY_F12: 74 | case GLFW_KEY_F13: 75 | case GLFW_KEY_F14: 76 | case GLFW_KEY_F15: 77 | case GLFW_KEY_F16: 78 | case GLFW_KEY_F17: 79 | case GLFW_KEY_F18: 80 | case GLFW_KEY_F19: 81 | case GLFW_KEY_F20: 82 | case GLFW_KEY_F21: 83 | case GLFW_KEY_F22: 84 | case GLFW_KEY_F23: 85 | case GLFW_KEY_F24: 86 | case GLFW_KEY_F25: 87 | case GLFW_KEY_KP_ENTER: 88 | case GLFW_KEY_LEFT_SHIFT: 89 | case GLFW_KEY_LEFT_CONTROL: 90 | case GLFW_KEY_LEFT_ALT: 91 | case GLFW_KEY_LEFT_SUPER: 92 | case GLFW_KEY_RIGHT_SHIFT: 93 | case GLFW_KEY_RIGHT_CONTROL: 94 | case GLFW_KEY_RIGHT_ALT: 95 | case GLFW_KEY_RIGHT_SUPER: 96 | case GLFW_KEY_MENU: 97 | return 0; 98 | } 99 | return 1; 100 | } 101 | 102 | int printable = 0; 103 | 104 | static void keycb(GLFWwindow* window, int key, int scancode, int action, int mods) 105 | { 106 | (void)scancode; 107 | (void)mods; 108 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 109 | glfwSetWindowShouldClose(window, GL_TRUE); 110 | if (action == GLFW_PRESS) 111 | printable = isPrintable(key); 112 | 113 | if (action == GLFW_PRESS) { 114 | if (!isPrintable(key)) { 115 | if (input.nkeys < MI_MAX_INPUTKEYS) { 116 | input.keys[input.nkeys].type = MI_KEYPRESSED; 117 | input.keys[input.nkeys].code = key; 118 | input.nkeys++; 119 | } 120 | } 121 | } 122 | if (action == GLFW_RELEASE) { 123 | if (!isPrintable(key)) { 124 | if (input.nkeys < MI_MAX_INPUTKEYS) { 125 | input.keys[input.nkeys].type = MI_KEYRELEASED; 126 | input.keys[input.nkeys].code = key; 127 | input.nkeys++; 128 | } 129 | } 130 | } 131 | } 132 | 133 | static void charcb(GLFWwindow* window, unsigned int codepoint) 134 | { 135 | (void)window; 136 | 137 | if (printable) { 138 | if (input.nkeys < MI_MAX_INPUTKEYS) { 139 | input.keys[input.nkeys].type = MI_CHARTYPED; 140 | input.keys[input.nkeys].code = codepoint; 141 | input.nkeys++; 142 | } 143 | } 144 | } 145 | 146 | static void buttoncb(GLFWwindow* window, int button, int action, int mods) 147 | { 148 | (void)window; 149 | (void)mods; 150 | if (button == GLFW_MOUSE_BUTTON_LEFT ) { 151 | if (action == GLFW_PRESS) { 152 | input.mbut |= MI_MOUSE_PRESSED; 153 | } 154 | if (action == GLFW_RELEASE) { 155 | input.mbut |= MI_MOUSE_RELEASED; 156 | } 157 | } 158 | } 159 | 160 | 161 | 162 | /* 163 | 164 | - templates 165 | - variables 166 | - collection 167 | - events 168 | 169 | */ 170 | 171 | /*struct MIcell* createPanel() 172 | { 173 | struct MIcell* panel = NULL; 174 | 175 | panel = miBegin(miTemplate("id=panel #asd #asd #asd #materials")); 176 | miBegin(miBox("id=panel dir=col align=justify padding='5 5' width=250 height=400")); 177 | miText("id=header label='Materials' font-size=24 spacing=10"); 178 | miBegin(miBox("id=search dir=row align=justify padding='2 2' spacing=5")); 179 | miIcon("id=search-icon icon=search"); 180 | miText("id=search-input label='Search' grow=1 paddingx=5"); 181 | miIcon("id=search-clear icon=cancel-circled"); 182 | miEnd(); 183 | 184 | miCollection(Box("id=materials dir=col align=justify spacing=5")); 185 | 186 | miBegin(Box("id=footer dir=row pack=end spacing=5"); 187 | miIconButton("id=footer-add icon=plus label=Add spacing=5"); 188 | miButton("id=footer-remove label=Remove spacing=5"); 189 | miEnd(); 190 | miSlider("id=slider padding='5 5' value=0.5 vmin=0 vmax=1 spacing=5"); 191 | miEnd(); 192 | miEnd(); 193 | 194 | 195 | 196 | char search[128]; 197 | miBegin(panel); 198 | miSetStr("search-input", search); 199 | if (miChanged("search-input")) 200 | miGetStr("search-input", search, sizeof(search)); 201 | updateSearch(search); 202 | } 203 | if (miClicked("search-clear")) { 204 | miBlur("search-input"); 205 | strcpy(search, ""); 206 | } 207 | 208 | miCollectionBegin("materials"); 209 | for (i = 0; i < materialCount; i++) { 210 | miBegin(materialItem); 211 | miSetStr("name", materisl[i].name); 212 | miSetFloat4("color", materisl[i].color); 213 | if (miChanged("name") || miChanged("color")) { 214 | miGetStr("name", materisl[i].name, sizeof(materisl[i].name)); 215 | miGetFloat4("color", materisl[i].color); 216 | saveUndo(); 217 | } 218 | miEnd(); 219 | } 220 | miCollectionEnd(); 221 | 222 | miEnd(); 223 | 224 | return panel; 225 | }*/ 226 | 227 | struct MIcell* createPanel() 228 | { 229 | struct MIcell* panel = NULL; 230 | struct MIcell* search = NULL; 231 | struct MIcell* footer = NULL; 232 | struct MIcell* list = NULL; 233 | struct MIcell* button = NULL; 234 | 235 | panel = miCreateBox("id=panel dir=col align=justify padding='5 5' width=250 height=400"); 236 | 237 | miAddChild(panel, miCreateText("id=header label='Materials' font-size=24 spacing=10")); 238 | 239 | search = miCreateBox("id=search dir=row align=justify padding='2 2' spacing=5"); 240 | miAddChild(search, miCreateIcon("id=search-icon icon=search")); 241 | miAddChild(search, miCreateText("id=search-input label='Search' grow=1 paddingx=5")); 242 | miAddChild(search, miCreateIcon("id=search-clear icon=cancel-circled")); 243 | miAddChild(panel, search); 244 | 245 | list = miCreateBox("dir=col grow=1 spacing=5"); 246 | // miAddChild(list, miCreateCollection("id=list")); 247 | miAddChild(panel, list); 248 | 249 | footer = miCreateBox("id=footer dir=row pack=end spacing=5"); 250 | miAddChild(footer, miCreateIconButton("id=footer-add icon=plus label=Add spacing=5")); 251 | miAddChild(footer, miCreateButton("id=footer-remove label=Remove spacing=5")); 252 | miAddChild(panel, footer); 253 | 254 | miAddChild(panel, miCreateSlider("id=slider padding='5 5' value=0.5 vmin=0 vmax=1 spacing=5")); 255 | 256 | return panel; 257 | } 258 | 259 | int main() 260 | { 261 | GLFWwindow* window; 262 | struct NVGcontext* vg = NULL; 263 | 264 | char search[64]; 265 | 266 | /* int blending = 0; 267 | float opacity = 0.5f; 268 | float position[3] = {100.0f, 120.0f, 234.0f}; 269 | float color[4] = {255/255.0f,192/255.0f,0/255.0f,255/255.0f}; 270 | float iterations = 42; 271 | int cull = 1; 272 | char name[64] = "Mikko"; 273 | const char* choices[] = { "Normal", "Minimum Color", "Screen Door", "Maximum Velocity" }; 274 | float scroll = 30;*/ 275 | 276 | struct MIcell* panel = NULL; 277 | float iconScale = 21.0f/1000.0f; 278 | 279 | printf("start\n"); 280 | 281 | if (!glfwInit()) { 282 | printf("Failed to init GLFW."); 283 | return -1; 284 | } 285 | 286 | glfwSetErrorCallback(errorcb); 287 | 288 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 289 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 290 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 291 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 292 | 293 | window = glfwCreateWindow(1000, 600, "mg", NULL, NULL); 294 | if (!window) { 295 | glfwTerminate(); 296 | return -1; 297 | } 298 | 299 | glfwSetKeyCallback(window, keycb); 300 | glfwSetCharCallback(window, charcb); 301 | glfwSetMouseButtonCallback(window, buttoncb); 302 | 303 | glfwMakeContextCurrent(window); 304 | 305 | vg = nvgCreateGL3(512, 512, NVG_ANTIALIAS); 306 | if (vg == NULL) { 307 | printf("Could not init nanovg.\n"); 308 | return -1; 309 | } 310 | if (nvgCreateFont(vg, "sans", "../example/fonts/Roboto-Regular.ttf") == -1) { 311 | printf("Could not add font italic.\n"); 312 | return -1; 313 | } 314 | if (nvgCreateFont(vg, "sans-bold", "../example/fonts/Roboto-Bold.ttf") == -1) { 315 | printf("Could not add font bold.\n"); 316 | return -1; 317 | } 318 | 319 | miInit(); 320 | 321 | if (miCreateIconImage("check", "../example/icons/check.svg", iconScale)) { 322 | printf("Could not create icon 'check'.\n"); 323 | return -1; 324 | } 325 | if (miCreateIconImage("arrow-combo", "../example/icons/arrow-combo.svg", iconScale)) { 326 | printf("Could not create icon 'arrow-combo'.\n"); 327 | return -1; 328 | } 329 | if (miCreateIconImage("tools", "../example/icons/tools.svg", iconScale)) { 330 | printf("Could not create icon 'tool'.\n"); 331 | return -1; 332 | } 333 | if (miCreateIconImage("cancel-circled", "../example/icons/cancel-circled.svg", iconScale)) { 334 | printf("Could not create icon 'cancel-circled'.\n"); 335 | return -1; 336 | } 337 | if (miCreateIconImage("plus", "../example/icons/plus.svg", iconScale)) { 338 | printf("Could not create icon 'plus'.\n"); 339 | return -1; 340 | } 341 | if (miCreateIconImage("search", "../example/icons/search.svg", iconScale)) { 342 | printf("Could not create icon 'search'.\n"); 343 | return -1; 344 | } 345 | 346 | panel = createPanel(); 347 | 348 | miLayout(panel, vg); 349 | 350 | glfwSetTime(0); 351 | 352 | while (!glfwWindowShouldClose(window)) 353 | { 354 | double mx, my; 355 | int winWidth, winHeight; 356 | int fbWidth, fbHeight; 357 | float pxRatio; 358 | 359 | // float t = glfwGetTime(); 360 | // float x,y,popy; 361 | // struct MGhit* hit; 362 | 363 | glfwGetCursorPos(window, &mx, &my); 364 | glfwGetWindowSize(window, &winWidth, &winHeight); 365 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 366 | // Calculate pixel ration for hi-dpi devices. 367 | pxRatio = (float)fbWidth / (float)winWidth; 368 | 369 | // Update and render 370 | glViewport(0, 0, fbWidth, fbHeight); 371 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 372 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 373 | glEnable(GL_BLEND); 374 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 375 | glDisable(GL_DEPTH_TEST); 376 | 377 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio, NVG_STRAIGHT_ALPHA); 378 | 379 | input.mx = mx; 380 | input.my = my; 381 | miFrameBegin(vg, winWidth, winHeight, &input); 382 | // input.nkeys = 0; 383 | // input.mbut = 0; 384 | 385 | miInput(panel, &input); 386 | 387 | /* float s = slider; 388 | if (miSync(panel, "slider", &s)) { 389 | slider = s; 390 | } 391 | 392 | if (miValue(panel, "slider", &s)) { 393 | slider = s; 394 | } 395 | 396 | if (miClicked(panel, "footer-add")) { 397 | } 398 | */ 399 | 400 | miRender(panel, vg); 401 | 402 | miFrameEnd(); 403 | 404 | nvgEndFrame(vg); 405 | 406 | glEnable(GL_DEPTH_TEST); 407 | 408 | glfwSwapBuffers(window); 409 | glfwPollEvents(); 410 | } 411 | 412 | nvgDeleteGL3(vg); 413 | 414 | miFreeCell(panel); 415 | 416 | miTerminate(); 417 | 418 | glfwTerminate(); 419 | return 0; 420 | } 421 | -------------------------------------------------------------------------------- /example/example3.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 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 20 | #include 21 | #include 22 | #include 23 | #define GLFW_INCLUDE_GLCOREARB 24 | #include 25 | #include "nanovg.h" 26 | #define NANOVG_GL3_IMPLEMENTATION 27 | #include "nanovg_gl.h" 28 | #include "milli2.h" 29 | #define NANOSVG_IMPLEMENTATION 1 30 | #include "nanosvg.h" 31 | 32 | MIinputState input; 33 | 34 | void errorcb(int error, const char* desc) 35 | { 36 | printf("GLFW error %d: %s\n", error, desc); 37 | } 38 | 39 | 40 | static int isPrintable(int key) 41 | { 42 | switch (key) { 43 | case GLFW_KEY_ESCAPE: 44 | case GLFW_KEY_ENTER: 45 | case GLFW_KEY_TAB: 46 | case GLFW_KEY_BACKSPACE: 47 | case GLFW_KEY_INSERT: 48 | case GLFW_KEY_DELETE: 49 | case GLFW_KEY_RIGHT: 50 | case GLFW_KEY_LEFT: 51 | case GLFW_KEY_DOWN: 52 | case GLFW_KEY_UP: 53 | case GLFW_KEY_PAGE_UP: 54 | case GLFW_KEY_PAGE_DOWN: 55 | case GLFW_KEY_HOME: 56 | case GLFW_KEY_END: 57 | case GLFW_KEY_CAPS_LOCK: 58 | case GLFW_KEY_SCROLL_LOCK: 59 | case GLFW_KEY_NUM_LOCK: 60 | case GLFW_KEY_PRINT_SCREEN: 61 | case GLFW_KEY_PAUSE: 62 | case GLFW_KEY_F1: 63 | case GLFW_KEY_F2: 64 | case GLFW_KEY_F3: 65 | case GLFW_KEY_F4: 66 | case GLFW_KEY_F5: 67 | case GLFW_KEY_F6: 68 | case GLFW_KEY_F7: 69 | case GLFW_KEY_F8: 70 | case GLFW_KEY_F9: 71 | case GLFW_KEY_F10: 72 | case GLFW_KEY_F11: 73 | case GLFW_KEY_F12: 74 | case GLFW_KEY_F13: 75 | case GLFW_KEY_F14: 76 | case GLFW_KEY_F15: 77 | case GLFW_KEY_F16: 78 | case GLFW_KEY_F17: 79 | case GLFW_KEY_F18: 80 | case GLFW_KEY_F19: 81 | case GLFW_KEY_F20: 82 | case GLFW_KEY_F21: 83 | case GLFW_KEY_F22: 84 | case GLFW_KEY_F23: 85 | case GLFW_KEY_F24: 86 | case GLFW_KEY_F25: 87 | case GLFW_KEY_KP_ENTER: 88 | case GLFW_KEY_LEFT_SHIFT: 89 | case GLFW_KEY_LEFT_CONTROL: 90 | case GLFW_KEY_LEFT_ALT: 91 | case GLFW_KEY_LEFT_SUPER: 92 | case GLFW_KEY_RIGHT_SHIFT: 93 | case GLFW_KEY_RIGHT_CONTROL: 94 | case GLFW_KEY_RIGHT_ALT: 95 | case GLFW_KEY_RIGHT_SUPER: 96 | case GLFW_KEY_MENU: 97 | return 0; 98 | } 99 | return 1; 100 | } 101 | 102 | int printable = 0; 103 | 104 | static void keycb(GLFWwindow* window, int key, int scancode, int action, int mods) 105 | { 106 | (void)scancode; 107 | (void)mods; 108 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 109 | glfwSetWindowShouldClose(window, GL_TRUE); 110 | if (action == GLFW_PRESS) 111 | printable = isPrintable(key); 112 | 113 | if (action == GLFW_PRESS) { 114 | if (!isPrintable(key)) { 115 | if (input.nkeys < MI_MAX_INPUTKEYS) { 116 | input.keys[input.nkeys].type = MI_KEYPRESSED; 117 | input.keys[input.nkeys].code = key; 118 | input.keys[input.nkeys].mods = mods; 119 | input.nkeys++; 120 | } 121 | } 122 | } 123 | if (action == GLFW_RELEASE) { 124 | if (!isPrintable(key)) { 125 | if (input.nkeys < MI_MAX_INPUTKEYS) { 126 | input.keys[input.nkeys].type = MI_KEYRELEASED; 127 | input.keys[input.nkeys].code = key; 128 | input.keys[input.nkeys].mods = mods; 129 | input.nkeys++; 130 | } 131 | } 132 | } 133 | } 134 | 135 | static void charcb(GLFWwindow* window, unsigned int codepoint) 136 | { 137 | (void)window; 138 | 139 | if (printable) { 140 | if (input.nkeys < MI_MAX_INPUTKEYS) { 141 | input.keys[input.nkeys].type = MI_CHARTYPED; 142 | input.keys[input.nkeys].code = codepoint; 143 | input.keys[input.nkeys].mods = 0; 144 | input.nkeys++; 145 | } 146 | } 147 | } 148 | 149 | static void buttoncb(GLFWwindow* window, int button, int action, int mods) 150 | { 151 | (void)window; 152 | (void)mods; 153 | if (button == GLFW_MOUSE_BUTTON_LEFT ) { 154 | if (action == GLFW_PRESS) { 155 | input.mbut |= MI_MOUSE_PRESSED; 156 | } 157 | if (action == GLFW_RELEASE) { 158 | input.mbut |= MI_MOUSE_RELEASED; 159 | } 160 | } 161 | } 162 | 163 | /* 164 | 165 | 166 | - row 167 | - left 168 | - right 169 | - pack n 170 | - col 171 | - grid 172 | - regular w * h 173 | - pack n * h 174 | 175 | - widget style 176 | 177 | 178 | 179 | */ 180 | 181 | 182 | int main() 183 | { 184 | GLFWwindow* window; 185 | struct NVGcontext* vg = NULL; 186 | 187 | char search[64] = "Foob-foob"; 188 | double t = 0; 189 | 190 | /* int blending = 0; 191 | float opacity = 0.5f; 192 | float position[3] = {100.0f, 120.0f, 234.0f}; 193 | float color[4] = {255/255.0f,192/255.0f,0/255.0f,255/255.0f}; 194 | float iterations = 42; 195 | int cull = 1; 196 | char name[64] = "Mikko"; 197 | const char* choices[] = { "Normal", "Minimum Color", "Screen Door", "Maximum Velocity" }; 198 | float scroll = 30;*/ 199 | 200 | float iconScale = 21.0f/1000.0f; 201 | 202 | printf("start\n"); 203 | 204 | if (!glfwInit()) { 205 | printf("Failed to init GLFW."); 206 | return -1; 207 | } 208 | 209 | glfwSetErrorCallback(errorcb); 210 | 211 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 212 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 213 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 214 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 215 | 216 | window = glfwCreateWindow(1000, 600, "mg", NULL, NULL); 217 | if (!window) { 218 | glfwTerminate(); 219 | return -1; 220 | } 221 | 222 | glfwSetKeyCallback(window, keycb); 223 | glfwSetCharCallback(window, charcb); 224 | glfwSetMouseButtonCallback(window, buttoncb); 225 | 226 | glfwMakeContextCurrent(window); 227 | 228 | vg = nvgCreateGL3(512, 512, NVG_ANTIALIAS); 229 | if (vg == NULL) { 230 | printf("Could not init nanovg.\n"); 231 | return -1; 232 | } 233 | 234 | miInit(vg); 235 | 236 | if (miCreateFont(MI_FONT_NORMAL, "../example/fonts/Roboto-Regular.ttf")) { 237 | printf("Could not add font italic.\n"); 238 | return -1; 239 | } 240 | if (miCreateFont(MI_FONT_ITALIC, "../example/fonts/Roboto-Italic.ttf")) { 241 | printf("Could not add font italic.\n"); 242 | return -1; 243 | } 244 | if (miCreateFont(MI_FONT_BOLD, "../example/fonts/Roboto-Bold.ttf")) { 245 | printf("Could not add font bold.\n"); 246 | return -1; 247 | } 248 | 249 | if (miCreateIconImage("check", "../example/icons/check.svg", iconScale)) { 250 | printf("Could not create icon 'check'.\n"); 251 | return -1; 252 | } 253 | if (miCreateIconImage("arrow-combo", "../example/icons/arrow-combo.svg", iconScale)) { 254 | printf("Could not create icon 'arrow-combo'.\n"); 255 | return -1; 256 | } 257 | if (miCreateIconImage("tools", "../example/icons/tools.svg", iconScale)) { 258 | printf("Could not create icon 'tool'.\n"); 259 | return -1; 260 | } 261 | if (miCreateIconImage("cancel-circled", "../example/icons/cancel-circled.svg", iconScale)) { 262 | printf("Could not create icon 'cancel-circled'.\n"); 263 | return -1; 264 | } 265 | if (miCreateIconImage("plus", "../example/icons/plus.svg", iconScale)) { 266 | printf("Could not create icon 'plus'.\n"); 267 | return -1; 268 | } 269 | if (miCreateIconImage("search", "../example/icons/search.svg", iconScale)) { 270 | printf("Could not create icon 'search'.\n"); 271 | return -1; 272 | } 273 | 274 | glfwSetTime(0); 275 | 276 | MIcanvasState canvas = {0}; 277 | float value = 0.15f; 278 | 279 | while (!glfwWindowShouldClose(window)) 280 | { 281 | double mx, my; 282 | int winWidth, winHeight; 283 | int fbWidth, fbHeight; 284 | float pxRatio; 285 | double dt; 286 | dt = glfwGetTime() - t; 287 | t += dt; 288 | 289 | // float t = glfwGetTime(); 290 | // float x,y,popy; 291 | // struct MGhit* hit; 292 | 293 | glfwGetCursorPos(window, &mx, &my); 294 | glfwGetWindowSize(window, &winWidth, &winHeight); 295 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 296 | // Calculate pixel ration for hi-dpi devices. 297 | pxRatio = (float)fbWidth / (float)winWidth; 298 | 299 | // Update and render 300 | glViewport(0, 0, fbWidth, fbHeight); 301 | glClearColor(0.3f, 0.3f, 0.32f, 1.0f); 302 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 303 | glEnable(GL_BLEND); 304 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 305 | glDisable(GL_DEPTH_TEST); 306 | 307 | nvgBeginFrame(vg, winWidth, winHeight, pxRatio, NVG_STRAIGHT_ALPHA); 308 | 309 | input.mx = mx; 310 | input.my = my; 311 | miFrameBegin(winWidth, winHeight, &input, (float)dt); 312 | 313 | // input.nkeys = 0; 314 | // input.mbut = 0; 315 | 316 | // miInput(panel, &input); 317 | 318 | /* float s = slider; 319 | if (miSync(panel, "slider", &s)) { 320 | slider = s; 321 | } 322 | 323 | if (miValue(panel, "slider", &s)) { 324 | slider = s; 325 | } 326 | 327 | if (miClicked(panel, "footer-add")) { 328 | } 329 | */ 330 | 331 | // miRender(panel, vg); 332 | 333 | miPanelBegin(50,50, 250,450); 334 | 335 | miDockBegin(MI_TOP_BOTTOM); 336 | miText("Materials"); 337 | float cols[3] = {25, -1, 25}; 338 | miDivsBegin(MI_LEFT_RIGHT, 3, cols); 339 | miRowHeight(25); 340 | miText("S"); 341 | miInput(search, sizeof(search)); 342 | miText("X"); 343 | miText("Q"); 344 | miDivsEnd(); 345 | miSliderValue(&value, -1.0f, 1.0f); 346 | /* miLayoutBegin(); 347 | miRowHeight(25); 348 | miPack(MI_LEFT_RIGHT); 349 | miText("S"); 350 | miPack(MI_RIGHT_LEFT); 351 | miText("X"); 352 | miPack(MI_FILLX); 353 | miInput(search, sizeof(search)); 354 | miLayoutEnd();*/ 355 | miDockEnd(); 356 | 357 | miDockBegin(MI_BOTTOM_TOP); 358 | float cols2[3] = {-1, 60, 40}; 359 | miDivsBegin(MI_LEFT_RIGHT, 3, cols2); 360 | miRowHeight(20); 361 | miSpacer(); 362 | miButton("Add"); 363 | miButton("Delete"); 364 | miDivsEnd(); 365 | miDockEnd(); 366 | 367 | // miLayoutBegin(); 368 | // miRowHeight(20); 369 | /* miPack(MI_LEFT_RIGHT); 370 | miText("Ins"); 371 | miPack(MI_RIGHT_LEFT); 372 | miButton("Delete"); 373 | miButton("Add");*/ 374 | 375 | // miLayoutEnd(); 376 | 377 | miDockBegin(MI_FILLY); 378 | 379 | 380 | float cols3[2] = {50, -1}; 381 | miDivsBegin(MI_LEFT_RIGHT, 2, cols3); 382 | miRowHeight(50); 383 | miText("IMG"); 384 | float rows[4] = {-1, 20, 15, -1}; 385 | miDivsBegin(MI_TOP_BOTTOM, 4, rows); 386 | miSpacer(); 387 | miText("Plastic"); 388 | miLayoutBegin(MI_LEFT_RIGHT); 389 | miPack(MI_LEFT_RIGHT); 390 | miText("very shiny"); 391 | miPack(MI_RIGHT_LEFT); 392 | miText("7kB"); 393 | miLayoutEnd(); 394 | miDivsEnd(); 395 | miDivsEnd(); 396 | 397 | miLayoutBegin(MI_LEFT_RIGHT); 398 | miRowHeight(50); 399 | miText("IMG"); 400 | miLayoutBegin(MI_TOP_BOTTOM); 401 | miText("Plastic"); 402 | miText("very shiny"); 403 | miLayoutEnd(); 404 | miLayoutEnd(); 405 | 406 | miDockEnd(); 407 | 408 | 409 | /* miText("Text 1"); 410 | float cols[3] = {25, -1, 25}; 411 | miDivsBegin(MI_LEFT_RIGHT, 3, cols); 412 | // miLayoutBegin(MI_LEFT_RIGHT); 413 | miPack(MI_LEFT_RIGHT); 414 | miText("Text 2.1"); 415 | miButton("Text 2.2"); 416 | miText("Text 2.3"); 417 | miDivsEnd(); 418 | // miLayoutEnd(); 419 | miText("Text 3");*/ 420 | 421 | /* miPack(MI_BOTTOM_TOP); 422 | miText("BOTTOM"); 423 | 424 | miPack(MI_LEFT_RIGHT); 425 | miText("LEFT"); 426 | 427 | miPack(MI_RIGHT_LEFT); 428 | miText("RIGHT");*/ 429 | 430 | 431 | /* MIhandle button = miButton("Popup"); 432 | MIhandle popup = miPopupBegin(button, MI_ONCLICK, MI_BELOW); 433 | miText("Popup..."); 434 | miCanvasBegin(&canvas, MI_FIT, 50); 435 | miCanvasEnd(); 436 | 437 | if (miClicked(miButton("Close"))) { 438 | printf("Close popup\n"); 439 | miPopupHide(popup); 440 | } 441 | 442 | MIhandle button2 = miButton("Popup 2"); 443 | miPopupBegin(button2, MI_ONHOVER, MI_RIGHT); 444 | miText("Popup 2"); 445 | miPopupEnd(); 446 | 447 | MIhandle button3 = miButton("Popup 3"); 448 | miPopupBegin(button3, MI_ONHOVER, MI_RIGHT); 449 | miText("Popup 3"); 450 | miPopupEnd(); 451 | 452 | miPopupEnd(); 453 | 454 | miSlider(&value, -1.0f, 1.0f); 455 | miSliderValue(&value, -1.0f, 1.0f); 456 | miText("Foobar"); 457 | 458 | float divs[] = {50,100}; 459 | miDivsBegin(MI_ROW, divs, 2, 30, 5); 460 | miButton("Tab 1"); 461 | miButton("Tab 2"); 462 | 463 | miDivsBegin(MI_COL, divs, 2, 30, 0); 464 | miButton("Tab 4.1"); 465 | miDivsBegin(MI_ROW, divs, 2, 30, 0); 466 | miButton("Tab 4.2.1"); 467 | miButton("Tab 4.2.2"); 468 | miButton("Tab 4.2.3"); 469 | miDivsEnd(); 470 | miDivsEnd(); 471 | 472 | miButton("Tab 3"); 473 | 474 | miButton("Tab 5"); 475 | miStackBegin(MI_COL, 30, 5); 476 | miText("Tab 6.1"); 477 | miStackBegin(MI_ROW, 30, 5); 478 | miText("Tab 6.2.1"); 479 | miText("Tab 6.2.2"); 480 | miText("Tab 6.2.3"); 481 | miStackEnd(); 482 | miText("Tab 6.3"); 483 | miStackEnd(); 484 | 485 | miDivsEnd(); 486 | 487 | miText("Foofoo"); 488 | 489 | miButtonRowBegin(4); 490 | miButton("A"); 491 | miButton("B"); 492 | miButton("C"); 493 | miButton("D"); 494 | miButtonRowEnd(); 495 | 496 | miInput(search, sizeof(search)); 497 | 498 | miPanelBegin(250,250, 250,40); 499 | miText("Another one..."); 500 | miPanelEnd();*/ 501 | 502 | miPanelEnd(); 503 | 504 | 505 | miFrameEnd(); 506 | 507 | nvgEndFrame(vg); 508 | 509 | glEnable(GL_DEPTH_TEST); 510 | 511 | glfwSwapBuffers(window); 512 | glfwPollEvents(); 513 | } 514 | 515 | nvgDeleteGL3(vg); 516 | 517 | miTerminate(); 518 | 519 | glfwTerminate(); 520 | return 0; 521 | } 522 | -------------------------------------------------------------------------------- /example/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/milligui/cf5a44a5296934f6eb7f59548675d6d62df2e489/example/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /example/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/milligui/cf5a44a5296934f6eb7f59548675d6d62df2e489/example/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /example/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/memononen/milligui/cf5a44a5296934f6eb7f59548675d6d62df2e489/example/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /example/icons/README.md: -------------------------------------------------------------------------------- 1 | The icons are from an icon font called Entypo by Daniel Bruce. 2 | http://www.entypo.com 3 | https://github.com/danielbruce/entypo -------------------------------------------------------------------------------- /example/icons/arrow-combo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/icons/cancel-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/icons/tools.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /flux.txt: -------------------------------------------------------------------------------- 1 | 2 | struct MIstore { 3 | }; 4 | 5 | typedef int (*MIactionFun)(void* state, void* payload, int size); 6 | 7 | struct MIaction { 8 | const char* name; 9 | MIactionFun action; 10 | }; 11 | 12 | void miStoreChanged(int store); 13 | void miDispatch(const char* action, void* data, int size); 14 | int miCreateStore(struct MIaction* actions, void* state); 15 | 16 | 17 | struct TodoItem { 18 | int id; 19 | char* text; 20 | int completed; 21 | }; 22 | 23 | struct TodoState { 24 | struct TodoItem* items; 25 | int count; 26 | }; 27 | 28 | struct TodoStore { 29 | struct MIstore store; 30 | struct TodoState state; 31 | int id; 32 | }; 33 | 34 | struct TodoStore _todoStore; 35 | int _todoStoreId = 0; 36 | 37 | 38 | static int _onAddTodo(void* state, void* payload, int size) 39 | { 40 | struct TodoItem* items = NULL; 41 | struct TodoItem* it = NULL; 42 | 43 | // Grow array 44 | items = realloc(_todoStore.state.items); 45 | if (items == NULL) return 0; 46 | _todoStore.state.items = items; 47 | 48 | // Create new item 49 | it = (struct TodoItem*)calloc(1, sizeof(struct TodoItem)); 50 | if (it == NULL) goto error; 51 | it->text = strndup((char*)payload, size); 52 | if (it->text == NULL) goto error; 53 | 54 | // Add item 55 | it->id = _todoStore.id++; 56 | _todoStore.state.items[_todoStore.state.count++] = it; 57 | 58 | miStoreChanged(_todoStoreId); 59 | 60 | return 1; 61 | error: 62 | if (it != NULL) { 63 | free(it->text); 64 | free(it); 65 | } 66 | return 0; 67 | } 68 | 69 | static int _onToggleTodo(void* state, void* payload, int size) 70 | { 71 | int i; 72 | int id = *(int*)payload; 73 | for (i = 0; i < _todoStore.state.count; i++) { 74 | if (_todoStore.state.items[i].id == id) { 75 | _todoStore.state.items[i].completed = !_todoStore.state.items[i].completed; 76 | miStoreChanged(_todoStoreId); 77 | return 1; 78 | } 79 | } 80 | return 0; 81 | } 82 | 83 | static int _onClearTodos(void* state, void* payload, int size) 84 | { 85 | int i, head = 0; 86 | // Removed completed items. 87 | for (i = 0; i < _todoStore.state.count; i++) { 88 | if (!_todoStore.state.items[i].completed) 89 | _todoStore.state.items[head++] = _todoStore.state.items[i]; 90 | } 91 | _todoStore.state.count = head; 92 | 93 | miStoreChanged(_todoStoreId); 94 | 95 | return 0; 96 | } 97 | 98 | struct MIaction _todoActions { 99 | { "ADD_TODO", _onAddTodo }, 100 | { "TOGGLE_TODO", _onToggleTodo }, 101 | { "CLEAR_TODOS", _onCleanTodos }, 102 | NULL 103 | }; 104 | 105 | 106 | void initTodoStore() 107 | { 108 | _todoStoreId = miCreateStore(_todoActions, &_todoStore.state); 109 | } 110 | 111 | void addTodo(const char* text) 112 | { 113 | miDispatch("ADD_TODO", text, sizeof(text)); 114 | } 115 | 116 | void toggleTodo(int id) 117 | { 118 | miDispatch("TOGGLE_TODO", &id, sizeof(int)); 119 | } 120 | 121 | void clearTodos() 122 | { 123 | miDispatch("CLEAR_TODOS", NULL, 0); 124 | } 125 | 126 | const struct TodoState* getTodoState() 127 | { 128 | return &_todoState; 129 | } 130 | 131 | 132 | // Public API 133 | void initTodoStore(); 134 | void addTodo(const char* text); 135 | void toggleTodo(int id); 136 | void clearTodos(); 137 | 138 | struct TodoItem* getTodoItems(); 139 | int getTodoItemCount(); 140 | 141 | 142 | 143 | 144 | void initAppComponent(); 145 | void initTodoItemComponent(); 146 | 147 | 148 | _todoItemRender(struct MIcomponent* comp, void* state) 149 | { 150 | struct TodoItem* item = (struct TodoItem*)state; 151 | if (miClicked(miText(item->text))) 152 | toggleTodo(id); 153 | } 154 | 155 | 156 | struct MIcomponent* _todoItemCreate() 157 | { 158 | struct MIcomponent* comp = (struct MIcomponent*)calloc(1, sizeof(struct MIcomponent)); 159 | if (comp == NULL) goto error; 160 | 161 | return comp; 162 | 163 | error: 164 | free(comp); 165 | return NULL; 166 | } 167 | 168 | void initTodoItemComponent() 169 | { 170 | miRegisterClass("todo-item"); 171 | } 172 | 173 | 174 | _appRender(struct MIcomponent* comp, void* state) 175 | { 176 | int i; 177 | miBegin(miBox(MI_COL)); 178 | miBegin(miBox(MI_COL)); 179 | for (i = 0; i < getTodoItemCount(state); i++) 180 | miEnd(); 181 | miEnd(); 182 | 183 | 184 |
185 |
    186 | {this.state.todos.map(function(todo, i) { 187 | return
  • ; 188 | })} 189 |
190 |
191 | 192 | 193 |
194 | 195 |
196 | } 197 | 198 | void initAppComponent() 199 | { 200 | } 201 | 202 | 203 | 204 | miRenderComponent("app"); 205 | -------------------------------------------------------------------------------- /idea.md: -------------------------------------------------------------------------------- 1 | MilliGUI is simple stupid UI library for editors and prootypes. 2 | - the data binding uses IMGUI 3 | - widgets are composed of elements like in HTML 4 | - layout works like flex-box 5 | - styling is done using css like selectors 6 | 7 | Layout and data binding are done using the usual IMGUI style coding, 8 | styling is done using predefined styles, matched using simple selectors. 9 | 10 | 11 | - basic elements 12 | - panel (absolute div) 13 | - box (flex box div) 14 | - popup (relative div) 15 | - text 16 | - image 17 | - input (text) 18 | - slider 19 | 20 | - components 21 | - button 22 | - select 23 | - toggle 24 | - button group 25 | - tab 26 | - numner input 27 | - color input 28 | - vec3 input 29 | 30 | - layout parameter for box 31 | - width: #, auto 32 | - width of the box 33 | - default is auto (fit to content) 34 | - height: #, auto 35 | - height of the box 36 | - default is auto (fit to content) 37 | - dir: row, col 38 | - direction in which the child elements are stacked in 39 | - defines main direction (cross direction is the opposite) 40 | - grow: # 41 | - controls how much the element is grown to fit into the free parent space 42 | - default 0 (don’t grow) 43 | - align: start, end, centre, justify 44 | - how to align in cross dir 45 | - overflow: fit, hidden, scroll 46 | - how to handle overflow in main direction (cross is always fit/shrink content) 47 | - logic: none, click, drag, type 48 | - what kind of logic to apply to the element 49 | - class: style 50 | - visual representation of the element 51 | 52 | - presentation parameters 53 | - panel & box 54 | - padding x, y 55 | - spacing (margin right/bottom in main dir) 56 | - fill colour 57 | - border color 58 | - border radius 59 | - corner radius 60 | - text 61 | - font face 62 | - font size 63 | - font weight 64 | - text color 65 | - image 66 | - border color 67 | - border radius 68 | - corner radius 69 | - input 70 | - font face 71 | - font size 72 | - font weight 73 | - text color 74 | - slider 75 | - handle size 76 | - handle fill colour 77 | - handle border color 78 | - handle border radius 79 | - handle corner radius 80 | - slot color 81 | - slot size 82 | 83 | - presentation style selector 84 | - each element has class 85 | - selector tries to match the longest definition starting from right 86 | - use wildcard * to match anything in between 87 | - example: 88 | - “text” matches any text 89 | - “button text” matches text inside buttons 90 | - “properties * button” matches button inside properties box/panel 91 | - “properties * button text” matches button text inside properties box/panel 92 | - selectors work using hashes 93 | - styles don’t cascade 94 | 95 | 96 | -------------------------------------------------------------------------------- /lib/nanovg/glnanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 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 | #ifndef GLNANOVG_H 19 | #define GLNANOVG_H 20 | 21 | struct NVGcontext* glnvgCreate(); 22 | void glnvgDelete(struct NVGcontext* ctx); 23 | 24 | #endif 25 | 26 | #ifdef GLNANOVG_IMPLEMENTATION 27 | 28 | #include 29 | #include "nanovg.h" 30 | 31 | enum GLNVGuniformLoc { 32 | GLNVG_LOC_SCISSORMAT, 33 | GLNVG_LOC_SCISSOREXT, 34 | GLNVG_LOC_PAINTMAT, 35 | GLNVG_LOC_EXTENT, 36 | GLNVG_LOC_RADIUS, 37 | GLNVG_LOC_FEATHER, 38 | GLNVG_LOC_INNERCOL, 39 | GLNVG_LOC_OUTERCOL, 40 | GLNVG_LOC_STROKEMULT, 41 | GLNVG_LOC_TEX, 42 | GLNVG_MAX_LOCS 43 | }; 44 | 45 | struct GLNVGshader { 46 | GLuint prog; 47 | GLuint frag; 48 | GLuint vert; 49 | GLint loc[GLNVG_MAX_LOCS]; 50 | }; 51 | 52 | struct GLNVGtexture { 53 | int id; 54 | GLuint tex; 55 | int width, height; 56 | int type; 57 | }; 58 | 59 | struct GLNVGcontext { 60 | struct GLNVGshader gradShader; 61 | struct GLNVGshader imgShader; 62 | struct GLNVGtexture* textures; 63 | int ntextures; 64 | int ctextures; 65 | int textureId; 66 | }; 67 | 68 | static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) 69 | { 70 | struct GLNVGtexture* tex = NULL; 71 | int i; 72 | 73 | for (i = 0; i < gl->ntextures; i++) { 74 | if (gl->textures[i].id == 0) { 75 | tex = &gl->textures[i]; 76 | break; 77 | } 78 | } 79 | if (tex == NULL) { 80 | if (gl->ntextures+1 > gl->ctextures) { 81 | gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2; 82 | gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); 83 | if (gl->textures == NULL) return NULL; 84 | } 85 | tex = &gl->textures[gl->ntextures++]; 86 | } 87 | 88 | memset(tex, 0, sizeof(*tex)); 89 | tex->id = ++gl->textureId; 90 | 91 | return tex; 92 | } 93 | 94 | static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id) 95 | { 96 | int i; 97 | for (i = 0; i < gl->ntextures; i++) 98 | if (gl->textures[i].id == id) 99 | return &gl->textures[i]; 100 | return NULL; 101 | } 102 | 103 | static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id) 104 | { 105 | int i; 106 | for (i = 0; i < gl->ntextures; i++) { 107 | if (gl->textures[i].id == id) { 108 | if (gl->textures[i].tex != 0) 109 | glDeleteTextures(1, &gl->textures[i].tex); 110 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); 111 | return 1; 112 | } 113 | } 114 | return 0; 115 | } 116 | 117 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) 118 | { 119 | char str[512+1]; 120 | int len = 0; 121 | glGetShaderInfoLog(shader, 512, &len, str); 122 | if (len > 512) len = 512; 123 | str[len] = '\0'; 124 | printf("Shader %s/%s error:\n%s\n", name, type, str); 125 | } 126 | 127 | static void glnvg__dumpProgramError(GLuint prog, const char* name) 128 | { 129 | char str[512+1]; 130 | int len = 0; 131 | glGetProgramInfoLog(prog, 512, &len, str); 132 | if (len > 512) len = 512; 133 | str[len] = '\0'; 134 | printf("Program %s error:\n%s\n", name, str); 135 | } 136 | 137 | static void glnvg__checkError(const char* str) 138 | { 139 | GLenum err = glGetError(); 140 | if (err != GL_NO_ERROR) { 141 | printf("Error %08x after %s\n", err, str); 142 | } 143 | } 144 | 145 | static int glnvg__createShader(struct GLNVGshader* shader, const char* name, const char* vshader, const char* fshader) 146 | { 147 | GLint status; 148 | GLuint prog, vert, frag; 149 | 150 | memset(shader, 0, sizeof(*shader)); 151 | 152 | prog = glCreateProgram(); 153 | vert = glCreateShader(GL_VERTEX_SHADER); 154 | frag = glCreateShader(GL_FRAGMENT_SHADER); 155 | glShaderSource(vert, 1, &vshader, 0); 156 | glShaderSource(frag, 1, &fshader, 0); 157 | 158 | glCompileShader(vert); 159 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); 160 | if (status != GL_TRUE) { 161 | glnvg__dumpShaderError(vert, name, "vert"); 162 | return 0; 163 | } 164 | 165 | glCompileShader(frag); 166 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); 167 | if (status != GL_TRUE) { 168 | glnvg__dumpShaderError(frag, name, "frag"); 169 | return 0; 170 | } 171 | 172 | glAttachShader(prog, vert); 173 | glAttachShader(prog, frag); 174 | glLinkProgram(prog); 175 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 176 | if (status != GL_TRUE) { 177 | glnvg__dumpProgramError(prog, name); 178 | return 0; 179 | } 180 | 181 | shader->prog = prog; 182 | shader->vert = vert; 183 | shader->frag = frag; 184 | 185 | return 1; 186 | } 187 | 188 | static void glnvg__deleteShader(struct GLNVGshader* shader) 189 | { 190 | if (shader->prog != 0) 191 | glDeleteProgram(shader->prog); 192 | if (shader->vert != 0) 193 | glDeleteShader(shader->vert); 194 | if (shader->frag != 0) 195 | glDeleteShader(shader->frag); 196 | } 197 | 198 | static void glnvg__getUniforms(struct GLNVGshader* shader) 199 | { 200 | shader->loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(shader->prog, "scissorMat"); 201 | shader->loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(shader->prog, "scissorExt"); 202 | shader->loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(shader->prog, "paintMat"); 203 | shader->loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(shader->prog, "extent"); 204 | shader->loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(shader->prog, "radius"); 205 | shader->loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(shader->prog, "feather"); 206 | shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol"); 207 | shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol"); 208 | shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult"); 209 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); 210 | } 211 | 212 | static int glnvg__renderCreate(void* uptr) 213 | { 214 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 215 | 216 | static const char* fillVertShader = 217 | "#version 120\n" 218 | "varying vec2 pos;\n" 219 | "varying vec2 alpha;\n" 220 | "void main(void) {\n" 221 | " alpha = gl_MultiTexCoord0.st;" 222 | " pos = gl_Vertex.xy;\n" 223 | " gl_Position = ftransform();\n" 224 | "}\n"; 225 | 226 | static const char* fillFragGradShader = 227 | "#version 120\n" 228 | "uniform mat3 scissorMat;\n" 229 | "uniform vec2 scissorExt;\n" 230 | "uniform mat3 paintMat;\n" 231 | "uniform vec2 extent;\n" 232 | "uniform float radius;\n" 233 | "uniform float feather;\n" 234 | "uniform vec4 innerCol;\n" 235 | "uniform vec4 outerCol;\n" 236 | "uniform float strokeMult;\n" 237 | "varying vec2 pos;\n" 238 | "varying vec2 alpha;\n" 239 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 240 | " vec2 ext2 = ext - vec2(rad,rad);\n" 241 | " vec2 d = abs(pt) - ext2;\n" 242 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 243 | "}\n" 244 | "void main(void) {\n" 245 | " // Scissoring\n" 246 | " vec2 sc = vec2(0.5,0.5) - (abs((scissorMat * vec3(pos,1.0)).xy) - scissorExt);\n" 247 | " float scissor = clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 248 | // " if (scissor < 0.001) discard;\n" 249 | " // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 250 | " float strokeAlpha = min(1.0, (1.0-abs(alpha.x*2.0-1.0))*strokeMult) * alpha.y;\n" 251 | " // Calculate gradient color using box gradient\n" 252 | " vec2 pt = (paintMat * vec3(pos,1.0)).xy;\n" 253 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 254 | " vec4 color = mix(innerCol,outerCol,d);\n" 255 | " // Combine alpha\n" 256 | " color.w *= strokeAlpha * scissor;\n" 257 | " gl_FragColor = color;\n" 258 | "}\n"; 259 | 260 | static const char* fillFragImgShader = 261 | "#version 120\n" 262 | "uniform mat3 scissorMat;\n" 263 | "uniform vec2 scissorExt;\n" 264 | "uniform mat3 paintMat;\n" 265 | "uniform vec2 extent;\n" 266 | "uniform float strokeMult;\n" 267 | "uniform sampler2D tex;\n" 268 | "varying vec2 pos;\n" 269 | "varying vec2 alpha;\n" 270 | "void main(void) {\n" 271 | " // Scissoring\n" 272 | " vec2 sc = vec2(0.5,0.5) - (abs((scissorMat * vec3(pos,1.0)).xy) - scissorExt);\n" 273 | " float scissor = clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 274 | // " if (scissor < 0.001) discard;\n" 275 | " // Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 276 | " float strokeAlpha = min(1.0, (1.0-abs(alpha.x*2.0-1.0))*strokeMult) * alpha.y;\n" 277 | " // Calculate color fron texture\n" 278 | " vec2 pt = (paintMat * vec3(pos,1.0)).xy;\n" 279 | " pt /= extent;\n" 280 | " vec4 color = texture2D(tex, pt);\n" 281 | " // Combine alpha\n" 282 | " color.w *= strokeAlpha * scissor;\n" 283 | " gl_FragColor = color;\n" 284 | "}\n"; 285 | 286 | glnvg__checkError("init"); 287 | 288 | if (glnvg__createShader(&gl->gradShader, "grad", fillVertShader, fillFragGradShader) == 0) 289 | return 0; 290 | if (glnvg__createShader(&gl->imgShader, "image", fillVertShader, fillFragImgShader) == 0) 291 | return 0; 292 | 293 | glnvg__checkError("uniform locations"); 294 | glnvg__getUniforms(&gl->gradShader); 295 | glnvg__getUniforms(&gl->imgShader); 296 | 297 | glnvg__checkError("done"); 298 | 299 | return 1; 300 | } 301 | 302 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const unsigned char* data) 303 | { 304 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 305 | struct GLNVGtexture* tex = glnvg__allocTexture(gl); 306 | if (tex == NULL) return 0; 307 | glGenTextures(1, &tex->tex); 308 | tex->width = w; 309 | tex->height = h; 310 | tex->type = type; 311 | glBindTexture(GL_TEXTURE_2D, tex->tex); 312 | 313 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 314 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 315 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 316 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 317 | 318 | if (type == NVG_TEXTURE_RGBA) 319 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 320 | else 321 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data); 322 | 323 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 324 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 325 | 326 | glnvg__checkError("create tex"); 327 | 328 | return tex->id; 329 | } 330 | 331 | static int glnvg__renderDeleteTexture(void* uptr, int image) 332 | { 333 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 334 | return glnvg__deleteTexture(gl, image); 335 | } 336 | 337 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 338 | { 339 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 340 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 341 | if (tex == NULL) return 0; 342 | glBindTexture(GL_TEXTURE_2D, tex->tex); 343 | 344 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 345 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 346 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 347 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 348 | 349 | if (tex->type == NVG_TEXTURE_RGBA) 350 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 351 | else 352 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_ALPHA, GL_UNSIGNED_BYTE, data); 353 | 354 | return 1; 355 | } 356 | 357 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) 358 | { 359 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 360 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 361 | if (tex == NULL) return 0; 362 | *w = tex->width; 363 | *h = tex->height; 364 | return 1; 365 | } 366 | 367 | static void glnvg__toFloatColor(float* fc, unsigned int c) 368 | { 369 | fc[0] = ((c) & 0xff) / 255.0f; 370 | fc[1] = ((c>>8) & 0xff) / 255.0f; 371 | fc[2] = ((c>>16) & 0xff) / 255.0f; 372 | fc[3] = ((c>>24) & 0xff) / 255.0f; 373 | } 374 | 375 | static void glnvg__xformIdentity(float* t) 376 | { 377 | t[0] = 1.0f; t[1] = 0.0f; 378 | t[2] = 0.0f; t[3] = 1.0f; 379 | t[4] = 0.0f; t[5] = 0.0f; 380 | } 381 | 382 | static void glnvg__xformInverse(float* inv, float* t) 383 | { 384 | double det = (double)t[0] * t[3] - (double)t[2] * t[1]; 385 | if (det > -1e-6 && det < -1e-6) { 386 | glnvg__xformIdentity(t); 387 | return; 388 | } 389 | double invdet = 1.0 / det; 390 | inv[0] = (float)(t[3] * invdet); 391 | inv[2] = (float)(-t[2] * invdet); 392 | inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); 393 | inv[1] = (float)(-t[1] * invdet); 394 | inv[3] = (float)(t[0] * invdet); 395 | inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); 396 | } 397 | 398 | static void glnvg__xformToMat3x3(float* m3, float* t) 399 | { 400 | m3[0] = t[0]; 401 | m3[1] = t[1]; 402 | m3[2] = 0.0f; 403 | m3[3] = t[2]; 404 | m3[4] = t[3]; 405 | m3[5] = 0.0f; 406 | m3[6] = t[4]; 407 | m3[7] = t[5]; 408 | m3[8] = 1.0f; 409 | } 410 | 411 | static int glnvg__setupPaint(struct GLNVGcontext* gl, struct NVGpaint* paint, struct NVGscissor* scissor, 412 | float width, float aasize) 413 | { 414 | float innerCol[4]; 415 | float outerCol[4]; 416 | glnvg__toFloatColor(innerCol, paint->innerColor); 417 | glnvg__toFloatColor(outerCol, paint->outerColor); 418 | struct GLNVGtexture* tex = NULL; 419 | float invxform[6], paintMat[9], scissorMat[9]; 420 | float scissorx = 0, scissory = 0; 421 | 422 | glnvg__xformInverse(invxform, paint->xform); 423 | glnvg__xformToMat3x3(paintMat, invxform); 424 | 425 | if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f) { 426 | memset(scissorMat, 0, sizeof(scissorMat)); 427 | scissorx = 1.0f; 428 | scissory = 1.0f; 429 | } else { 430 | glnvg__xformInverse(invxform, scissor->xform); 431 | glnvg__xformToMat3x3(scissorMat, invxform); 432 | scissorx = scissor->extent[0]; 433 | scissory = scissor->extent[1]; 434 | } 435 | 436 | if (paint->image != 0) { 437 | tex = glnvg__findTexture(gl, paint->image); 438 | if (tex == NULL) return 0; 439 | glUseProgram(gl->imgShader.prog); 440 | glUniformMatrix3fv(gl->imgShader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 441 | glUniform2f(gl->imgShader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 442 | glUniformMatrix3fv(gl->imgShader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 443 | glUniform2f(gl->imgShader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 444 | glUniform1f(gl->imgShader.loc[GLNVG_LOC_STROKEMULT], width*0.5f + aasize*0.5f); 445 | glUniform1i(gl->imgShader.loc[GLNVG_LOC_TEX], 0); 446 | glnvg__checkError("tex paint loc"); 447 | glBindTexture(GL_TEXTURE_2D, tex->tex); 448 | glEnable(GL_TEXTURE_2D); 449 | glnvg__checkError("tex paint tex"); 450 | } else { 451 | glUseProgram(gl->gradShader.prog); 452 | glUniformMatrix3fv(gl->gradShader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, scissorMat); 453 | glUniform2f(gl->gradShader.loc[GLNVG_LOC_SCISSOREXT], scissorx, scissory); 454 | glUniformMatrix3fv(gl->gradShader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, paintMat); 455 | glUniform2f(gl->gradShader.loc[GLNVG_LOC_EXTENT], paint->extent[0], paint->extent[1]); 456 | glUniform1f(gl->gradShader.loc[GLNVG_LOC_RADIUS], paint->radius); 457 | glUniform1f(gl->gradShader.loc[GLNVG_LOC_FEATHER], paint->feather); 458 | glUniform4fv(gl->gradShader.loc[GLNVG_LOC_INNERCOL], 1, innerCol); 459 | glUniform4fv(gl->gradShader.loc[GLNVG_LOC_OUTERCOL], 1, outerCol); 460 | glUniform1f(gl->gradShader.loc[GLNVG_LOC_STROKEMULT], width*0.5f + aasize*0.5f); 461 | glnvg__checkError("grad paint loc"); 462 | } 463 | return 1; 464 | } 465 | 466 | static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float aasize, 467 | const float* bounds, const struct NVGpath* paths, int npaths) 468 | { 469 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 470 | const struct NVGpath* path; 471 | int i; 472 | 473 | if (gl->gradShader.prog == 0) 474 | return; 475 | 476 | glEnable(GL_CULL_FACE); 477 | 478 | glEnableClientState(GL_VERTEX_ARRAY); 479 | 480 | // Draw shapes 481 | glDisable(GL_BLEND); 482 | glEnable(GL_STENCIL_TEST); 483 | glStencilMask(0xff); 484 | glStencilFunc(GL_ALWAYS, 0, ~0); 485 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 486 | 487 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 488 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 489 | glDisable(GL_CULL_FACE); 490 | for (i = 0; i < npaths; i++) { 491 | path = &paths[i]; 492 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->fill[0].x); 493 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 494 | } 495 | 496 | /* glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP); 497 | glCullFace(GL_BACK); 498 | 499 | for (i = 0; i < npaths; i++) { 500 | path = &paths[i]; 501 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->fill[0].x); 502 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 503 | } 504 | 505 | glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP); 506 | glCullFace(GL_FRONT); 507 | 508 | for (i = 0; i < npaths; i++) { 509 | path = &paths[i]; 510 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->fill[0].x); 511 | glDrawArrays(GL_TRIANGLE_FAN, 0, path->nfill); 512 | } 513 | glCullFace(GL_BACK);*/ 514 | 515 | glEnable(GL_CULL_FACE); 516 | 517 | // Draw aliased off-pixels 518 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 519 | glEnable(GL_BLEND); 520 | 521 | glStencilFunc(GL_EQUAL, 0x00, 0xff); 522 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 523 | 524 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 525 | 526 | glnvg__setupPaint(gl, paint, scissor, 1.0001f, aasize); 527 | 528 | // Draw fringes 529 | for (i = 0; i < npaths; i++) { 530 | path = &paths[i]; 531 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->stroke[0].x); 532 | glTexCoordPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->stroke[0].u); 533 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 534 | } 535 | 536 | glDisableClientState(GL_VERTEX_ARRAY); 537 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 538 | 539 | // Draw fill 540 | glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); 541 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 542 | 543 | glBegin(GL_QUADS); 544 | glTexCoord2f(0.5f,1.0f); 545 | glVertex2f(bounds[0], bounds[3]); 546 | glVertex2f(bounds[2], bounds[3]); 547 | glVertex2f(bounds[2], bounds[1]); 548 | glVertex2f(bounds[0], bounds[1]); 549 | glEnd(); 550 | 551 | glUseProgram(0); 552 | 553 | glDisable(GL_TEXTURE_2D); 554 | glDisable(GL_STENCIL_TEST); 555 | } 556 | 557 | static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float aasize, 558 | float width, const struct NVGpath* paths, int npaths) 559 | { 560 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 561 | const struct NVGpath* path; 562 | int i; 563 | 564 | if (gl->gradShader.prog == 0) 565 | return; 566 | 567 | glnvg__setupPaint(gl, paint, scissor, width, aasize); 568 | 569 | glEnable(GL_CULL_FACE); 570 | 571 | glEnableClientState(GL_VERTEX_ARRAY); 572 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 573 | 574 | // Draw Strokes 575 | for (i = 0; i < npaths; i++) { 576 | path = &paths[i]; 577 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->stroke[0].x); 578 | glTexCoordPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &path->stroke[0].u); 579 | glDrawArrays(GL_TRIANGLE_STRIP, 0, path->nstroke); 580 | } 581 | 582 | glDisableClientState(GL_VERTEX_ARRAY); 583 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 584 | 585 | glUseProgram(0); 586 | glDisable(GL_TEXTURE_2D); 587 | } 588 | 589 | static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, int image, 590 | const struct NVGvertex* verts, int nverts) 591 | { 592 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 593 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 594 | float color[4]; 595 | 596 | if (tex != NULL) { 597 | glBindTexture(GL_TEXTURE_2D, tex->tex); 598 | glEnable(GL_TEXTURE_2D); 599 | } 600 | 601 | glEnableClientState(GL_VERTEX_ARRAY); 602 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 603 | 604 | glnvg__toFloatColor(color, paint->innerColor); 605 | glColor4fv(color); 606 | 607 | glVertexPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &verts[0].x); 608 | glTexCoordPointer(2, GL_FLOAT, sizeof(struct NVGvertex), &verts[0].u); 609 | glDrawArrays(GL_TRIANGLES, 0, nverts); 610 | 611 | glDisableClientState(GL_VERTEX_ARRAY); 612 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 613 | 614 | glDisable(GL_TEXTURE_2D); 615 | } 616 | 617 | static void glnvg__renderDelete(void* uptr) 618 | { 619 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 620 | int i; 621 | if (gl == NULL) return; 622 | 623 | glnvg__deleteShader(&gl->gradShader); 624 | glnvg__deleteShader(&gl->imgShader); 625 | 626 | for (i = 0; i < gl->ntextures; i++) { 627 | if (gl->textures[i].tex != 0) 628 | glDeleteTextures(1, &gl->textures[i].tex); 629 | } 630 | free(gl->textures); 631 | 632 | free(gl); 633 | } 634 | 635 | 636 | struct NVGcontext* glnvgCreate(int atlasw, int atlash) 637 | { 638 | struct NVGparams params; 639 | struct NVGcontext* ctx = NULL; 640 | struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext)); 641 | if (gl == NULL) goto error; 642 | memset(gl, 0, sizeof(struct GLNVGcontext)); 643 | 644 | memset(¶ms, 0, sizeof(params)); 645 | params.renderCreate = glnvg__renderCreate; 646 | params.renderCreateTexture = glnvg__renderCreateTexture; 647 | params.renderDeleteTexture = glnvg__renderDeleteTexture; 648 | params.renderUpdateTexture = glnvg__renderUpdateTexture; 649 | params.renderGetTextureSize = glnvg__renderGetTextureSize; 650 | params.renderFill = glnvg__renderFill; 651 | params.renderStroke = glnvg__renderStroke; 652 | params.renderTriangles = glnvg__renderTriangles; 653 | params.renderDelete = glnvg__renderDelete; 654 | params.userPtr = gl; 655 | params.atlasWidth = atlasw; 656 | params.atlasHeight = atlash; 657 | 658 | ctx = nvgCreateInternal(¶ms); 659 | if (ctx == NULL) goto error; 660 | 661 | return ctx; 662 | 663 | error: 664 | if (gl != NULL) free(gl); 665 | if (ctx != NULL) nvgDeleteInternal(ctx); 666 | return NULL; 667 | } 668 | 669 | void glnvgDelete(struct NVGcontext* ctx) 670 | { 671 | nvgDeleteInternal(ctx); 672 | } 673 | 674 | #endif 675 | -------------------------------------------------------------------------------- /lib/nanovg/nanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 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 | #ifndef NANOVG_H 20 | #define NANOVG_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define NVG_PI 3.14159265358979323846264338327f 27 | 28 | struct NVGcontext; 29 | 30 | struct NVGcolor { 31 | union { 32 | float rgba[4]; 33 | struct { 34 | float r,g,b,a; 35 | }; 36 | }; 37 | }; 38 | 39 | struct NVGpaint { 40 | float xform[6]; 41 | float extent[2]; 42 | float radius; 43 | float feather; 44 | struct NVGcolor innerColor; 45 | struct NVGcolor outerColor; 46 | int image; 47 | int repeat; 48 | }; 49 | 50 | enum NVGwinding { 51 | NVG_CCW = 1, // Winding for solid shapes 52 | NVG_CW = 2, // Winding for holes 53 | NVG_REVERSE = 3, // Reverse winding 54 | NVG_RETAIN = 4, // Retain winding 55 | }; 56 | 57 | enum NVGsolidity { 58 | NVG_SOLID = 1, // CCW 59 | NVG_HOLE = 2, // CW 60 | }; 61 | 62 | enum NVGlineCap { 63 | NVG_BUTT, 64 | NVG_ROUND, 65 | NVG_SQUARE, 66 | NVG_BEVEL, 67 | NVG_MITER, 68 | }; 69 | 70 | enum NVGpatternRepeat { 71 | NVG_REPEATX = 0x01, // Repeat image pattern in X direction 72 | NVG_REPEATY = 0x02, // Repeat image pattern in Y direction 73 | }; 74 | 75 | enum NVGalign { 76 | // Horizontal align 77 | NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. 78 | NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. 79 | NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. 80 | // Vertical align 81 | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. 82 | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 83 | NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. 84 | NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. 85 | }; 86 | 87 | enum NVGalpha { 88 | NVG_STRAIGHT_ALPHA, 89 | NVG_PREMULTIPLIED_ALPHA, 90 | }; 91 | 92 | struct NVGglyphPosition { 93 | const char* str; // Position of the glyph in the input string. 94 | float x; // The x-coordinate of the logical glyph position. 95 | float minx, maxx; // The bounds of the glyph shape. 96 | }; 97 | 98 | struct NVGtextRow { 99 | const char* start; // Pointer to the input text where the row starts. 100 | const char* end; // Pointer to the input text where the row ends (one past the last character). 101 | const char* next; // Pointer to the beginning of the next row. 102 | float width; // Logical width of the row. 103 | float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. 104 | }; 105 | 106 | 107 | // Begin drawing a new frame 108 | // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() 109 | // nvgBeginFrame() defines the size of the window to render to in relation currently 110 | // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to 111 | // control the rendering on Hi-DPI devices. 112 | // For example, GLFW returns two dimension for an opened window: window size and 113 | // frame buffer size. In that case you would set windowWidth/Height to the window size 114 | // devicePixelRatio to: frameBufferWidth / windowWidth. 115 | // AlphaBlend controls if drawing the shapes to the render target should be done using straight or 116 | // premultiplied alpha. If rendering directly to framebuffer you probably want to use NVG_STRAIGHT_ALPHA, 117 | // if rendering to texture which should contain transparent regions NVG_PREMULTIPLIED_ALPHA is the 118 | // right choice. 119 | void nvgBeginFrame(struct NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio, int alphaBlend); 120 | 121 | // Ends drawing flushing remaining render state. 122 | void nvgEndFrame(struct NVGcontext* ctx); 123 | 124 | // 125 | // Color utils 126 | // 127 | // Colors in NanoVG are stored as unsigned ints in ABGR format. 128 | 129 | // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). 130 | struct NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); 131 | 132 | // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. 133 | struct NVGcolor nvgRGBf(float r, float g, float b); 134 | 135 | 136 | // Returns a color value from red, green, blue and alpha values. 137 | struct NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 138 | 139 | // Returns a color value from red, green, blue and alpha values. 140 | struct NVGcolor nvgRGBAf(float r, float g, float b, float a); 141 | 142 | 143 | // Linearly interpoaltes from color c0 to c1, and returns resulting color value. 144 | struct NVGcolor nvgLerpRGBA(struct NVGcolor c0, struct NVGcolor c1, float u); 145 | 146 | // Sets transparency of a color value. 147 | struct NVGcolor nvgTransRGBA(struct NVGcolor c0, unsigned char a); 148 | 149 | // Sets transparency of a color value. 150 | struct NVGcolor nvgTransRGBAf(struct NVGcolor c0, float a); 151 | 152 | // Returns color value specified by hue, saturation and lightness. 153 | // HSL values are all in range [0..1], alpha will be set to 255. 154 | struct NVGcolor nvgHSL(float h, float s, float l); 155 | 156 | // Returns color value specified by hue, saturation and lightness and alpha. 157 | // HSL values are all in range [0..1], alpha in range [0..255] 158 | struct NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); 159 | 160 | // 161 | // State Handling 162 | // 163 | // NanoVG contains state which represents how paths will be rendered. 164 | // The state contains transform, fill and stroke styles, text and font styles, 165 | // and scissor clipping. 166 | 167 | // Pushes and saves the current render state into a state stack. 168 | // A matching nvgRestore() must be used to restore the state. 169 | void nvgSave(struct NVGcontext* ctx); 170 | 171 | // Pops and restores current render state. 172 | void nvgRestore(struct NVGcontext* ctx); 173 | 174 | // Resets current render state to default values. Does not affect the render state stack. 175 | void nvgReset(struct NVGcontext* ctx); 176 | 177 | // 178 | // Render styles 179 | // 180 | // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. 181 | // Solid color is simply defined as a color value, different kinds of paints can be created 182 | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). 183 | // 184 | // Current render style can be saved and restored using nvgSave() and nvgRestore(). 185 | 186 | // Sets current stroke style to a solid color. 187 | void nvgStrokeColor(struct NVGcontext* ctx, struct NVGcolor color); 188 | 189 | // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. 190 | void nvgStrokePaint(struct NVGcontext* ctx, struct NVGpaint paint); 191 | 192 | // Sets current fill cstyle to a solid color. 193 | void nvgFillColor(struct NVGcontext* ctx, struct NVGcolor color); 194 | 195 | // Sets current fill style to a paint, which can be a one of the gradients or a pattern. 196 | void nvgFillPaint(struct NVGcontext* ctx, struct NVGpaint paint); 197 | 198 | // Sets the miter limit of the stroke style. 199 | // Miter limit controls when a sharp corner is beveled. 200 | void nvgMiterLimit(struct NVGcontext* ctx, float limit); 201 | 202 | // Sets the stroke witdth of the stroke style. 203 | void nvgStrokeWidth(struct NVGcontext* ctx, float size); 204 | 205 | // Sets how the end of the line (cap) is drawn, 206 | // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. 207 | void nvgLineCap(struct NVGcontext* ctx, int cap); 208 | 209 | // Sets how sharp path corners are drawn. 210 | // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. 211 | void nvgLineJoin(struct NVGcontext* ctx, int join); 212 | 213 | // 214 | // Transforms 215 | // 216 | // The paths, gradients, patterns and scissor region are transformed by an transformation 217 | // matrix at the time when they are passed to the API. 218 | // The current transformation matrix is a affine matrix: 219 | // [sx kx tx] 220 | // [ky sy ty] 221 | // [ 0 0 1] 222 | // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. 223 | // The last row is assumed to be 0,0,1 and is not stored. 224 | // 225 | // Apart from nvgResetTransform(), each transformation function first creates 226 | // specific transformation matrix and pre-multiplies the current transformation by it. 227 | // 228 | // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). 229 | 230 | // Resets current transform to a identity matrix. 231 | void nvgResetTransform(struct NVGcontext* ctx); 232 | 233 | // Premultiplies current coordinate system by specified matrix. 234 | // The parameters are interpreted as matrix as follows: 235 | // [a c e] 236 | // [b d f] 237 | // [0 0 1] 238 | void nvgTransform(struct NVGcontext* ctx, float a, float b, float c, float d, float e, float f); 239 | 240 | // Translates current coordinate system. 241 | void nvgTranslate(struct NVGcontext* ctx, float x, float y); 242 | 243 | // Rotates current coordinate system. Angle is specifid in radians. 244 | void nvgRotate(struct NVGcontext* ctx, float angle); 245 | 246 | // Skews the current coordinate system along X axis. Angle is specifid in radians. 247 | void nvgSkewX(struct NVGcontext* ctx, float angle); 248 | 249 | // Skews the current coordinate system along Y axis. Angle is specifid in radians. 250 | void nvgSkewY(struct NVGcontext* ctx, float angle); 251 | 252 | // Scales the current coordinat system. 253 | void nvgScale(struct NVGcontext* ctx, float x, float y); 254 | 255 | // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. 256 | // [a c e] 257 | // [b d f] 258 | // [0 0 1] 259 | // There should be space for 6 floats in the return buffer for the values a-f. 260 | void nvgCurrentTransform(struct NVGcontext* ctx, float* xform); 261 | 262 | 263 | // The following functions can be used to make calculations on 2x3 transformation matrices. 264 | // A 2x3 matrix is representated as float[6]. 265 | 266 | // Sets the transform to identity matrix. 267 | void nvgTransformIdentity(float* dst); 268 | 269 | // Sets the transform to translation matrix matrix. 270 | void nvgTransformTranslate(float* dst, float tx, float ty); 271 | 272 | // Sets the transform to scale matrix. 273 | void nvgTransformScale(float* dst, float sx, float sy); 274 | 275 | // Sets the transform to rotate matrix. Angle is specifid in radians. 276 | void nvgTransformRotate(float* dst, float a); 277 | 278 | // Sets the transform to skew-x matrix. Angle is specifid in radians. 279 | void nvgTransformSkewX(float* dst, float a); 280 | 281 | // Sets the transform to skew-y matrix. Angle is specifid in radians. 282 | void nvgTransformSkewY(float* dst, float a); 283 | 284 | // Sets the transform to the result of multiplication of two transforms, of A = A*B. 285 | void nvgTransformMultiply(float* dst, const float* src); 286 | 287 | // Sets the transform to the result of multiplication of two transforms, of A = B*A. 288 | void nvgTransformPremultiply(float* dst, const float* src); 289 | 290 | // Sets the destination to inverse of specified transform. 291 | // Returns 1 if the inverse could be calculated, else 0. 292 | int nvgTransformInverse(float* dst, const float* src); 293 | 294 | // Transform a point by given transform. 295 | void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); 296 | 297 | // Converts degress to radians and vice versa. 298 | float nvgDegToRad(float deg); 299 | float nvgRadToDeg(float rad); 300 | 301 | // 302 | // Images 303 | // 304 | // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. 305 | // In addition you can upload your own image. The image loading is provided by stb_image. 306 | 307 | // Creates image by loading it from the disk from specified file name. 308 | // Returns handle to the image. 309 | int nvgCreateImage(struct NVGcontext* ctx, const char* filename); 310 | 311 | // Creates image by loading it from the specified chunk of memory. 312 | // Returns handle to the image. 313 | int nvgCreateImageMem(struct NVGcontext* ctx, unsigned char* data, int ndata); 314 | 315 | // Creates image from specified image data. 316 | // Returns handle to the image. 317 | int nvgCreateImageRGBA(struct NVGcontext* ctx, int w, int h, const unsigned char* data); 318 | 319 | // Updates image data specified by image handle. 320 | void nvgUpdateImage(struct NVGcontext* ctx, int image, const unsigned char* data); 321 | 322 | // Returns the domensions of a created image. 323 | void nvgImageSize(struct NVGcontext* ctx, int image, int* w, int* h); 324 | 325 | // Deletes created image. 326 | void nvgDeleteImage(struct NVGcontext* ctx, int image); 327 | 328 | // 329 | // Paints 330 | // 331 | // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. 332 | // These can be used as paints for strokes and fills. 333 | 334 | // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates 335 | // of the linear gradient, icol specifies the start color and ocol the end color. 336 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 337 | struct NVGpaint nvgLinearGradient(struct NVGcontext* ctx, float sx, float sy, float ex, float ey, 338 | struct NVGcolor icol, struct NVGcolor ocol); 339 | 340 | // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering 341 | // drop shadows or hilights for boxes. Parameters (x,y) define the top-left corner of the rectangle, 342 | // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry 343 | // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. 344 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 345 | struct NVGpaint nvgBoxGradient(struct NVGcontext* ctx, float x, float y, float w, float h, 346 | float r, float f, struct NVGcolor icol, struct NVGcolor ocol); 347 | 348 | // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify 349 | // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. 350 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 351 | struct NVGpaint nvgRadialGradient(struct NVGcontext* ctx, float cx, float cy, float inr, float outr, 352 | struct NVGcolor icol, struct NVGcolor ocol); 353 | 354 | // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, 355 | // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render, 356 | // and repeat is combination of NVG_REPEATX and NVG_REPEATY which tells if the image should be repeated across x or y. 357 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 358 | struct NVGpaint nvgImagePattern(struct NVGcontext* ctx, float ox, float oy, float ex, float ey, 359 | float angle, int image, int repeat); 360 | 361 | // 362 | // Scissoring 363 | // 364 | // Scissoring allows you to clip the rendering into a rectangle. This is useful for varius 365 | // user interface cases like rendering a text edit or a timeline. 366 | 367 | // Sets the current 368 | // The scissor rectangle is transformed by the current transform. 369 | void nvgScissor(struct NVGcontext* ctx, float x, float y, float w, float h); 370 | 371 | // Reset and disables scissoring. 372 | void nvgResetScissor(struct NVGcontext* ctx); 373 | 374 | // 375 | // Paths 376 | // 377 | // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. 378 | // Then you define one or more paths and sub-paths which describe the shape. The are functions 379 | // to draw common shapes like rectangles and circles, and lower level step-by-step functions, 380 | // which allow to define a path curve by curve. 381 | // 382 | // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise 383 | // winding and holes should have counter clockwise order. To specify winding of a path you can 384 | // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. 385 | // 386 | // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it 387 | // with current stroke style by calling nvgStroke(). 388 | // 389 | // The curve segments and sub-paths are transformed by the current transform. 390 | 391 | // Clears the current path and sub-paths. 392 | void nvgBeginPath(struct NVGcontext* ctx); 393 | 394 | // Starts new sub-path with specified point as first point. 395 | void nvgMoveTo(struct NVGcontext* ctx, float x, float y); 396 | 397 | // Adds line segment from the last point in the path to the specified point. 398 | void nvgLineTo(struct NVGcontext* ctx, float x, float y); 399 | 400 | // Adds bezier segment from last point in the path via two control points to the specified point. 401 | void nvgBezierTo(struct NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 402 | 403 | // Adds an arc segment at the corner defined by the last path point, and two specified points. 404 | void nvgArcTo(struct NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); 405 | 406 | // Closes current sub-path with a line segment. 407 | void nvgClosePath(struct NVGcontext* ctx); 408 | 409 | // Sets the current sub-path winding, see NVGwinding and NVGsolidity. 410 | void nvgPathWinding(struct NVGcontext* ctx, int dir); 411 | 412 | // Creates new arc shaped sub-path. 413 | void nvgArc(struct NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); 414 | 415 | // Creates new rectangle shaped sub-path. 416 | void nvgRect(struct NVGcontext* ctx, float x, float y, float w, float h); 417 | 418 | // Creates new rounded rectangle shaped sub-path. 419 | void nvgRoundedRect(struct NVGcontext* ctx, float x, float y, float w, float h, float r); 420 | 421 | // Creates new ellipse shaped sub-path. 422 | void nvgEllipse(struct NVGcontext* ctx, float cx, float cy, float rx, float ry); 423 | 424 | // Creates new circle shaped sub-path. 425 | void nvgCircle(struct NVGcontext* ctx, float cx, float cy, float r); 426 | 427 | // Fills the current path with current fill style. 428 | void nvgFill(struct NVGcontext* ctx); 429 | 430 | // Fills the current path with current stroke style. 431 | void nvgStroke(struct NVGcontext* ctx); 432 | 433 | 434 | // 435 | // Text 436 | // 437 | // NanoVG allows you to load .ttf files and use the font to render text. 438 | // 439 | // The appearance of the text can be defined by setting the current text style 440 | // and by specifying the fill color. Common text and font settings such as 441 | // font size, letter spacing and text align are supported. Font blur allows you 442 | // to create simple text effects such as drop shadows. 443 | // 444 | // At render time the font face can be set based on the font handles or name. 445 | // 446 | // Font measure functions return values in local space, the calculations are 447 | // carried in the same resolution as the final rendering. This is done because 448 | // the text glyph positions are snapped to the nearest pixels sharp rendering. 449 | // 450 | // The local space means that values are not rotated or scale as per the current 451 | // transformation. For example if you set font size to 12, which would mean that 452 | // line height is 16, then regardless of the current scaling and rotation, the 453 | // returned line height is always 16. Some measures may vary because of the scaling 454 | // since aforementioned pixel snapping. 455 | // 456 | // While this may sound a little odd, the setup allows you to always render the 457 | // same way regardless of scaling. I.e. following works regardless of scaling: 458 | // 459 | // const char* txt = "Text me up."; 460 | // nvgTextBounds(vg, x,y, txt, NULL, bounds); 461 | // nvgBeginPath(vg); 462 | // nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); 463 | // nvgFill(vg); 464 | // 465 | // Note: currently only solid color fill is supported for text. 466 | 467 | // Creates font by loading it from the disk from specified file name. 468 | // Returns handle to the font. 469 | int nvgCreateFont(struct NVGcontext* ctx, const char* name, const char* filename); 470 | 471 | // Creates image by loading it from the specified memory chunk. 472 | // Returns handle to the font. 473 | int nvgCreateFontMem(struct NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); 474 | 475 | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. 476 | int nvgFindFont(struct NVGcontext* ctx, const char* name); 477 | 478 | // Sets the font size of current text style. 479 | void nvgFontSize(struct NVGcontext* ctx, float size); 480 | 481 | // Sets the blur of current text style. 482 | void nvgFontBlur(struct NVGcontext* ctx, float blur); 483 | 484 | // Sets the letter spacing of current text style. 485 | void nvgTextLetterSpacing(struct NVGcontext* ctx, float spacing); 486 | 487 | // Sets the proportional line height of current text style. The line height is specified as multiple of font size. 488 | void nvgTextLineHeight(struct NVGcontext* ctx, float lineHeight); 489 | 490 | // Sets the text align of current text style, see NVGaling for options. 491 | void nvgTextAlign(struct NVGcontext* ctx, int align); 492 | 493 | // Sets the font face based on specified id of current text style. 494 | void nvgFontFaceId(struct NVGcontext* ctx, int font); 495 | 496 | // Sets the font face based on specified name of current text style. 497 | void nvgFontFace(struct NVGcontext* ctx, const char* font); 498 | 499 | // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. 500 | float nvgText(struct NVGcontext* ctx, float x, float y, const char* string, const char* end); 501 | 502 | // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. 503 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 504 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 505 | void nvgTextBox(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); 506 | 507 | // Measures the specified text string. Parameter bounds should be a pointer to float[4], 508 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 509 | // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). 510 | // Measured values are returned in local coordinate space. 511 | float nvgTextBounds(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); 512 | 513 | // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], 514 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 515 | // Measured values are returned in local coordinate space. 516 | void nvgTextBoxBounds(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); 517 | 518 | // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. 519 | // Measured values are returned in local coordinate space. 520 | int nvgTextGlyphPositions(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, struct NVGglyphPosition* positions, int maxPositions); 521 | 522 | // Returns the vertical metrics based on the current text style. 523 | // Measured values are returned in local coordinate space. 524 | void nvgTextMetrics(struct NVGcontext* ctx, float* ascender, float* descender, float* lineh); 525 | 526 | // Breaks the specified text into lines. If end is specified only the sub-string will be used. 527 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 528 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 529 | int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, struct NVGtextRow* rows, int maxRows); 530 | 531 | // 532 | // Internal Render API 533 | // 534 | enum NVGtexture { 535 | NVG_TEXTURE_ALPHA = 0x01, 536 | NVG_TEXTURE_RGBA = 0x02, 537 | }; 538 | 539 | struct NVGscissor 540 | { 541 | float xform[6]; 542 | float extent[2]; 543 | }; 544 | 545 | struct NVGvertex { 546 | float x,y,u,v; 547 | }; 548 | 549 | struct NVGpath { 550 | int first; 551 | int count; 552 | unsigned char closed; 553 | int nbevel; 554 | struct NVGvertex* fill; 555 | int nfill; 556 | struct NVGvertex* stroke; 557 | int nstroke; 558 | int winding; 559 | int convex; 560 | }; 561 | 562 | struct NVGparams { 563 | void* userPtr; 564 | int atlasWidth, atlasHeight; 565 | int edgeAntiAlias; 566 | int (*renderCreate)(void* uptr); 567 | int (*renderCreateTexture)(void* uptr, int type, int w, int h, const unsigned char* data); 568 | int (*renderDeleteTexture)(void* uptr, int image); 569 | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); 570 | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); 571 | void (*renderViewport)(void* uptr, int width, int height, int alphaBlend); 572 | void (*renderFlush)(void* uptr, int alphaBlend); 573 | void (*renderFill)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, const float* bounds, const struct NVGpath* paths, int npaths); 574 | void (*renderStroke)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, float strokeWidth, const struct NVGpath* paths, int npaths); 575 | void (*renderTriangles)(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, const struct NVGvertex* verts, int nverts); 576 | void (*renderDelete)(void* uptr); 577 | }; 578 | 579 | // Contructor and destructor, called by the render back-end. 580 | struct NVGcontext* nvgCreateInternal(struct NVGparams* params); 581 | void nvgDeleteInternal(struct NVGcontext* ctx); 582 | 583 | // Debug function to dump cached path data. 584 | void nvgDebugDumpPathCache(struct NVGcontext* ctx); 585 | 586 | // Compiler references 587 | // http://sourceforge.net/p/predef/wiki/Compilers/ 588 | #if _MSC_VER >= 1800 589 | // VS 2013 seems to be too smart for school, it will still list the variable as unused if passed into sizeof(). 590 | #define NVG_NOTUSED(v) do { (void)(v); } while(0) 591 | #else 592 | #define NVG_NOTUSED(v) (void)sizeof(v) 593 | #endif 594 | 595 | #ifdef __cplusplus 596 | } 597 | #endif 598 | 599 | #endif // NANOVG_H 600 | -------------------------------------------------------------------------------- /lib/nanovg/nanovg_gl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 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 | #ifndef NANOVG_GL3_H 19 | #define NANOVG_GL3_H 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define NVG_ANTIALIAS 1 26 | 27 | #if defined NANOVG_GL2_IMPLEMENTATION 28 | # define NANOVG_GL2 1 29 | # define NANOVG_GL_IMPLEMENTATION 1 30 | #elif defined NANOVG_GL3_IMPLEMENTATION 31 | # define NANOVG_GL3 1 32 | # define NANOVG_GL_IMPLEMENTATION 1 33 | # define NANOVG_GL_USE_UNIFORMBUFFER 1 34 | #elif defined NANOVG_GLES2_IMPLEMENTATION 35 | # define NANOVG_GLES2 1 36 | # define NANOVG_GL_IMPLEMENTATION 1 37 | #elif defined NANOVG_GLES3_IMPLEMENTATION 38 | # define NANOVG_GLES3 1 39 | # define NANOVG_GL_IMPLEMENTATION 1 40 | #endif 41 | 42 | 43 | #if defined NANOVG_GL2 44 | 45 | struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa); 46 | void nvgDeleteGL2(struct NVGcontext* ctx); 47 | 48 | #elif defined NANOVG_GL3 49 | 50 | struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa); 51 | void nvgDeleteGL3(struct NVGcontext* ctx); 52 | 53 | #elif defined NANOVG_GLES2 54 | 55 | struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int edgeaa); 56 | void nvgDeleteGLES2(struct NVGcontext* ctx); 57 | 58 | #elif defined NANOVG_GLES3 59 | 60 | struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa); 61 | void nvgDeleteGLES3(struct NVGcontext* ctx); 62 | 63 | #endif 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #endif 70 | 71 | #ifdef NANOVG_GL_IMPLEMENTATION 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include "nanovg.h" 78 | 79 | enum GLNVGuniformLoc { 80 | #if NANOVG_GL_USE_UNIFORMBUFFER 81 | GLNVG_LOC_VIEW, 82 | GLNVG_LOC_FRAG, 83 | #else 84 | GLNVG_LOC_VIEWSIZE, 85 | GLNVG_LOC_SCISSORMAT, 86 | GLNVG_LOC_SCISSOREXT, 87 | GLNVG_LOC_SCISSORSCALE, 88 | GLNVG_LOC_PAINTMAT, 89 | GLNVG_LOC_EXTENT, 90 | GLNVG_LOC_RADIUS, 91 | GLNVG_LOC_FEATHER, 92 | GLNVG_LOC_INNERCOL, 93 | GLNVG_LOC_OUTERCOL, 94 | GLNVG_LOC_STROKEMULT, 95 | GLNVG_LOC_TEX, 96 | GLNVG_LOC_TEXTYPE, 97 | GLNVG_LOC_TYPE, 98 | #endif 99 | GLNVG_MAX_LOCS 100 | }; 101 | 102 | enum GLNVGshaderType { 103 | NSVG_SHADER_FILLGRAD, 104 | NSVG_SHADER_FILLIMG, 105 | NSVG_SHADER_SIMPLE, 106 | NSVG_SHADER_IMG 107 | }; 108 | 109 | #if NANOVG_GL_USE_UNIFORMBUFFER 110 | enum GLNVGuniformBindings { 111 | GLNVG_VIEW_BINDING = 0, 112 | GLNVG_FRAG_BINDING = 1, 113 | }; 114 | #endif 115 | 116 | struct GLNVGshader { 117 | GLuint prog; 118 | GLuint frag; 119 | GLuint vert; 120 | GLint loc[GLNVG_MAX_LOCS]; 121 | }; 122 | 123 | struct GLNVGtexture { 124 | int id; 125 | GLuint tex; 126 | int width, height; 127 | int type; 128 | }; 129 | 130 | enum GLNVGcallType { 131 | GLNVG_FILL, 132 | GLNVG_CONVEXFILL, 133 | GLNVG_STROKE, 134 | GLNVG_TRIANGLES, 135 | }; 136 | 137 | struct GLNVGcall { 138 | int type; 139 | int image; 140 | int pathOffset; 141 | int pathCount; 142 | int triangleOffset; 143 | int triangleCount; 144 | int uniformOffset; 145 | }; 146 | 147 | struct GLNVGpath { 148 | int fillOffset; 149 | int fillCount; 150 | int strokeOffset; 151 | int strokeCount; 152 | }; 153 | 154 | struct GLNVGfragUniforms { 155 | float scissorMat[12]; // matrices are actually 3 vec4s 156 | float paintMat[12]; 157 | struct NVGcolor innerCol; 158 | struct NVGcolor outerCol; 159 | float scissorExt[2]; 160 | float scissorScale[2]; 161 | float extent[2]; 162 | float radius; 163 | float feather; 164 | float strokeMult; 165 | int texType; 166 | int type; 167 | }; 168 | 169 | struct GLNVGcontext { 170 | struct GLNVGshader shader; 171 | struct GLNVGtexture* textures; 172 | float view[2]; 173 | int ntextures; 174 | int ctextures; 175 | int textureId; 176 | GLuint vertBuf; 177 | #if defined NANOVG_GL3 178 | GLuint vertArr; 179 | #endif 180 | #if NANOVG_GL_USE_UNIFORMBUFFER 181 | GLuint viewBuf; 182 | GLuint fragBuf; 183 | #endif 184 | int fragSize; 185 | int edgeAntiAlias; 186 | 187 | // Per frame buffers 188 | struct GLNVGcall* calls; 189 | int ccalls; 190 | int ncalls; 191 | struct GLNVGpath* paths; 192 | int cpaths; 193 | int npaths; 194 | struct NVGvertex* verts; 195 | int cverts; 196 | int nverts; 197 | unsigned char* uniforms; 198 | int cuniforms; 199 | int nuniforms; 200 | }; 201 | 202 | 203 | static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) 204 | { 205 | struct GLNVGtexture* tex = NULL; 206 | int i; 207 | 208 | for (i = 0; i < gl->ntextures; i++) { 209 | if (gl->textures[i].id == 0) { 210 | tex = &gl->textures[i]; 211 | break; 212 | } 213 | } 214 | if (tex == NULL) { 215 | if (gl->ntextures+1 > gl->ctextures) { 216 | gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2; 217 | gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); 218 | if (gl->textures == NULL) return NULL; 219 | } 220 | tex = &gl->textures[gl->ntextures++]; 221 | } 222 | 223 | memset(tex, 0, sizeof(*tex)); 224 | tex->id = ++gl->textureId; 225 | 226 | return tex; 227 | } 228 | 229 | static struct GLNVGtexture* glnvg__findTexture(struct GLNVGcontext* gl, int id) 230 | { 231 | int i; 232 | for (i = 0; i < gl->ntextures; i++) 233 | if (gl->textures[i].id == id) 234 | return &gl->textures[i]; 235 | return NULL; 236 | } 237 | 238 | static int glnvg__deleteTexture(struct GLNVGcontext* gl, int id) 239 | { 240 | int i; 241 | for (i = 0; i < gl->ntextures; i++) { 242 | if (gl->textures[i].id == id) { 243 | if (gl->textures[i].tex != 0) 244 | glDeleteTextures(1, &gl->textures[i].tex); 245 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); 246 | return 1; 247 | } 248 | } 249 | return 0; 250 | } 251 | 252 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) 253 | { 254 | char str[512+1]; 255 | int len = 0; 256 | glGetShaderInfoLog(shader, 512, &len, str); 257 | if (len > 512) len = 512; 258 | str[len] = '\0'; 259 | printf("Shader %s/%s error:\n%s\n", name, type, str); 260 | } 261 | 262 | static void glnvg__dumpProgramError(GLuint prog, const char* name) 263 | { 264 | char str[512+1]; 265 | int len = 0; 266 | glGetProgramInfoLog(prog, 512, &len, str); 267 | if (len > 512) len = 512; 268 | str[len] = '\0'; 269 | printf("Program %s error:\n%s\n", name, str); 270 | } 271 | 272 | static int glnvg__checkError(const char* str) 273 | { 274 | GLenum err = glGetError(); 275 | if (err != GL_NO_ERROR) { 276 | printf("Error %08x after %s\n", err, str); 277 | return 1; 278 | } 279 | return 0; 280 | } 281 | 282 | static int glnvg__createShader(struct GLNVGshader* shader, const char* name, const char* header, const char* opts, const char* vshader, const char* fshader) 283 | { 284 | GLint status; 285 | GLuint prog, vert, frag; 286 | const char* str[3]; 287 | str[0] = header; 288 | str[1] = opts != NULL ? opts : ""; 289 | 290 | memset(shader, 0, sizeof(*shader)); 291 | 292 | prog = glCreateProgram(); 293 | vert = glCreateShader(GL_VERTEX_SHADER); 294 | frag = glCreateShader(GL_FRAGMENT_SHADER); 295 | str[2] = vshader; 296 | glShaderSource(vert, 3, str, 0); 297 | str[2] = fshader; 298 | glShaderSource(frag, 3, str, 0); 299 | 300 | glCompileShader(vert); 301 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); 302 | if (status != GL_TRUE) { 303 | glnvg__dumpShaderError(vert, name, "vert"); 304 | return 0; 305 | } 306 | 307 | glCompileShader(frag); 308 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); 309 | if (status != GL_TRUE) { 310 | glnvg__dumpShaderError(frag, name, "frag"); 311 | return 0; 312 | } 313 | 314 | glAttachShader(prog, vert); 315 | glAttachShader(prog, frag); 316 | 317 | glBindAttribLocation(prog, 0, "vertex"); 318 | glBindAttribLocation(prog, 1, "tcoord"); 319 | 320 | glLinkProgram(prog); 321 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 322 | if (status != GL_TRUE) { 323 | glnvg__dumpProgramError(prog, name); 324 | return 0; 325 | } 326 | 327 | shader->prog = prog; 328 | shader->vert = vert; 329 | shader->frag = frag; 330 | 331 | return 1; 332 | } 333 | 334 | static void glnvg__deleteShader(struct GLNVGshader* shader) 335 | { 336 | if (shader->prog != 0) 337 | glDeleteProgram(shader->prog); 338 | if (shader->vert != 0) 339 | glDeleteShader(shader->vert); 340 | if (shader->frag != 0) 341 | glDeleteShader(shader->frag); 342 | } 343 | 344 | static void glnvg__getUniforms(struct GLNVGshader* shader) 345 | { 346 | #if NANOVG_GL_USE_UNIFORMBUFFER 347 | shader->loc[GLNVG_LOC_VIEW] = glGetUniformBlockIndex(shader->prog, "view"); 348 | shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag"); 349 | #else 350 | shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); 351 | shader->loc[GLNVG_LOC_SCISSORMAT] = glGetUniformLocation(shader->prog, "scissorMat"); 352 | shader->loc[GLNVG_LOC_SCISSOREXT] = glGetUniformLocation(shader->prog, "scissorExt"); 353 | shader->loc[GLNVG_LOC_SCISSORSCALE] = glGetUniformLocation(shader->prog, "scissorScale"); 354 | shader->loc[GLNVG_LOC_PAINTMAT] = glGetUniformLocation(shader->prog, "paintMat"); 355 | shader->loc[GLNVG_LOC_EXTENT] = glGetUniformLocation(shader->prog, "extent"); 356 | shader->loc[GLNVG_LOC_RADIUS] = glGetUniformLocation(shader->prog, "radius"); 357 | shader->loc[GLNVG_LOC_FEATHER] = glGetUniformLocation(shader->prog, "feather"); 358 | shader->loc[GLNVG_LOC_INNERCOL] = glGetUniformLocation(shader->prog, "innerCol"); 359 | shader->loc[GLNVG_LOC_OUTERCOL] = glGetUniformLocation(shader->prog, "outerCol"); 360 | shader->loc[GLNVG_LOC_STROKEMULT] = glGetUniformLocation(shader->prog, "strokeMult"); 361 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); 362 | shader->loc[GLNVG_LOC_TEXTYPE] = glGetUniformLocation(shader->prog, "texType"); 363 | shader->loc[GLNVG_LOC_TYPE] = glGetUniformLocation(shader->prog, "type"); 364 | #endif 365 | } 366 | 367 | static int glnvg__renderCreate(void* uptr) 368 | { 369 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 370 | int align = 4; 371 | 372 | // TODO: mediump float may not be enough for GLES2 in iOS. 373 | // see the following discussion: https://github.com/memononen/nanovg/issues/46 374 | static const char* shaderHeader = 375 | #if defined NANOVG_GL2 376 | "#define NANOVG_GL2 1\n"; 377 | #elif defined NANOVG_GL3 378 | "#version 150 core\n" 379 | #if NANOVG_GL_USE_UNIFORMBUFFER 380 | "#define USE_UNIFORMBUFFER 1\n" 381 | #endif 382 | "#define NANOVG_GL3 1\n"; 383 | #elif defined NANOVG_GLES2 384 | "#version 100\n" 385 | "precision mediump float;\n" 386 | "#define NANOVG_GL2 1\n"; 387 | #elif defined NANOVG_GLES3 388 | "#version 300 es\n" 389 | "precision mediump float;\n" 390 | "#define NANOVG_GL3 1\n"; 391 | #endif 392 | 393 | static const char* fillVertShader = 394 | "#ifdef NANOVG_GL3\n" 395 | "#ifdef USE_UNIFORMBUFFER\n" 396 | " layout(std140) uniform view {\n" 397 | " vec2 viewSize;\n" 398 | " };\n" 399 | "#else\n" 400 | " uniform vec2 viewSize;\n" 401 | "#endif\n" 402 | " in vec2 vertex;\n" 403 | " in vec2 tcoord;\n" 404 | " out vec2 ftcoord;\n" 405 | " out vec2 fpos;\n" 406 | "#else\n" 407 | " uniform vec2 viewSize;\n" 408 | " attribute vec2 vertex;\n" 409 | " attribute vec2 tcoord;\n" 410 | " varying vec2 ftcoord;\n" 411 | " varying vec2 fpos;\n" 412 | "#endif\n" 413 | "void main(void) {\n" 414 | " ftcoord = tcoord;\n" 415 | " fpos = vertex;\n" 416 | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" 417 | "}\n"; 418 | 419 | static const char* fillFragShader = 420 | "#ifdef NANOVG_GL3\n" 421 | "#ifdef USE_UNIFORMBUFFER\n" 422 | " layout(std140) uniform frag {\n" 423 | " mat3 scissorMat;\n" 424 | " mat3 paintMat;\n" 425 | " vec4 innerCol;\n" 426 | " vec4 outerCol;\n" 427 | " vec2 scissorExt;\n" 428 | " vec2 scissorScale;\n" 429 | " vec2 extent;\n" 430 | " float radius;\n" 431 | " float feather;\n" 432 | " float strokeMult;\n" 433 | " int texType;\n" 434 | " int type;\n" 435 | " };\n" 436 | "#else\n" 437 | " uniform mat3 scissorMat;\n" 438 | " uniform mat3 paintMat;\n" 439 | " uniform vec4 innerCol;\n" 440 | " uniform vec4 outerCol;\n" 441 | " uniform vec2 scissorExt;\n" 442 | " uniform vec2 scissorScale;\n" 443 | " uniform vec2 extent;\n" 444 | " uniform float radius;\n" 445 | " uniform float feather;\n" 446 | " uniform float strokeMult;\n" 447 | " uniform int texType;\n" 448 | " uniform int type;\n" 449 | "#endif\n" 450 | " uniform sampler2D tex;\n" 451 | " in vec2 ftcoord;\n" 452 | " in vec2 fpos;\n" 453 | " out vec4 outColor;\n" 454 | "#else\n" 455 | " uniform mat3 scissorMat;\n" 456 | " uniform mat3 paintMat;\n" 457 | " uniform vec4 innerCol;\n" 458 | " uniform vec4 outerCol;\n" 459 | " uniform vec2 scissorExt;\n" 460 | " uniform vec2 scissorScale;\n" 461 | " uniform vec2 extent;\n" 462 | " uniform float radius;\n" 463 | " uniform float feather;\n" 464 | " uniform float strokeMult;\n" 465 | " uniform int texType;\n" 466 | " uniform int type;\n" 467 | " uniform sampler2D tex;\n" 468 | " varying vec2 ftcoord;\n" 469 | " varying vec2 fpos;\n" 470 | "#endif\n" 471 | "\n" 472 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 473 | " vec2 ext2 = ext - vec2(rad,rad);\n" 474 | " vec2 d = abs(pt) - ext2;\n" 475 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 476 | "}\n" 477 | "\n" 478 | "// Scissoring\n" 479 | "float scissorMask(vec2 p) {\n" 480 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 481 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 482 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 483 | "}\n" 484 | "#ifdef EDGE_AA\n" 485 | "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 486 | "float strokeMask() {\n" 487 | " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" 488 | "}\n" 489 | "#endif\n" 490 | "\n" 491 | "void main(void) {\n" 492 | " vec4 result;\n" 493 | " float scissor = scissorMask(fpos);\n" 494 | "#ifdef EDGE_AA\n" 495 | " float strokeAlpha = strokeMask();\n" 496 | "#else\n" 497 | " float strokeAlpha = 1.0;\n" 498 | "#endif\n" 499 | " if (type == 0) { // Gradient\n" 500 | " // Calculate gradient color using box gradient\n" 501 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 502 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 503 | " vec4 color = mix(innerCol,outerCol,d);\n" 504 | " // Combine alpha\n" 505 | " color.w *= strokeAlpha * scissor;\n" 506 | " result = color;\n" 507 | " } else if (type == 1) { // Image\n" 508 | " // Calculate color fron texture\n" 509 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 510 | "#ifdef NANOVG_GL3\n" 511 | " vec4 color = texture(tex, pt);\n" 512 | "#else\n" 513 | " vec4 color = texture2D(tex, pt);\n" 514 | "#endif\n" 515 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 516 | " // Combine alpha\n" 517 | " color.w *= strokeAlpha * scissor;\n" 518 | " result = color;\n" 519 | " } else if (type == 2) { // Stencil fill\n" 520 | " result = vec4(1,1,1,1);\n" 521 | " } else if (type == 3) { // Textured tris\n" 522 | "#ifdef NANOVG_GL3\n" 523 | " vec4 color = texture(tex, ftcoord);\n" 524 | "#else\n" 525 | " vec4 color = texture2D(tex, ftcoord);\n" 526 | "#endif\n" 527 | " color = texType == 0 ? color : vec4(1,1,1,color.x);\n" 528 | " color.w *= scissor;\n" 529 | " result = color * innerCol;\n" 530 | " }\n" 531 | "#ifdef NANOVG_GL3\n" 532 | " outColor = result;\n" 533 | "#else\n" 534 | " gl_FragColor = result;\n" 535 | "#endif\n" 536 | "}\n"; 537 | 538 | glnvg__checkError("init"); 539 | 540 | if (gl->edgeAntiAlias) { 541 | if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0) 542 | return 0; 543 | } else { 544 | if (glnvg__createShader(&gl->shader, "shader", shaderHeader, NULL, fillVertShader, fillFragShader) == 0) 545 | return 0; 546 | } 547 | 548 | glnvg__checkError("uniform locations"); 549 | glnvg__getUniforms(&gl->shader); 550 | 551 | // Create dynamic vertex array 552 | #if defined NANOVG_GL3 553 | glGenVertexArrays(1, &gl->vertArr); 554 | #endif 555 | glGenBuffers(1, &gl->vertBuf); 556 | 557 | #if NANOVG_GL_USE_UNIFORMBUFFER 558 | // Create UBOs 559 | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_VIEW], GLNVG_VIEW_BINDING); 560 | glGenBuffers(1, &gl->viewBuf); 561 | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); 562 | glGenBuffers(1, &gl->fragBuf); 563 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); 564 | #endif 565 | gl->fragSize = sizeof(struct GLNVGfragUniforms) + align - sizeof(struct GLNVGfragUniforms) % align; 566 | 567 | glnvg__checkError("create done"); 568 | 569 | glFinish(); 570 | 571 | return 1; 572 | } 573 | 574 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const unsigned char* data) 575 | { 576 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 577 | struct GLNVGtexture* tex = glnvg__allocTexture(gl); 578 | 579 | if (tex == NULL) return 0; 580 | glGenTextures(1, &tex->tex); 581 | tex->width = w; 582 | tex->height = h; 583 | tex->type = type; 584 | glBindTexture(GL_TEXTURE_2D, tex->tex); 585 | 586 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 587 | #ifndef NANOVG_GLES2 588 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 589 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 590 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 591 | #endif 592 | 593 | if (type == NVG_TEXTURE_RGBA) 594 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 595 | else 596 | #if defined(NANOVG_GLES2) 597 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 598 | #elif defined(NANOVG_GLES3) 599 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 600 | #else 601 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 602 | #endif 603 | 604 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 605 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 606 | 607 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 608 | #ifndef NANOVG_GLES2 609 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 610 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 611 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 612 | #endif 613 | 614 | if (glnvg__checkError("create tex")) 615 | return 0; 616 | 617 | glBindTexture(GL_TEXTURE_2D, 0); 618 | 619 | return tex->id; 620 | } 621 | 622 | 623 | static int glnvg__renderDeleteTexture(void* uptr, int image) 624 | { 625 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 626 | return glnvg__deleteTexture(gl, image); 627 | } 628 | 629 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 630 | { 631 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 632 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 633 | 634 | if (tex == NULL) return 0; 635 | glBindTexture(GL_TEXTURE_2D, tex->tex); 636 | 637 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 638 | 639 | #ifndef NANOVG_GLES2 640 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 641 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 642 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 643 | #else 644 | // No support for all of skip, need to update a whole row at a time. 645 | if (tex->type == NVG_TEXTURE_RGBA) 646 | data += y*tex->width*4; 647 | else 648 | data += y*tex->width; 649 | x = 0; 650 | w = tex->width; 651 | #endif 652 | 653 | if (tex->type == NVG_TEXTURE_RGBA) 654 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 655 | else 656 | #ifdef NANOVG_GLES2 657 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 658 | #else 659 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); 660 | #endif 661 | 662 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 663 | #ifndef NANOVG_GLES2 664 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 665 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 666 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 667 | #endif 668 | 669 | glBindTexture(GL_TEXTURE_2D, 0); 670 | 671 | return 1; 672 | } 673 | 674 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) 675 | { 676 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 677 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 678 | if (tex == NULL) return 0; 679 | *w = tex->width; 680 | *h = tex->height; 681 | return 1; 682 | } 683 | 684 | static void glnvg__xformToMat3x4(float* m3, float* t) 685 | { 686 | m3[0] = t[0]; 687 | m3[1] = t[1]; 688 | m3[2] = 0.0f; 689 | m3[3] = 0.0f; 690 | m3[4] = t[2]; 691 | m3[5] = t[3]; 692 | m3[6] = 0.0f; 693 | m3[7] = 0.0f; 694 | m3[8] = t[4]; 695 | m3[9] = t[5]; 696 | m3[10] = 1.0f; 697 | m3[11] = 0.0f; 698 | } 699 | 700 | static int glnvg__convertPaint(struct GLNVGcontext* gl, struct GLNVGfragUniforms* frag, struct NVGpaint* paint, 701 | struct NVGscissor* scissor, float width, float fringe) 702 | { 703 | struct GLNVGtexture* tex = NULL; 704 | float invxform[6]; 705 | 706 | memset(frag, 0, sizeof(*frag)); 707 | 708 | frag->innerCol = paint->innerColor; 709 | frag->outerCol = paint->outerColor; 710 | 711 | nvgTransformInverse(invxform, paint->xform); 712 | glnvg__xformToMat3x4(frag->paintMat, invxform); 713 | 714 | if (scissor->extent[0] < 0.5f || scissor->extent[1] < 0.5f) { 715 | memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); 716 | frag->scissorExt[0] = 1.0f; 717 | frag->scissorExt[1] = 1.0f; 718 | frag->scissorScale[0] = 1.0f; 719 | frag->scissorScale[1] = 1.0f; 720 | } else { 721 | nvgTransformInverse(invxform, scissor->xform); 722 | glnvg__xformToMat3x4(frag->scissorMat, invxform); 723 | frag->scissorExt[0] = scissor->extent[0]; 724 | frag->scissorExt[1] = scissor->extent[1]; 725 | frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; 726 | frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; 727 | } 728 | memcpy(frag->extent, paint->extent, sizeof(frag->extent)); 729 | frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; 730 | 731 | if (paint->image != 0) { 732 | tex = glnvg__findTexture(gl, paint->image); 733 | if (tex == NULL) return 0; 734 | frag->type = NSVG_SHADER_FILLIMG; 735 | frag->texType = tex->type == NVG_TEXTURE_RGBA ? 0 : 1; 736 | } else { 737 | frag->type = NSVG_SHADER_FILLGRAD; 738 | frag->radius = paint->radius; 739 | frag->feather = paint->feather; 740 | } 741 | return 1; 742 | } 743 | 744 | static struct GLNVGfragUniforms* nvg__fragUniformPtr(struct GLNVGcontext* gl, int i); 745 | 746 | #if !NANOVG_GL_USE_UNIFORMBUFFER 747 | static void glnvg__mat3(float* dst, float* src) 748 | { 749 | dst[0] = src[0]; 750 | dst[1] = src[1]; 751 | dst[2] = src[2]; 752 | 753 | dst[3] = src[4]; 754 | dst[4] = src[5]; 755 | dst[5] = src[6]; 756 | 757 | dst[6] = src[8]; 758 | dst[7] = src[9]; 759 | dst[8] = src[10]; 760 | } 761 | #endif 762 | 763 | static void glnvg__setUniforms(struct GLNVGcontext* gl, int uniformOffset, int image) 764 | { 765 | #if NANOVG_GL_USE_UNIFORMBUFFER 766 | glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(struct GLNVGfragUniforms)); 767 | #else 768 | struct GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset); 769 | float tmp[9]; // Maybe there's a way to get rid of this... 770 | glnvg__mat3(tmp, frag->scissorMat); 771 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_SCISSORMAT], 1, GL_FALSE, tmp); 772 | glnvg__mat3(tmp, frag->paintMat); 773 | glUniformMatrix3fv(gl->shader.loc[GLNVG_LOC_PAINTMAT], 1, GL_FALSE, tmp); 774 | glUniform4fv(gl->shader.loc[GLNVG_LOC_INNERCOL], 1, frag->innerCol.rgba); 775 | glUniform4fv(gl->shader.loc[GLNVG_LOC_OUTERCOL], 1, frag->outerCol.rgba); 776 | glUniform2fv(gl->shader.loc[GLNVG_LOC_SCISSOREXT], 1, frag->scissorExt); 777 | glUniform2fv(gl->shader.loc[GLNVG_LOC_SCISSORSCALE], 1, frag->scissorScale); 778 | glUniform2fv(gl->shader.loc[GLNVG_LOC_EXTENT], 1, frag->extent); 779 | glUniform1f(gl->shader.loc[GLNVG_LOC_RADIUS], frag->radius); 780 | glUniform1f(gl->shader.loc[GLNVG_LOC_FEATHER], frag->feather); 781 | glUniform1f(gl->shader.loc[GLNVG_LOC_STROKEMULT], frag->strokeMult); 782 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEXTYPE], frag->texType); 783 | glUniform1i(gl->shader.loc[GLNVG_LOC_TYPE], frag->type); 784 | #endif 785 | 786 | if (image != 0) { 787 | struct GLNVGtexture* tex = glnvg__findTexture(gl, image); 788 | glBindTexture(GL_TEXTURE_2D, tex != NULL ? tex->tex : 0); 789 | glnvg__checkError("tex paint tex"); 790 | } else { 791 | glBindTexture(GL_TEXTURE_2D, 0); 792 | } 793 | } 794 | 795 | static void glnvg__renderViewport(void* uptr, int width, int height, int alphaBlend) 796 | { 797 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 798 | NVG_NOTUSED(alphaBlend); 799 | gl->view[0] = (float)width; 800 | gl->view[1] = (float)height; 801 | } 802 | 803 | static void glnvg__fill(struct GLNVGcontext* gl, struct GLNVGcall* call) 804 | { 805 | struct GLNVGpath* paths = &gl->paths[call->pathOffset]; 806 | int i, npaths = call->pathCount; 807 | 808 | // Draw shapes 809 | glDisable(GL_BLEND); 810 | glEnable(GL_STENCIL_TEST); 811 | glStencilMask(0xff); 812 | glStencilFunc(GL_ALWAYS, 0, ~0L); 813 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 814 | 815 | // set bindpoint for solid loc 816 | glnvg__setUniforms(gl, call->uniformOffset, 0); 817 | glnvg__checkError("fill simple"); 818 | 819 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 820 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 821 | glDisable(GL_CULL_FACE); 822 | for (i = 0; i < npaths; i++) 823 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); 824 | glEnable(GL_CULL_FACE); 825 | 826 | // Draw aliased off-pixels 827 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 828 | glEnable(GL_BLEND); 829 | 830 | glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); 831 | glnvg__checkError("fill fill"); 832 | 833 | if (gl->edgeAntiAlias) { 834 | glStencilFunc(GL_EQUAL, 0x00, 0xff); 835 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 836 | // Draw fringes 837 | for (i = 0; i < npaths; i++) 838 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 839 | } 840 | 841 | // Draw fill 842 | glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); 843 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 844 | glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); 845 | 846 | glDisable(GL_STENCIL_TEST); 847 | } 848 | 849 | static void glnvg__convexFill(struct GLNVGcontext* gl, struct GLNVGcall* call) 850 | { 851 | struct GLNVGpath* paths = &gl->paths[call->pathOffset]; 852 | int i, npaths = call->pathCount; 853 | 854 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 855 | glnvg__checkError("convex fill"); 856 | 857 | for (i = 0; i < npaths; i++) 858 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); 859 | if (gl->edgeAntiAlias) { 860 | // Draw fringes 861 | for (i = 0; i < npaths; i++) 862 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 863 | } 864 | } 865 | 866 | static void glnvg__stroke(struct GLNVGcontext* gl, struct GLNVGcall* call) 867 | { 868 | struct GLNVGpath* paths = &gl->paths[call->pathOffset]; 869 | int npaths = call->pathCount, i; 870 | 871 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 872 | glnvg__checkError("stroke fill"); 873 | 874 | // Draw Strokes 875 | for (i = 0; i < npaths; i++) 876 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 877 | } 878 | 879 | static void glnvg__triangles(struct GLNVGcontext* gl, struct GLNVGcall* call) 880 | { 881 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 882 | glnvg__checkError("triangles fill"); 883 | 884 | glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); 885 | } 886 | 887 | static void glnvg__renderFlush(void* uptr, int alphaBlend) 888 | { 889 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 890 | int i; 891 | 892 | if (gl->ncalls > 0) { 893 | 894 | // Setup require GL state. 895 | glUseProgram(gl->shader.prog); 896 | 897 | if (alphaBlend == NVG_PREMULTIPLIED_ALPHA) 898 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 899 | else 900 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 901 | glEnable(GL_CULL_FACE); 902 | glCullFace(GL_BACK); 903 | glEnable(GL_BLEND); 904 | glDisable(GL_DEPTH_TEST); 905 | glDisable(GL_SCISSOR_TEST); 906 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 907 | glStencilMask(0xffffffff); 908 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 909 | glStencilFunc(GL_ALWAYS, 0, 0xffffffff); 910 | glActiveTexture(GL_TEXTURE0); 911 | 912 | #if NANOVG_GL_USE_UNIFORMBUFFER 913 | // Upload ubo for frag shaders 914 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); 915 | glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW); 916 | #endif 917 | 918 | // Upload vertex data 919 | #if defined NANOVG_GL3 920 | glBindVertexArray(gl->vertArr); 921 | #endif 922 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 923 | glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(struct NVGvertex), gl->verts, GL_STREAM_DRAW); 924 | glEnableVertexAttribArray(0); 925 | glEnableVertexAttribArray(1); 926 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(size_t)0); 927 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(struct NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); 928 | 929 | #if NANOVG_GL_USE_UNIFORMBUFFER 930 | // once per frame set ubo for view 931 | glBindBuffer(GL_UNIFORM_BUFFER, gl->viewBuf); 932 | glBufferData(GL_UNIFORM_BUFFER, sizeof(gl->view), 0, GL_STREAM_DRAW); 933 | glBufferData(GL_UNIFORM_BUFFER, sizeof(gl->view), gl->view, GL_STREAM_DRAW); 934 | glBindBufferBase(GL_UNIFORM_BUFFER, GLNVG_VIEW_BINDING, gl->viewBuf); 935 | 936 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); 937 | #else 938 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 939 | glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view); 940 | #endif 941 | 942 | for (i = 0; i < gl->ncalls; i++) { 943 | struct GLNVGcall* call = &gl->calls[i]; 944 | if (call->type == GLNVG_FILL) 945 | glnvg__fill(gl, call); 946 | else if (call->type == GLNVG_CONVEXFILL) 947 | glnvg__convexFill(gl, call); 948 | else if (call->type == GLNVG_STROKE) 949 | glnvg__stroke(gl, call); 950 | else if (call->type == GLNVG_TRIANGLES) 951 | glnvg__triangles(gl, call); 952 | } 953 | 954 | glDisableVertexAttribArray(0); 955 | glDisableVertexAttribArray(1); 956 | #if defined NANOVG_GL3 957 | glBindVertexArray(0); 958 | #endif 959 | glUseProgram(0); 960 | glBindTexture(GL_TEXTURE_2D, 0); 961 | } 962 | 963 | // Reset calls 964 | gl->nverts = 0; 965 | gl->npaths = 0; 966 | gl->ncalls = 0; 967 | gl->nuniforms = 0; 968 | } 969 | 970 | static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) 971 | { 972 | int i, count = 0; 973 | for (i = 0; i < npaths; i++) { 974 | count += paths[i].nfill; 975 | count += paths[i].nstroke; 976 | } 977 | return count; 978 | } 979 | 980 | static int glnvg__maxi(int a, int b) { return a > b ? a : b; } 981 | 982 | static struct GLNVGcall* glnvg__allocCall(struct GLNVGcontext* gl) 983 | { 984 | struct GLNVGcall* ret = NULL; 985 | if (gl->ncalls+1 > gl->ccalls) { 986 | gl->ccalls = gl->ccalls == 0 ? 32 : gl->ccalls * 2; 987 | gl->calls = (struct GLNVGcall*)realloc(gl->calls, sizeof(struct GLNVGcall) * gl->ccalls); 988 | } 989 | ret = &gl->calls[gl->ncalls++]; 990 | memset(ret, 0, sizeof(struct GLNVGcall)); 991 | return ret; 992 | } 993 | 994 | static int glnvg__allocPaths(struct GLNVGcontext* gl, int n) 995 | { 996 | int ret = 0; 997 | if (gl->npaths+n > gl->cpaths) { 998 | gl->cpaths = gl->cpaths == 0 ? glnvg__maxi(n, 32) : gl->cpaths * 2; 999 | gl->paths = (struct GLNVGpath*)realloc(gl->paths, sizeof(struct GLNVGpath) * gl->cpaths); 1000 | } 1001 | ret = gl->npaths; 1002 | gl->npaths += n; 1003 | return ret; 1004 | } 1005 | 1006 | static int glnvg__allocVerts(struct GLNVGcontext* gl, int n) 1007 | { 1008 | int ret = 0; 1009 | if (gl->nverts+n > gl->cverts) { 1010 | gl->cverts = gl->cverts == 0 ? glnvg__maxi(n, 256) : gl->cverts * 2; 1011 | gl->verts = (struct NVGvertex*)realloc(gl->verts, sizeof(struct NVGvertex) * gl->cverts); 1012 | } 1013 | ret = gl->nverts; 1014 | gl->nverts += n; 1015 | return ret; 1016 | } 1017 | 1018 | static int glnvg__allocFragUniforms(struct GLNVGcontext* gl, int n) 1019 | { 1020 | int ret = 0, structSize = gl->fragSize; 1021 | if (gl->nuniforms+n > gl->cuniforms) { 1022 | gl->cuniforms = gl->cuniforms == 0 ? glnvg__maxi(n, 32) : gl->cuniforms * 2; 1023 | gl->uniforms = (unsigned char*)realloc(gl->uniforms, gl->cuniforms * structSize); 1024 | } 1025 | ret = gl->nuniforms * structSize; 1026 | gl->nuniforms += n; 1027 | return ret; 1028 | } 1029 | 1030 | static struct GLNVGfragUniforms* nvg__fragUniformPtr(struct GLNVGcontext* gl, int i) 1031 | { 1032 | return (struct GLNVGfragUniforms*)&gl->uniforms[i]; 1033 | } 1034 | 1035 | static void glnvg__vset(struct NVGvertex* vtx, float x, float y, float u, float v) 1036 | { 1037 | vtx->x = x; 1038 | vtx->y = y; 1039 | vtx->u = u; 1040 | vtx->v = v; 1041 | } 1042 | 1043 | static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 1044 | const float* bounds, const struct NVGpath* paths, int npaths) 1045 | { 1046 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 1047 | struct GLNVGcall* call = glnvg__allocCall(gl); 1048 | struct NVGvertex* quad; 1049 | struct GLNVGfragUniforms* frag; 1050 | int i, maxverts, offset; 1051 | 1052 | call->type = GLNVG_FILL; 1053 | call->pathOffset = glnvg__allocPaths(gl, npaths); 1054 | call->pathCount = npaths; 1055 | call->image = paint->image; 1056 | 1057 | if (npaths == 1 && paths[0].convex) 1058 | call->type = GLNVG_CONVEXFILL; 1059 | 1060 | // Allocate vertices for all the paths. 1061 | maxverts = glnvg__maxVertCount(paths, npaths) + 6; 1062 | offset = glnvg__allocVerts(gl, maxverts); 1063 | 1064 | for (i = 0; i < npaths; i++) { 1065 | struct GLNVGpath* copy = &gl->paths[call->pathOffset + i]; 1066 | const struct NVGpath* path = &paths[i]; 1067 | memset(copy, 0, sizeof(struct GLNVGpath)); 1068 | if (path->nfill > 0) { 1069 | copy->fillOffset = offset; 1070 | copy->fillCount = path->nfill; 1071 | memcpy(&gl->verts[offset], path->fill, sizeof(struct NVGvertex) * path->nfill); 1072 | offset += path->nfill; 1073 | } 1074 | if (path->nstroke > 0) { 1075 | copy->strokeOffset = offset; 1076 | copy->strokeCount = path->nstroke; 1077 | memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke); 1078 | offset += path->nstroke; 1079 | } 1080 | } 1081 | 1082 | // Quad 1083 | call->triangleOffset = offset; 1084 | call->triangleCount = 6; 1085 | quad = &gl->verts[call->triangleOffset]; 1086 | glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f); 1087 | glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f); 1088 | glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f); 1089 | 1090 | glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f); 1091 | glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f); 1092 | glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f); 1093 | 1094 | // Setup uniforms for draw calls 1095 | if (call->type == GLNVG_FILL) { 1096 | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); 1097 | // Simple shader for stencil 1098 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); 1099 | memset(frag, 0, sizeof(*frag)); 1100 | frag->type = NSVG_SHADER_SIMPLE; 1101 | // Fill shader 1102 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe); 1103 | } else { 1104 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1105 | // Fill shader 1106 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe); 1107 | } 1108 | } 1109 | 1110 | static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, 1111 | float strokeWidth, const struct NVGpath* paths, int npaths) 1112 | { 1113 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 1114 | struct GLNVGcall* call = glnvg__allocCall(gl); 1115 | int i, maxverts, offset; 1116 | 1117 | call->type = GLNVG_STROKE; 1118 | call->pathOffset = glnvg__allocPaths(gl, npaths); 1119 | call->pathCount = npaths; 1120 | call->image = paint->image; 1121 | 1122 | // Allocate vertices for all the paths. 1123 | maxverts = glnvg__maxVertCount(paths, npaths); 1124 | offset = glnvg__allocVerts(gl, maxverts); 1125 | 1126 | for (i = 0; i < npaths; i++) { 1127 | struct GLNVGpath* copy = &gl->paths[call->pathOffset + i]; 1128 | const struct NVGpath* path = &paths[i]; 1129 | memset(copy, 0, sizeof(struct GLNVGpath)); 1130 | if (path->nstroke) { 1131 | copy->strokeOffset = offset; 1132 | copy->strokeCount = path->nstroke; 1133 | memcpy(&gl->verts[offset], path->stroke, sizeof(struct NVGvertex) * path->nstroke); 1134 | offset += path->nstroke; 1135 | } 1136 | } 1137 | 1138 | // Fill shader 1139 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1140 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe); 1141 | } 1142 | 1143 | static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, 1144 | const struct NVGvertex* verts, int nverts) 1145 | { 1146 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 1147 | struct GLNVGcall* call = glnvg__allocCall(gl); 1148 | struct GLNVGfragUniforms* frag; 1149 | 1150 | call->type = GLNVG_TRIANGLES; 1151 | call->image = paint->image; 1152 | 1153 | // Allocate vertices for all the paths. 1154 | call->triangleOffset = glnvg__allocVerts(gl, nverts); 1155 | call->triangleCount = nverts; 1156 | memcpy(&gl->verts[call->triangleOffset], verts, sizeof(struct NVGvertex) * nverts); 1157 | 1158 | // Fill shader 1159 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1160 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); 1161 | glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f); 1162 | frag->type = NSVG_SHADER_IMG; 1163 | } 1164 | 1165 | static void glnvg__renderDelete(void* uptr) 1166 | { 1167 | struct GLNVGcontext* gl = (struct GLNVGcontext*)uptr; 1168 | int i; 1169 | if (gl == NULL) return; 1170 | 1171 | glnvg__deleteShader(&gl->shader); 1172 | 1173 | #if NANOVG_GL3 1174 | #if NANOVG_GL_USE_UNIFORMBUFFER 1175 | if (gl->viewBuf != 0) 1176 | glDeleteBuffers(1, &gl->viewBuf); 1177 | if (gl->fragBuf != 0) 1178 | glDeleteBuffers(1, &gl->fragBuf); 1179 | #endif 1180 | if (gl->vertArr != 0) 1181 | glDeleteVertexArrays(1, &gl->vertArr); 1182 | #endif 1183 | if (gl->vertBuf != 0) 1184 | glDeleteBuffers(1, &gl->vertBuf); 1185 | 1186 | for (i = 0; i < gl->ntextures; i++) { 1187 | if (gl->textures[i].tex != 0) 1188 | glDeleteTextures(1, &gl->textures[i].tex); 1189 | } 1190 | free(gl->textures); 1191 | 1192 | free(gl); 1193 | } 1194 | 1195 | 1196 | #if defined NANOVG_GL2 1197 | struct NVGcontext* nvgCreateGL2(int atlasw, int atlash, int edgeaa) 1198 | #elif defined NANOVG_GL3 1199 | struct NVGcontext* nvgCreateGL3(int atlasw, int atlash, int edgeaa) 1200 | #elif defined NANOVG_GLES2 1201 | struct NVGcontext* nvgCreateGLES2(int atlasw, int atlash, int edgeaa) 1202 | #elif defined NANOVG_GLES3 1203 | struct NVGcontext* nvgCreateGLES3(int atlasw, int atlash, int edgeaa) 1204 | #endif 1205 | { 1206 | struct NVGparams params; 1207 | struct NVGcontext* ctx = NULL; 1208 | struct GLNVGcontext* gl = (struct GLNVGcontext*)malloc(sizeof(struct GLNVGcontext)); 1209 | if (gl == NULL) goto error; 1210 | memset(gl, 0, sizeof(struct GLNVGcontext)); 1211 | 1212 | memset(¶ms, 0, sizeof(params)); 1213 | params.renderCreate = glnvg__renderCreate; 1214 | params.renderCreateTexture = glnvg__renderCreateTexture; 1215 | params.renderDeleteTexture = glnvg__renderDeleteTexture; 1216 | params.renderUpdateTexture = glnvg__renderUpdateTexture; 1217 | params.renderGetTextureSize = glnvg__renderGetTextureSize; 1218 | params.renderViewport = glnvg__renderViewport; 1219 | params.renderFlush = glnvg__renderFlush; 1220 | params.renderFill = glnvg__renderFill; 1221 | params.renderStroke = glnvg__renderStroke; 1222 | params.renderTriangles = glnvg__renderTriangles; 1223 | params.renderDelete = glnvg__renderDelete; 1224 | params.userPtr = gl; 1225 | params.atlasWidth = atlasw; 1226 | params.atlasHeight = atlash; 1227 | params.edgeAntiAlias = edgeaa; 1228 | 1229 | gl->edgeAntiAlias = edgeaa; 1230 | 1231 | ctx = nvgCreateInternal(¶ms); 1232 | if (ctx == NULL) goto error; 1233 | 1234 | return ctx; 1235 | 1236 | error: 1237 | // 'gl' is freed by nvgDeleteInternal. 1238 | if (ctx != NULL) nvgDeleteInternal(ctx); 1239 | return NULL; 1240 | } 1241 | 1242 | #if NANOVG_GL2 1243 | void nvgDeleteGL2(struct NVGcontext* ctx) 1244 | #elif NANOVG_GL3 1245 | void nvgDeleteGL3(struct NVGcontext* ctx) 1246 | #elif NANOVG_GLES2 1247 | void nvgDeleteGLES2(struct NVGcontext* ctx) 1248 | #elif NANOVG_GLES3 1249 | void nvgDeleteGLES3(struct NVGcontext* ctx) 1250 | #endif 1251 | { 1252 | nvgDeleteInternal(ctx); 1253 | } 1254 | 1255 | #endif 1256 | -------------------------------------------------------------------------------- /premake4.lua: -------------------------------------------------------------------------------- 1 | 2 | local action = _ACTION or "" 3 | 4 | solution "mgui" 5 | location ( "build" ) 6 | configurations { "Debug", "Release" } 7 | platforms {"native", "x64", "x32"} 8 | 9 | project "example" 10 | kind "ConsoleApp" 11 | language "C" 12 | files { "example/example.c", "src/mgui.c", "lib/nanovg/*" } 13 | includedirs { "src", "lib/nanovg", "lib/nanosvg" } 14 | targetdir("build") 15 | 16 | configuration { "linux" } 17 | links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" } 18 | 19 | configuration { "windows" } 20 | links { "glu32","opengl32", "gdi32", "winmm", "user32" } 21 | 22 | configuration { "macosx" } 23 | links { "glfw3" } 24 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } 25 | 26 | configuration "Debug" 27 | defines { "DEBUG" } 28 | flags { "Symbols", "ExtraWarnings"} 29 | 30 | configuration "Release" 31 | defines { "NDEBUG" } 32 | flags { "Optimize", "ExtraWarnings"} 33 | 34 | project "example2" 35 | kind "ConsoleApp" 36 | language "C" 37 | files { "example/example2.c", "src/milli.c", "lib/nanovg/*" } 38 | includedirs { "src", "lib/nanovg", "lib/nanosvg" } 39 | targetdir("build") 40 | 41 | configuration { "linux" } 42 | links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" } 43 | 44 | configuration { "windows" } 45 | links { "glu32","opengl32", "gdi32", "winmm", "user32" } 46 | 47 | configuration { "macosx" } 48 | links { "glfw3" } 49 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } 50 | 51 | configuration "Debug" 52 | defines { "DEBUG" } 53 | flags { "Symbols", "ExtraWarnings"} 54 | 55 | configuration "Release" 56 | defines { "NDEBUG" } 57 | flags { "Optimize", "ExtraWarnings"} 58 | 59 | project "example3" 60 | kind "ConsoleApp" 61 | language "C" 62 | files { "example/example3.c", "src/milli2.c", "lib/nanovg/*" } 63 | includedirs { "src", "lib/nanovg", "lib/nanosvg" } 64 | targetdir("build") 65 | 66 | configuration { "linux" } 67 | links { "X11","Xrandr", "rt", "GL", "GLU", "pthread" } 68 | 69 | configuration { "windows" } 70 | links { "glu32","opengl32", "gdi32", "winmm", "user32" } 71 | 72 | configuration { "macosx" } 73 | links { "glfw3" } 74 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } 75 | 76 | configuration "Debug" 77 | defines { "DEBUG" } 78 | flags { "Symbols", "ExtraWarnings"} 79 | 80 | configuration "Release" 81 | defines { "NDEBUG" } 82 | flags { "Optimize", "ExtraWarnings"} 83 | -------------------------------------------------------------------------------- /src/mgui.h: -------------------------------------------------------------------------------- 1 | #ifndef MGUI_H 2 | #define MGUI_H 3 | 4 | struct NVGcontext; 5 | 6 | int mgInit(); 7 | void mgTerminate(); 8 | 9 | enum MUImouseButton { 10 | MG_MOUSE_PRESSED = 1 << 0, 11 | MG_MOUSE_RELEASED = 1 << 1, 12 | }; 13 | 14 | enum MUIoverflow { 15 | MG_FIT, 16 | MG_HIDDEN, 17 | MG_SCROLL, 18 | MG_VISIBLE, 19 | }; 20 | 21 | enum MUIdir { 22 | MG_ROW, 23 | MG_COL, 24 | }; 25 | 26 | enum MUIalign { 27 | MG_START, 28 | MG_END, 29 | MG_CENTER, 30 | MG_JUSTIFY, 31 | }; 32 | 33 | #define MG_AUTO_SIZE 0xffff 34 | 35 | enum MGargTypes { 36 | MG_NONE = 0, // 0 37 | MG_WIDTH_ARG, 38 | MG_HEIGHT_ARG, 39 | MG_PROPWIDTH_ARG, 40 | MG_PROPHEIGHT_ARG, 41 | MG_SPACING_ARG, 42 | MG_PADDINGX_ARG, 43 | MG_PADDINGY_ARG, 44 | MG_GROW_ARG, 45 | MG_ALIGN_ARG, 46 | MG_PACK_ARG, // 10 47 | MG_OVERFLOW_ARG, 48 | MG_FONTSIZE_ARG, 49 | MG_LINEHEIGHT_ARG, 50 | MG_TEXTALIGN_ARG, 51 | MG_LOGIC_ARG, 52 | MG_STYLE_ARG, 53 | MG_CONTENTCOLOR_ARG, 54 | MG_FILLCOLOR_ARG, 55 | MG_BORDERCOLOR_ARG, 56 | MG_BORDERSIZE_ARG, 57 | MG_CORNERRADIUS_ARG, // 20 58 | MG_TAG_ARG, 59 | MG_ANCHOR_ARG, 60 | MG_PROPX_ARG, 61 | MG_PROPY_ARG, 62 | MG_X_ARG, 63 | MG_Y_ARG, 64 | }; 65 | 66 | enum MGlogicType { 67 | MG_CLICK = 1, 68 | MG_DRAG = 2, 69 | MG_TYPE = 3, 70 | }; 71 | 72 | 73 | #define mgOverflow(v) (mgPackOpt(MG_OVERFLOW_ARG, (v))) 74 | #define mgAlign(v) (mgPackOpt(MG_ALIGN_ARG, (v))) 75 | #define mgPack(v) (mgPackOpt(MG_PACK_ARG, (v))) 76 | #define mgGrow(v) (mgPackOpt(MG_GROW_ARG, (v))) 77 | 78 | #define mgWidth(v) (mgPackOptf(MG_WIDTH_ARG, (v))) 79 | #define mgHeight(v) (mgPackOptf(MG_HEIGHT_ARG, (v))) 80 | #define mgPropWidth(v) (mgPackOptf(MG_PROPWIDTH_ARG, (v))) 81 | #define mgPropHeight(v) (mgPackOptf(MG_PROPHEIGHT_ARG, (v))) 82 | #define mgPosition(a,b,x,y) (mgPackOpt2(MG_ANCHOR_ARG, (a), (b))), (mgPackOptf(MG_X_ARG, (x))), (mgPackOptf(MG_Y_ARG, (y))) 83 | #define mgPropPosition(a,b,x,y) (mgPackOpt2(MG_ANCHOR_ARG, (a), (b))), (mgPackOptf(MG_PROPX_ARG, (x))), (mgPackOptf(MG_PROPY_ARG, (y))) 84 | 85 | #define mgPaddingX(v) (mgPackOpt(MG_PADDINGX_ARG, (v))) 86 | #define mgPaddingY(v) (mgPackOpt(MG_PADDINGY_ARG, (v))) 87 | #define mgPadding(x,y) (mgPackOpt(MG_PADDINGX_ARG, (x))), (mgPackOpt(MG_PADDINGY_ARG, (y))) 88 | #define mgSpacing(v) (mgPackOpt(MG_SPACING_ARG, (v))) 89 | 90 | #define mgFontSize(v) (mgPackOpt(MG_FONTSIZE_ARG, (v))) 91 | #define mgTextAlign(v) (mgPackOpt(MG_TEXTALIGN_ARG, (v))) 92 | #define mgLineHeight(v) (mgPackOptf(MG_LINEHEIGHT_ARG, (v))) 93 | 94 | #define mgLogic(v) (mgPackOpt(MG_LOGIC_ARG, (v))) 95 | 96 | #define mgContentColor(r,g,b,a) (mgPackOpt(MG_CONTENTCOLOR_ARG, mgRGBA((r),(g),(b),(a)))) 97 | #define mgFillColor(r,g,b,a) (mgPackOpt(MG_FILLCOLOR_ARG, mgRGBA((r),(g),(b),(a)))) 98 | #define mgBorderColor(r,g,b,a) (mgPackOpt(MG_BORDERCOLOR_ARG, mgRGBA((r),(g),(b),(a)))) 99 | #define mgBorderSize(v) (mgPackOpt(MG_BORDERSIZE_ARG, (v))) 100 | #define mgCornerRadius(v) (mgPackOpt(MG_CORNERRADIUS_ARG, (v))) 101 | #define mgTag(v) (mgPackOptStr(MG_TAG_ARG, (v))) 102 | 103 | struct MGopt { 104 | unsigned char type; 105 | unsigned char units; 106 | union { 107 | float fval; 108 | int ival; 109 | char* sval; 110 | }; 111 | struct MGopt* next; 112 | }; 113 | 114 | struct MGstyle { 115 | unsigned int set; 116 | 117 | float width; 118 | float height; 119 | float x; 120 | float y; 121 | 122 | unsigned int contentColor; 123 | unsigned int fillColor; 124 | unsigned int borderColor; 125 | 126 | unsigned char cornerRadius; 127 | unsigned char borderSize; 128 | 129 | unsigned char spacing; 130 | unsigned char paddingx; 131 | unsigned char paddingy; 132 | 133 | unsigned char grow; 134 | unsigned char align; 135 | unsigned char pack; 136 | unsigned char overflow; 137 | 138 | unsigned char fontSize; 139 | unsigned char textAlign; 140 | float lineHeight; 141 | 142 | unsigned char logic; 143 | unsigned char anchor; 144 | }; 145 | 146 | 147 | #define mgOpts(...) mgOpts_(0, ##__VA_ARGS__, NULL) 148 | struct MGopt* mgOpts_(unsigned int dummy, ...); 149 | struct MGopt* mgPackOpt(unsigned char arg, int v); 150 | struct MGopt* mgPackOptf(unsigned char arg, float v); 151 | struct MGopt* mgPackOpt2(unsigned char arg, int x, int y); 152 | struct MGopt* mgPackOptStr(unsigned char arg, const char* str); 153 | unsigned int mgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 154 | unsigned int mgCreateStyle(const char* selector, struct MGopt* normal, struct MGopt* hover, struct MGopt* active, struct MGopt* focus); 155 | 156 | struct MGrect { 157 | float x, y, width, height; 158 | }; 159 | 160 | struct MGhit { 161 | float mx, my; 162 | float deltamx, deltamy; 163 | float localmx, localmy; 164 | int mods; 165 | unsigned int code; 166 | int clickCount; 167 | 168 | struct MGrect rect; 169 | struct MGrect view; 170 | struct MGstyle style; 171 | }; 172 | 173 | enum MGwidgetType { 174 | MG_BOX, 175 | MG_PANEL, 176 | MG_POPUP, 177 | MG_PARAGRAPH, 178 | MG_TEXT, 179 | MG_ICON, 180 | MG_INPUT, 181 | MG_CANVAS, 182 | }; 183 | 184 | enum MGwidgetState { 185 | MG_NORMAL = 0, 186 | MG_HOVER = 1<<0, 187 | MG_ACTIVE = 1<<1, 188 | MG_FOCUS = 1<<2, 189 | }; 190 | 191 | enum MGwidgetEvent { 192 | MG_FOCUSED, 193 | MG_BLURRED, 194 | MG_CLICKED, 195 | MG_PRESSED, 196 | MG_RELEASED, 197 | MG_DRAGGED, 198 | MG_ENTERED, 199 | MG_EXITED, 200 | MG_KEYPRESSED, 201 | MG_KEYRELEASED, 202 | MG_CHARTYPED, 203 | }; 204 | 205 | struct MGwidget; 206 | 207 | typedef void (*MGcanvasRenderFun)(void* uptr, struct MGwidget* w, struct NVGcontext* vg, const float* view); 208 | typedef void (*MGcanvasLogicFun)(void* uptr, struct MGwidget* w, int event, struct MGhit* hit); 209 | 210 | struct MGwidget { 211 | unsigned int id; 212 | float x, y, width, height; 213 | float cwidth, cheight; 214 | 215 | struct MGstyle style; 216 | 217 | unsigned char dir; 218 | unsigned char type; 219 | unsigned char state; 220 | unsigned char stop; 221 | 222 | unsigned char active; 223 | unsigned char bubble; 224 | 225 | union { 226 | struct { 227 | struct MGicon* icon; 228 | } icon; 229 | char* text; 230 | }; 231 | 232 | MGcanvasLogicFun logic; 233 | MGcanvasRenderFun render; 234 | void* uptr; 235 | int uptrsize; 236 | 237 | const char* tag; 238 | 239 | struct MGwidget* next; 240 | struct MGwidget* parent; 241 | struct MGwidget* children; 242 | }; 243 | 244 | #define MG_MAX_INPUTKEYS 32 245 | struct MGkeyPress { 246 | int type, code, mods; 247 | }; 248 | struct MGinputState 249 | { 250 | float mx, my; 251 | int mbut; 252 | struct MGkeyPress keys[MG_MAX_INPUTKEYS]; 253 | int nkeys; 254 | }; 255 | 256 | void mgFrameBegin(struct NVGcontext* vg, int width, int height, struct MGinputState* input, float dt); 257 | void mgFrameEnd(); 258 | 259 | int mgCreateIcon(const char* name, const char* filename); 260 | 261 | unsigned int mgPanelBegin(int dir, float x, float y, int zidx, struct MGopt* opts); 262 | unsigned int mgPanelEnd(); 263 | 264 | unsigned int mgBoxBegin(int dir, struct MGopt* opts); 265 | unsigned int mgBoxEnd(); 266 | unsigned int mgBox(struct MGopt* opts); 267 | 268 | unsigned int mgText(const char* text, struct MGopt* opts); 269 | unsigned int mgParagraph(const char* text, struct MGopt* opts); 270 | unsigned int mgIcon(const char* name, struct MGopt* opts); 271 | unsigned int mgInput(char* text, int maxtext, struct MGopt* opts); 272 | 273 | unsigned int mgCanvas(float width, float height, MGcanvasLogicFun logic, MGcanvasRenderFun render, struct MGopt* opts); 274 | 275 | // Derivative 276 | unsigned int mgNumber(float* value, struct MGopt* opts); 277 | unsigned int mgSelect(int* value, const char** choices, int nchoises, struct MGopt* opts); 278 | unsigned int mgLabel(const char* text, struct MGopt* opts); 279 | unsigned int mgNumber3(float* x, float* y, float* z, const char* units, struct MGopt* opts); 280 | unsigned int mgColor(float* r, float* g, float* b, float* a, struct MGopt* opts); 281 | unsigned int mgCheckBox(const char* text, int* value, struct MGopt* opts); 282 | unsigned int mgButton(const char* text, struct MGopt* opts); 283 | unsigned int mgIconButton(const char* icon, const char* text, struct MGopt* opts); 284 | unsigned int mgItem(const char* text, struct MGopt* opts); 285 | unsigned int mgSlider(float* value, float vmin, float vmax, struct MGopt* opts); 286 | unsigned int mgProgress(float progress, struct MGopt* opts); 287 | unsigned int mgScrollBar(float* offset, float contentSize, float viewSize, struct MGopt* opts); 288 | 289 | unsigned int mgPopupBegin(unsigned int target, int trigger, int dir, struct MGopt* opts); 290 | unsigned int mgPopupEnd(); 291 | void mgShowPopup(unsigned int id, int show); 292 | 293 | unsigned int mgTooltip(unsigned int target, const char* message, struct MGopt* opts); 294 | 295 | int mgClicked(unsigned int id); 296 | int mgPressed(unsigned int id); 297 | int mgDragged(unsigned int id); 298 | int mgReleased(unsigned int id); 299 | int mgBlurred(unsigned int id); 300 | int mgFocused(unsigned int id); 301 | int mgExited(unsigned int id); 302 | int mgEntered(unsigned int id); 303 | int mgChanged(unsigned int id); 304 | 305 | int mgIsActive(unsigned int id); 306 | int mgIsHover(unsigned int id); 307 | int mgIsFocus(unsigned int id); 308 | 309 | void mgFocus(unsigned int id); 310 | void mgFocusNext(unsigned int id); 311 | void mgFocusPrev(unsigned int id); 312 | void mgBlur(unsigned int id); 313 | 314 | 315 | #endif // MGUI_H 316 | -------------------------------------------------------------------------------- /src/milli.c: -------------------------------------------------------------------------------- 1 | #include "milli.h" 2 | #include "nanovg.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "nanosvg.h" 8 | 9 | 10 | static int mini(int a, int b) { return a < b ? a : b; } 11 | static int maxi(int a, int b) { return a > b ? a : b; } 12 | static float minf(float a, float b) { return a < b ? a : b; } 13 | static float maxf(float a, float b) { return a > b ? a : b; } 14 | static float clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } 15 | static float absf(float a) { return a < 0.0f ? -a : a; } 16 | 17 | static struct NVGcolor milli__nvgColMilli(struct MIcolor col) 18 | { 19 | struct NVGcolor c; 20 | c.r = col.r / 255.0f; 21 | c.g = col.g / 255.0f; 22 | c.b = col.b / 255.0f; 23 | c.a = col.a / 255.0f; 24 | return c; 25 | } 26 | 27 | static struct NVGcolor milli__nvgColUint(unsigned int col) 28 | { 29 | struct NVGcolor c; 30 | c.r = (col & 0xff) / 255.0f; 31 | c.g = ((col >> 8) & 0xff) / 255.0f; 32 | c.b = ((col >> 16) & 0xff) / 255.0f; 33 | c.a = ((col >> 24) & 0xff) / 255.0f; 34 | return c; 35 | } 36 | 37 | 38 | // TODO move resource handling to render abstraction. 39 | #define MI_MAX_ICONS 100 40 | struct MIiconImage 41 | { 42 | char* name; 43 | struct NSVGimage* image; 44 | }; 45 | static struct MIiconImage* icons[MI_MAX_ICONS]; 46 | static int iconCount = 0; 47 | 48 | static struct MIiconImage* findIcon(const char* name) 49 | { 50 | int i = 0; 51 | for (i = 0; i < iconCount; i++) { 52 | if (strcmp(icons[i]->name, name) == 0) 53 | return icons[i]; 54 | } 55 | printf("Could not find icon '%s'\n", name); 56 | return NULL; 57 | } 58 | 59 | static void milli__scaleIcon(struct NSVGimage* image, float scale) 60 | { 61 | int i; 62 | struct NSVGshape* shape = NULL; 63 | struct NSVGpath* path = NULL; 64 | image->width *= scale; 65 | image->height *= scale; 66 | for (shape = image->shapes; shape != NULL; shape = shape->next) { 67 | for (path = shape->paths; path != NULL; path = path->next) { 68 | path->bounds[0] *= scale; 69 | path->bounds[1] *= scale; 70 | path->bounds[2] *= scale; 71 | path->bounds[3] *= scale; 72 | for (i = 0; i < path->npts; i++) { 73 | path->pts[i*2+0] *= scale; 74 | path->pts[i*2+1] *= scale; 75 | } 76 | } 77 | } 78 | // TODO scale gradients. 79 | } 80 | 81 | int miCreateIconImage(const char* name, const char* filename, float scale) 82 | { 83 | struct MIiconImage* icon = NULL; 84 | 85 | if (iconCount >= MI_MAX_ICONS) 86 | return -1; 87 | 88 | icon = (struct MIiconImage*)malloc(sizeof(struct MIiconImage)); 89 | if (icon == NULL) goto error; 90 | memset(icon, 0, sizeof(struct MIiconImage)); 91 | 92 | icon->name = (char*)malloc(strlen(name)+1); 93 | if (icon->name == NULL) goto error; 94 | strcpy(icon->name, name); 95 | 96 | icon->image = nsvgParseFromFile(filename, "px", 96.0f); 97 | if (icon->image == NULL) goto error; 98 | 99 | // Scale 100 | if (scale > 0.0f) 101 | milli__scaleIcon(icon->image, scale); 102 | 103 | icons[iconCount++] = icon; 104 | 105 | return 0; 106 | 107 | error: 108 | if (icon != NULL) { 109 | if (icon->name != NULL) 110 | free(icon->name); 111 | if (icon->image != NULL) 112 | nsvgDelete(icon->image); 113 | free(icon); 114 | } 115 | return -1; 116 | } 117 | 118 | static void deleteIcons() 119 | { 120 | int i; 121 | for (i = 0; i < iconCount; i++) { 122 | if (icons[i]->image != NULL) 123 | nsvgDelete(icons[i]->image); 124 | free(icons[i]->name); 125 | free(icons[i]); 126 | } 127 | iconCount = 0; 128 | } 129 | 130 | static void drawIcon(struct NVGcontext* vg, struct MIrect* rect, struct NSVGimage* image, struct MIcolor* color) 131 | { 132 | int i; 133 | struct NSVGshape* shape = NULL; 134 | struct NSVGpath* path; 135 | float sx, sy, s; 136 | 137 | if (image == NULL) return; 138 | 139 | if (color != NULL) { 140 | nvgFillColor(vg, milli__nvgColMilli(*color)); 141 | nvgStrokeColor(vg, milli__nvgColMilli(*color)); 142 | } 143 | sx = rect->width / image->width; 144 | sy = rect->height / image->height; 145 | s = minf(sx, sy); 146 | 147 | nvgSave(vg); 148 | nvgTranslate(vg, rect->x + rect->width/2, rect->y + rect->height/2); 149 | nvgScale(vg, s, s); 150 | nvgTranslate(vg, -image->width/2, -image->height/2); 151 | 152 | for (shape = image->shapes; shape != NULL; shape = shape->next) { 153 | if (shape->fill.type == NSVG_PAINT_NONE && shape->stroke.type == NSVG_PAINT_NONE) continue; 154 | 155 | nvgBeginPath(vg); 156 | for (path = shape->paths; path != NULL; path = path->next) { 157 | nvgMoveTo(vg, path->pts[0], path->pts[1]); 158 | for (i = 1; i < path->npts; i += 3) { 159 | float* p = &path->pts[i*2]; 160 | nvgBezierTo(vg, p[0],p[1], p[2],p[3], p[4],p[5]); 161 | } 162 | if (path->closed) 163 | nvgLineTo(vg, path->pts[0], path->pts[1]); 164 | nvgPathWinding(vg, NVG_REVERSE); 165 | } 166 | 167 | if (shape->fill.type == NSVG_PAINT_COLOR) { 168 | if (color == NULL) 169 | nvgFillColor(vg, milli__nvgColUint(shape->fill.color)); 170 | nvgFill(vg); 171 | // printf("image %s\n", w->icon.icon->name); 172 | // nvgDebugDumpPathCache(vg); 173 | } 174 | if (shape->stroke.type == NSVG_PAINT_COLOR) { 175 | if (color == NULL) 176 | nvgStrokeColor(vg, milli__nvgColUint(shape->stroke.color)); 177 | nvgStrokeWidth(vg, shape->strokeWidth); 178 | nvgStroke(vg); 179 | } 180 | } 181 | 182 | nvgRestore(vg); 183 | } 184 | 185 | 186 | static struct MIvar* milli__findVar(struct MIcell* cell, const char* name) 187 | { 188 | struct MIvar* v; 189 | int nameLen; 190 | if (cell == NULL) return NULL; 191 | if (cell->vars == NULL) return NULL; 192 | if (name == NULL) return NULL; 193 | for (v = cell->vars; v != NULL; v = v->next) { 194 | printf("%p %s==%s\n", cell, v->name, name); 195 | if (strcmp(v->name, name) == 0) 196 | return v; 197 | } 198 | return NULL; 199 | } 200 | 201 | static void milli__addVar(struct MIcell* cell, const char* name, const char* key) 202 | { 203 | struct MIvar** prev = NULL; 204 | struct MIvar* var = NULL; 205 | int nameLen, keyLen; 206 | 207 | if (name == NULL || key == NULL) return; 208 | nameLen = strlen(name); 209 | keyLen = strlen(key); 210 | if (nameLen == 0 || keyLen == 0) return; 211 | 212 | // Check if the var exists 213 | if (milli__findVar(cell, name) != NULL) 214 | return; 215 | 216 | // Create new var 217 | var = (struct MIvar*)calloc(1, sizeof(struct MIvar)); 218 | if (var == NULL) goto error; 219 | // Store name 220 | var->name = strdup(name); 221 | if (var->name == NULL) goto error; 222 | // Store key 223 | var->key = strdup(key); 224 | if (var->key == NULL) goto error; 225 | // Add to linked list 226 | prev = &cell->vars; 227 | while (*prev != NULL) 228 | prev = &(*prev)->next; 229 | *prev = var; 230 | 231 | return; 232 | 233 | error: 234 | if (var != NULL) { 235 | free(var->name); 236 | free(var->key); 237 | free(var); 238 | } 239 | } 240 | 241 | static void milli__freeVars(struct MIvar* v) 242 | { 243 | while (v != NULL) { 244 | struct MIvar* next = v->next; 245 | free(v->name); 246 | free(v->key); 247 | free(v); 248 | v = next; 249 | } 250 | } 251 | 252 | 253 | static int milli_pointInRect(float x, float y, struct MIrect r) 254 | { 255 | return x >= r.x &&x <= r.x+r.width && y >= r.y && y <= r.y+r.height; 256 | } 257 | 258 | 259 | struct MIcontext { 260 | struct MIcell* active; 261 | struct MIcell* hover; 262 | struct MIcell* focus; 263 | 264 | float startmx, startmy; 265 | 266 | }; 267 | struct MIcontext g_context; 268 | 269 | int miInit() 270 | { 271 | memset(&g_context, 0, sizeof(g_context)); 272 | 273 | return 1; 274 | } 275 | 276 | void miTerminate() 277 | { 278 | deleteIcons(); 279 | } 280 | 281 | void miFrameBegin(struct NVGcontext* vg, int width, int height, struct MIinputState* input) 282 | { 283 | MILLI_NOTUSED(vg); 284 | MILLI_NOTUSED(width); 285 | MILLI_NOTUSED(height); 286 | MILLI_NOTUSED(input); 287 | } 288 | 289 | void miFrameEnd() 290 | { 291 | 292 | } 293 | 294 | static int milli__isspace(char c) 295 | { 296 | return strchr(" \t\n\v\f\r", c) != 0; 297 | } 298 | 299 | static int milli__isdigit(char c) 300 | { 301 | return strchr("0123456789", c) != 0; 302 | } 303 | 304 | static int milli__isnum(char c) 305 | { 306 | return strchr("0123456789+-.eE", c) != 0; 307 | } 308 | 309 | static struct MIparam* milli__allocParam(const char* key, int keyLen, const char* val, int valLen) 310 | { 311 | struct MIparam* p = (struct MIparam*)calloc(1, sizeof(struct MIparam)); 312 | if (p == NULL) goto error; 313 | 314 | p->key = (char*)malloc(keyLen+1); 315 | if (p->key == NULL) goto error; 316 | memcpy(p->key, key, keyLen); 317 | p->key[keyLen] = '\0'; 318 | 319 | p->val = (char*)malloc(valLen+1); 320 | if (p->val == NULL) goto error; 321 | memcpy(p->val, val, valLen); 322 | p->val[valLen] = '\0'; 323 | 324 | return p; 325 | 326 | error: 327 | if (p != NULL) { 328 | free(p->key); 329 | free(p->val); 330 | free(p); 331 | } 332 | return NULL; 333 | } 334 | 335 | struct MIparam* miParseParams(const char* s) 336 | { 337 | struct MIparam* ret = NULL; 338 | struct MIparam** cur = &ret; 339 | 340 | while (*s) { 341 | const char *key = NULL, *val = NULL; 342 | int keyLen = 0, valLen = 0; 343 | 344 | // Skip white space before the param name 345 | while (*s && milli__isspace(*s)) s++; 346 | if (!*s) break; 347 | key = s; 348 | // Find end of the attrib name. 349 | while (*s && !milli__isspace(*s) && *s != '=') s++; 350 | keyLen = (int)(s - key); 351 | 352 | // Skip until the beginning of the value. 353 | while (*s && (milli__isspace(*s) || *s == '=')) s++; 354 | if (*s == '\'' || *s == '\"') { 355 | // Parse quoted value 356 | char quote = *s; 357 | s++; // skip quote 358 | val = s; 359 | while (*s && *s != quote) s++; 360 | valLen = (int)(s - val); 361 | s++; // skip quote 362 | } else { 363 | // Parse unquoted value 364 | val = s; 365 | while (*s && !milli__isspace(*s)) s++; 366 | valLen = (int)(s - val); 367 | } 368 | 369 | if (key != NULL && keyLen > 0 && val != NULL && valLen > 0) { 370 | struct MIparam* p = milli__allocParam(key, keyLen, val, valLen); 371 | if (p != NULL) { 372 | *cur = p; 373 | cur = &p->next; 374 | } 375 | } 376 | } 377 | 378 | return ret; 379 | } 380 | 381 | void miFreeParams(struct MIparam* p) 382 | { 383 | if (p == NULL) return; 384 | miFreeParams(p->next); 385 | free(p->key); 386 | free(p->val); 387 | free(p); 388 | } 389 | 390 | int miCellParam(struct MIcell* cell, struct MIparam* p) 391 | { 392 | if (strcmp(p->key, "id") == 0) { 393 | if (p->val != NULL && strlen(p->val) > 0) { 394 | if (cell->id != NULL) free(cell->id); 395 | cell->id = strdup(p->val); 396 | return 1; 397 | } 398 | } else if (strcmp(p->key, "grow") == 0) { 399 | int grow = -1; 400 | sscanf(p->val, "%d", &grow); 401 | if (grow >= 0 && grow < 100) { 402 | cell->grow = grow; 403 | return 1; 404 | } 405 | } else if (strcmp(p->key, "paddingx") == 0) { 406 | int pad = -1; 407 | sscanf(p->val, "%d", &pad); 408 | if (pad >= 0 && pad < 100) { 409 | cell->paddingx = pad; 410 | return 1; 411 | } 412 | } else if (strcmp(p->key, "paddingy") == 0) { 413 | int pad = -1; 414 | sscanf(p->val, "%d", &pad); 415 | if (pad >= 0 && pad < 100) { 416 | cell->paddingy = pad; 417 | return 1; 418 | } 419 | } else if (strcmp(p->key, "padding") == 0) { 420 | int padx = -1, pady = -1; 421 | sscanf(p->val, "%d %d", &padx, &pady); 422 | if (padx >= 0 && padx < 100 && pady >= 0 && pady < 100) { 423 | cell->paddingx = padx; 424 | cell->paddingy = pady; 425 | return 1; 426 | } 427 | } else if (strcmp(p->key, "spacing") == 0) { 428 | int spacing = -1; 429 | sscanf(p->val, "%d", &spacing); 430 | if (spacing >= 0 && spacing < 100) { 431 | cell->spacing = spacing; 432 | return 1; 433 | } 434 | } 435 | return 0; 436 | } 437 | 438 | 439 | void milli__boxRender(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view) 440 | { 441 | struct MIbox* box = (struct MIbox*)cell; 442 | struct MIcell* child; 443 | struct MIrect rect = box->cell.frame; 444 | struct NVGcolor color = (g_context.hover == cell) ? nvgRGBA(255,255,255,64) : nvgRGBA(255,255,255,32); 445 | 446 | nvgBeginPath(vg); 447 | nvgRect(vg, rect.x, rect.y, rect.width, rect.height); 448 | nvgFillColor(vg, color); 449 | nvgFill(vg); 450 | 451 | // Render children 452 | for (child = box->cell.children; child != NULL; child = child->next) { 453 | if (child->render != NULL) 454 | child->render(child, vg, &rect); 455 | } 456 | } 457 | 458 | int milli__boxLayout(struct MIcell* cell, struct NVGcontext* vg) 459 | { 460 | struct MIbox* box = (struct MIbox*)cell; 461 | struct MIcell* child; 462 | float x, y, bw, bh; 463 | float sum = 0, avail = 0, packSpacing = 0; 464 | int ngrow = 0, nitems = 0; 465 | int reflow = 0; 466 | 467 | x = box->cell.frame.x + box->cell.paddingx; 468 | y = box->cell.frame.y + box->cell.paddingy; 469 | bw = maxf(0, box->cell.frame.width - box->cell.paddingx*2); 470 | bh = maxf(0, box->cell.frame.height - box->cell.paddingy*2); 471 | // x = box->x; // + root->style.paddingx; 472 | // y = root->y + root->style.paddingy; 473 | // rw = maxf(0, root->width - root->style.paddingx*2); 474 | // rh = maxf(0, root->height - root->style.paddingy*2); 475 | 476 | // Calculate desired sizes of the boxes. 477 | for (child = box->cell.children; child != NULL; child = child->next) { 478 | 479 | /* if (isStyleSet(&w->style, MG_PROPWIDTH_ARG)) { 480 | w->width = clampf(maxf(0, rw - w->style.paddingx*2) * w->style.width + w->style.paddingx*2, 0, rw); 481 | } else if (isStyleSet(&w->style, MG_WIDTH_ARG)) { 482 | w->width = w->style.width + w->style.paddingx*2; 483 | } else { 484 | w->width = w->cwidth + w->style.paddingx*2; 485 | } 486 | 487 | if (isStyleSet(&w->style, MG_PROPHEIGHT_ARG)) { 488 | w->height = clampf(maxf(0, rh - w->style.paddingy*2) * w->style.height + w->style.paddingy*2, 0, rh); 489 | } else if (isStyleSet(&w->style, MG_HEIGHT_ARG)) { 490 | w->height = w->style.height + w->style.paddingy*2; 491 | } else { 492 | w->height = w->cheight + w->style.paddingy*2; 493 | }*/ 494 | 495 | child->frame.width = child->content.width + child->paddingx*2; 496 | child->frame.height = child->content.height + child->paddingy*2; 497 | 498 | // Handle justify align already here. 499 | if (box->align == MI_JUSTIFY) { 500 | if (box->dir == MI_COL) 501 | child->frame.width = bw; 502 | else 503 | child->frame.height = bh; 504 | } 505 | 506 | child->frame.width = minf(child->frame.width, bw); 507 | child->frame.height = minf(child->frame.height, bh); 508 | } 509 | 510 | 511 | // Layout box model widgets 512 | if (box->dir == MI_COL) { 513 | 514 | for (child = box->cell.children; child != NULL; child = child->next) { 515 | sum += child->frame.height; 516 | if (child->next != NULL) sum += child->spacing; 517 | ngrow += child->grow; 518 | nitems++; 519 | } 520 | 521 | avail = bh - sum; 522 | if (box->overflow != MI_FIT) 523 | avail = maxf(0, avail); 524 | 525 | if (ngrow == 0 && avail > 0) { 526 | if (box->pack == MI_START) 527 | y += 0; 528 | else if (box->pack == MI_CENTER) 529 | y += avail/2; 530 | else if (box->pack == MI_END) 531 | y += avail; 532 | else if (box->pack == MI_JUSTIFY) { 533 | packSpacing = avail / nitems; 534 | y += packSpacing/2; 535 | } 536 | avail = 0; 537 | } 538 | 539 | for (child = box->cell.children; child != NULL; child = child->next) { 540 | child->frame.x = x; 541 | child->frame.y = y; 542 | if (ngrow > 0) 543 | child->frame.height += (float)child->grow/(float)ngrow * avail; 544 | else if (avail < 0) 545 | child->frame.height += 1.0f/(float)nitems * avail; 546 | 547 | switch (box->align) { 548 | case MI_END: 549 | child->frame.x += bw - child->frame.width; 550 | break; 551 | case MI_CENTER: 552 | child->frame.x += bw/2 - child->frame.width/2; 553 | break; 554 | default: // MI_START and MI_JUSTIFY 555 | break; 556 | } 557 | y += child->frame.height + child->spacing + packSpacing; 558 | 559 | if (child->layout) 560 | reflow |= child->layout(child, vg); 561 | } 562 | 563 | } else { 564 | 565 | for (child = box->cell.children; child != NULL; child = child->next) { 566 | sum += child->frame.width; 567 | if (child->next != NULL) sum += child->spacing; 568 | ngrow += child->grow; 569 | nitems++; 570 | } 571 | 572 | avail = bw - sum; 573 | if (box->overflow != MI_FIT) 574 | avail = maxf(0, avail); 575 | 576 | if (ngrow == 0 && avail > 0) { 577 | if (box->pack == MI_START) 578 | x += 0; 579 | else if (box->pack == MI_CENTER) 580 | x += avail/2; 581 | else if (box->pack == MI_END) 582 | x += avail; 583 | else if (box->pack == MI_JUSTIFY) { 584 | packSpacing = avail / nitems; 585 | x += packSpacing/2; 586 | } 587 | avail = 0; 588 | } 589 | 590 | for (child = box->cell.children; child != NULL; child = child->next) { 591 | child->frame.x = x; 592 | child->frame.y = y; 593 | if (ngrow > 0) 594 | child->frame.width += (float)child->grow/(float)ngrow * avail; 595 | else if (avail < 0) 596 | child->frame.width += 1.0f/(float)nitems * avail; 597 | 598 | switch (box->align) { 599 | case MI_END: 600 | child->frame.y += bh - child->frame.height; 601 | break; 602 | case MI_CENTER: 603 | child->frame.y += bh/2 - child->frame.height/2; 604 | break; 605 | default: // MI_START and MI_JUSTIFY 606 | break; 607 | } 608 | 609 | x += child->frame.width + child->spacing + packSpacing; 610 | 611 | if (child->layout) 612 | reflow |= child->layout(child, vg); 613 | } 614 | } 615 | 616 | return reflow; 617 | } 618 | 619 | static void milli__boxMeasure(struct MIcell* cell, struct NVGcontext* vg) 620 | { 621 | struct MIbox* box = (struct MIbox*)cell; 622 | struct MIsize* size = &box->cell.content; 623 | struct MIcell* child; 624 | 625 | size->width = 0; 626 | size->height = 0; 627 | 628 | // First measure children. 629 | for (child = box->cell.children; child != NULL; child = child->next) { 630 | if (child->measure != NULL) 631 | child->measure(child, vg); 632 | else 633 | child->content.width = child->content.height = 0; 634 | } 635 | 636 | // Adapt to child size based on direction. 637 | if (box->dir == MI_COL) { 638 | // Col 639 | for (child = box->cell.children; child != NULL; child = child->next) { 640 | size->width = maxf(size->width, child->content.width + child->paddingx*2); 641 | size->height += child->content.height + child->paddingy*2; 642 | if (child->next != NULL) size->height += child->spacing; 643 | } 644 | } else { 645 | // Row 646 | for (child = box->cell.children; child != NULL; child = child->next) { 647 | size->width += child->content.width + child->paddingx*2; 648 | size->height = maxf(size->height, child->content.height + child->paddingy*2); 649 | if (child->next != NULL) size->width += child->spacing; 650 | } 651 | } 652 | 653 | // Apply forced width 654 | if (box->width > 0) 655 | size->width = box->width; 656 | if (box->height > 0) 657 | size->height = box->height; 658 | } 659 | 660 | static int milli__boxLogic(struct MIcell* cell, struct MIevent* event) 661 | { 662 | struct MIbox* box = (struct MIbox*)cell; 663 | MILLI_NOTUSED(event); 664 | return 0; 665 | } 666 | 667 | static int milli__boxParseAlign(const char* str) 668 | { 669 | switch(str[0]) { 670 | case 's': return MI_START; 671 | case 'e': return MI_END; 672 | case 'c': return MI_CENTER; 673 | case 'j': return MI_JUSTIFY; 674 | } 675 | return -1; 676 | } 677 | 678 | static int milli__boxParseOverflow(const char* str) 679 | { 680 | switch(str[0]) { 681 | case 'f': return MI_FIT; 682 | case 'h': return MI_HIDDEN; 683 | case 's': return MI_SCROLL; 684 | case 'v': return MI_VISIBLE; 685 | } 686 | return -1; 687 | } 688 | 689 | static int milli__boxParseDir(const char* str) 690 | { 691 | switch(str[0]) { 692 | case 'r': return MI_ROW; 693 | case 'c': return MI_COL; 694 | } 695 | return -1; 696 | } 697 | 698 | static void milli__boxParam(struct MIcell* cell, struct MIparam* p) 699 | { 700 | struct MIbox* box = (struct MIbox*)cell; 701 | int valid = 0; 702 | 703 | if (miCellParam(cell, p)) return; 704 | 705 | if (strcmp(p->key, "dir") == 0) { 706 | int dir = milli__boxParseDir(p->val); 707 | if (dir != -1) { 708 | box->dir = dir; 709 | valid = 1; 710 | } 711 | } else if (strcmp(p->key, "align") == 0) { 712 | int align = milli__boxParseAlign(p->val); 713 | if (align != -1) { 714 | box->align = align; 715 | valid = 1; 716 | } 717 | } else if (strcmp(p->key, "pack") == 0) { 718 | int pack = milli__boxParseAlign(p->val); 719 | if (pack != -1) { 720 | box->pack = pack; 721 | valid = 1; 722 | } 723 | } else if (strcmp(p->key, "overflow") == 0) { 724 | int overflow = milli__boxParseOverflow(p->val); 725 | if (overflow != -1) { 726 | box->overflow = overflow; 727 | valid = 1; 728 | } 729 | } else if (strcmp(p->key, "width") == 0) { 730 | float width = -1; 731 | sscanf(p->val, "%f", &width); 732 | if (width > 0 && width < 10000) { 733 | box->width = width; 734 | valid = 1; 735 | } 736 | } else if (strcmp(p->key, "height") == 0) { 737 | float height = -1; 738 | sscanf(p->val, "%f", &height); 739 | if (height > 0 && height < 10000) { 740 | box->height = height; 741 | valid = 1; 742 | } 743 | } 744 | if (!valid) 745 | printf("Box: invalid parameter: %s=%s\n", p->key, p->val); 746 | } 747 | 748 | static void milli__boxDtor(struct MIcell* cell) 749 | { 750 | struct MIbox* box = (struct MIbox*)cell; 751 | } 752 | 753 | struct MIcell* miCreateBox(const char* params) 754 | { 755 | struct MIbox* box = (struct MIbox*)calloc(1, sizeof(struct MIbox)); 756 | if (box == NULL) goto error; 757 | box->cell.render = milli__boxRender; 758 | box->cell.layout = milli__boxLayout; 759 | box->cell.logic = milli__boxLogic; 760 | box->cell.measure = milli__boxMeasure; 761 | box->cell.param = milli__boxParam; 762 | box->cell.dtor = milli__boxDtor; 763 | box->align = MI_START; 764 | box->pack = MI_START; 765 | box->overflow = MI_HIDDEN; 766 | miSet((struct MIcell*)box, params); 767 | return (struct MIcell*)box; 768 | error: 769 | return NULL; 770 | } 771 | 772 | 773 | static void milli__textRender(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view) 774 | { 775 | struct MItext* text = (struct MItext*)cell; 776 | struct MIrect rect = text->cell.frame; 777 | struct NVGcolor color = (g_context.hover == cell) ? nvgRGBA(0,255,0,64) : nvgRGBA(0,255,0,32); 778 | 779 | MILLI_NOTUSED(view); 780 | 781 | if (text->text == NULL) return; 782 | 783 | nvgBeginPath(vg); 784 | nvgRect(vg, rect.x, rect.y, rect.width, rect.height); 785 | nvgFillColor(vg, color); 786 | nvgFill(vg); 787 | 788 | nvgFillColor(vg, nvgRGBA(255,255,255,255)); 789 | 790 | nvgFontFace(vg, text->fontFace); 791 | nvgFontSize(vg, text->fontSize); 792 | 793 | if (text->align == MI_CENTER) { 794 | nvgTextAlign(vg, NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 795 | nvgText(vg, cell->frame.x + cell->frame.width/2, cell->frame.y + cell->frame.height/2, text->text, NULL); 796 | } else if (text->align == MI_END) { 797 | nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); 798 | nvgText(vg, cell->frame.x + cell->frame.width - cell->paddingx, cell->frame.y + cell->frame.height/2, text->text, NULL); 799 | } else { 800 | nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE); 801 | nvgText(vg, cell->frame.x + cell->paddingx, cell->frame.y + cell->frame.height/2, text->text, NULL); 802 | } 803 | } 804 | 805 | int milli__textLayout(struct MIcell* cell, struct NVGcontext* vg) 806 | { 807 | MILLI_NOTUSED(cell); 808 | MILLI_NOTUSED(vg); 809 | /* struct MItext* text = (struct MItext*)cell; 810 | 811 | nvgFontFace(vg, text->fontFace); 812 | nvgFontSize(vg, text->fontSize); 813 | text->cell.content.width = text->text != NULL ? nvgTextBounds(vg, 0,0, text->text, NULL, NULL) : 0; 814 | nvgTextMetrics(vg, NULL, NULL, &text->cell.content.height); 815 | */ 816 | return 0; 817 | } 818 | 819 | static void milli__textMeasure(struct MIcell* cell, struct NVGcontext* vg) 820 | { 821 | struct MItext* text = (struct MItext*)cell; 822 | struct MIsize* size = &text->cell.content; 823 | nvgFontFace(vg, text->fontFace); 824 | nvgFontSize(vg, text->fontSize); 825 | size->width = text->text != NULL ? nvgTextBounds(vg, 0,0, text->text, NULL, NULL) : 10; 826 | nvgTextMetrics(vg, NULL, NULL, &size->height); 827 | } 828 | 829 | static int milli__textLogic(struct MIcell* cell, struct MIevent* event) 830 | { 831 | struct MItext* text = (struct MItext*)cell; 832 | MILLI_NOTUSED(event); 833 | return 0; 834 | } 835 | 836 | static void milli__textParam(struct MIcell* cell, struct MIparam* p) 837 | { 838 | struct MItext* text = (struct MItext*)cell; 839 | int valid = 0; 840 | 841 | if (miCellParam(cell, p)) return; 842 | 843 | if (strcmp(p->key, "label") == 0) { 844 | if (p->val != NULL && strlen(p->val) > 0) { 845 | if (text->text != NULL) free(text->text); 846 | text->text = strdup(p->val); 847 | valid = 1; 848 | } 849 | } else if (strcmp(p->key, "font-size") == 0) { 850 | float size = -1; 851 | sscanf(p->val, "%f", &size); 852 | if (size >= 0 && size < 100) { 853 | text->fontSize = size; 854 | valid = 1; 855 | } 856 | } else if (strcmp(p->key, "line-height") == 0) { 857 | float height = -1; 858 | sscanf(p->val, "%f", &height); 859 | if (height >= 0 && height < 5) { 860 | text->lineHeight = height; 861 | valid = 1; 862 | } 863 | } else if (strcmp(p->key, "font-face") == 0) { 864 | if (p->val != NULL && strlen(p->val) > 0) { 865 | if (text->fontFace != NULL) free(text->fontFace); 866 | text->fontFace = strdup(p->val); 867 | valid = 1; 868 | } 869 | } else if (strcmp(p->key, "align") == 0) { 870 | int align = milli__boxParseAlign(p->val); 871 | if (align != -1) { 872 | text->align = align; 873 | valid = 1; 874 | } 875 | } else if (strcmp(p->key, "pack") == 0) { 876 | int pack = milli__boxParseAlign(p->val); 877 | if (pack != -1) { 878 | text->pack = pack; 879 | valid = 1; 880 | } 881 | } 882 | 883 | if (!valid) 884 | printf("Text: invalid parameter: %s=%s\n", p->key, p->val); 885 | } 886 | 887 | static void milli__textDtor(struct MIcell* cell) 888 | { 889 | struct MItext* text = (struct MItext*)cell; 890 | free(text->text); 891 | free(text->fontFace); 892 | } 893 | 894 | struct MIcell* miCreateText(const char* params) 895 | { 896 | struct MItext* text = (struct MItext*)calloc(1, sizeof(struct MItext)); 897 | if (text == NULL) goto error; 898 | text->cell.render = milli__textRender; 899 | text->cell.layout = milli__textLayout; 900 | text->cell.logic = milli__textLogic; 901 | text->cell.measure = milli__textMeasure; 902 | text->cell.param = milli__textParam; 903 | text->cell.dtor = milli__textDtor; 904 | text->fontFace = strdup("sans"); 905 | text->fontSize = 18.0f; 906 | text->lineHeight = 1.2f; 907 | text->align = MI_START; 908 | text->pack = MI_CENTER; 909 | miSet((struct MIcell*)text, params); 910 | return (struct MIcell*)text; 911 | error: 912 | return NULL; 913 | } 914 | 915 | 916 | 917 | static void milli__iconRender(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view) 918 | { 919 | struct MIicon* icon = (struct MIicon*)cell; 920 | struct MIrect rect = icon->cell.frame; 921 | struct MIcolor col = {255,255,255,255}; 922 | struct NVGcolor color = (g_context.hover == cell) ? nvgRGBA(255,0,0,64) : nvgRGBA(255,0,0,32); 923 | 924 | nvgBeginPath(vg); 925 | nvgRect(vg, rect.x, rect.y, rect.width, rect.height); 926 | nvgFillColor(vg, color); 927 | nvgFill(vg); 928 | 929 | drawIcon(vg, &rect, icon->image->image, &col); 930 | } 931 | 932 | int milli__iconLayout(struct MIcell* cell, struct NVGcontext* vg) 933 | { 934 | MILLI_NOTUSED(cell); 935 | MILLI_NOTUSED(vg); 936 | /* struct MIicon* icon = (struct MIicon*)cell; 937 | if (icon->image != NULL) { 938 | icon->cell.content.width = icon->image->image->width; 939 | icon->cell.content.height = icon->image->image->height; 940 | } else { 941 | icon->cell.content.width = 1; 942 | icon->cell.content.height = 1; 943 | } 944 | if (icon->width > 0) icon->cell.content.width = icon->width; 945 | if (icon->height > 0) icon->cell.content.height = icon->height; 946 | */ 947 | return 0; 948 | } 949 | 950 | static void milli__iconMeasure(struct MIcell* cell, struct NVGcontext* vg) 951 | { 952 | struct MIicon* icon = (struct MIicon*)cell; 953 | struct MIsize* size = &icon->cell.content; 954 | if (icon->image != NULL) { 955 | size->width = icon->image->image->width; 956 | size->height = icon->image->image->height; 957 | } else { 958 | size->width = 1; 959 | size->height = 1; 960 | } 961 | if (icon->width > 0) size->width = icon->width; 962 | if (icon->height > 0) size->height = icon->height; 963 | } 964 | 965 | static int milli__iconLogic(struct MIcell* cell, struct MIevent* event) 966 | { 967 | struct MIicon* icon = (struct MIicon*)cell; 968 | MILLI_NOTUSED(event); 969 | return 0; 970 | } 971 | 972 | static void milli__iconParam(struct MIcell* cell, struct MIparam* p) 973 | { 974 | struct MIicon* icon = (struct MIicon*)cell; 975 | int valid = 0; 976 | 977 | if (miCellParam(cell, p)) return; 978 | 979 | if (strcmp(p->key, "icon") == 0) { 980 | struct MIiconImage* image = findIcon(p->val); 981 | if (image != NULL) { 982 | icon->image = image; 983 | valid = 1; 984 | } 985 | } else if (strcmp(p->key, "width") == 0) { 986 | float width = -1; 987 | sscanf(p->val, "%f", &width); 988 | if (width > 0 && width < 10000) { 989 | icon->width = width; 990 | valid = 1; 991 | } 992 | } else if (strcmp(p->key, "height") == 0) { 993 | float height = -1; 994 | sscanf(p->val, "%f", &height); 995 | if (height > 0 && height < 10000) { 996 | icon->width = height; 997 | valid = 1; 998 | } 999 | } 1000 | 1001 | if (!valid) 1002 | printf("Icon: invalid parameter: %s=%s\n", p->key, p->val); 1003 | 1004 | // static struct MIiconImage* findIcon(const char* name) 1005 | 1006 | } 1007 | 1008 | static void milli__iconDtor(struct MIcell* cell) 1009 | { 1010 | struct MIicon* icon = (struct MIicon*)cell; 1011 | } 1012 | 1013 | struct MIcell* miCreateIcon(const char* params) 1014 | { 1015 | struct MIicon* icon = (struct MIicon*)calloc(1, sizeof(struct MIicon)); 1016 | if (icon == NULL) goto error; 1017 | icon->cell.render = milli__iconRender; 1018 | icon->cell.layout = milli__iconLayout; 1019 | icon->cell.logic = milli__iconLogic; 1020 | icon->cell.measure = milli__iconMeasure; 1021 | icon->cell.param = milli__iconParam; 1022 | icon->cell.dtor = milli__iconDtor; 1023 | icon->width = -1; 1024 | icon->height = -1; 1025 | miSet((struct MIcell*)icon, params); 1026 | return (struct MIcell*)icon; 1027 | error: 1028 | return NULL; 1029 | } 1030 | 1031 | 1032 | 1033 | struct MIrect miInsetRect(struct MIrect r, float padx, float pady) 1034 | { 1035 | padx = minf(padx, r.width/2.0f); 1036 | pady = minf(pady, r.height/2.0f); 1037 | r.x += padx; 1038 | r.y += pady; 1039 | r.width -= padx*2; 1040 | r.height -= pady*2; 1041 | return r; 1042 | } 1043 | 1044 | static void milli__sliderRender(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view) 1045 | { 1046 | struct MIslider* slider = (struct MIslider*)cell; 1047 | struct MIrect rect = miInsetRect(slider->cell.frame, slider->cell.paddingx, slider->cell.paddingy); 1048 | float hr = rect.height/2; 1049 | float slotx = rect.x + hr; 1050 | float slotw = maxf(0, rect.width - hr*2); 1051 | float hx = slotx + clampf((slider->value - slider->vmin) / (slider->vmax - slider->vmin), 0.0f, 1.0f) * slotw; 1052 | struct NVGcolor color = (g_context.hover == cell) ? nvgRGBA(0,0,255,64) : nvgRGBA(0,0,255,32); 1053 | 1054 | nvgBeginPath(vg); 1055 | nvgRect(vg, rect.x, rect.y, rect.width, rect.height); 1056 | nvgFillColor(vg, color); 1057 | nvgFill(vg); 1058 | 1059 | nvgBeginPath(vg); 1060 | nvgRect(vg, rect.x, rect.y+rect.height/2-1, rect.width, 2); 1061 | nvgFillColor(vg, nvgRGBA(255,255,255,128)); 1062 | nvgFill(vg); 1063 | 1064 | nvgBeginPath(vg); 1065 | nvgCircle(vg, hx, rect.y+rect.height/2, hr); 1066 | nvgFillColor(vg, nvgRGBA(255,255,255,255)); 1067 | nvgFill(vg); 1068 | } 1069 | 1070 | static int milli__sliderLayout(struct MIcell* cell, struct NVGcontext* vg) 1071 | { 1072 | MILLI_NOTUSED(cell); 1073 | MILLI_NOTUSED(vg); 1074 | return 0; 1075 | } 1076 | 1077 | static void milli__sliderMeasure(struct MIcell* cell, struct NVGcontext* vg) 1078 | { 1079 | struct MIslider* slider = (struct MIslider*)cell; 1080 | struct MIsize* size = &slider->cell.content; 1081 | size->width = 100; 1082 | size->height = 20; 1083 | if (slider->width > 0) size->width = slider->width; 1084 | if (slider->height > 0) size->height = slider->height; 1085 | } 1086 | 1087 | static int milli__sliderLogic(struct MIcell* cell, struct MIevent* event) 1088 | { 1089 | struct MIslider* slider = (struct MIslider*)cell; 1090 | struct MIrect rect = miInsetRect(slider->cell.frame, slider->cell.paddingx, slider->cell.paddingy); 1091 | float hr = rect.height/2; 1092 | float slotx = rect.x + hr; 1093 | float slotw = maxf(0, rect.width - hr*2); 1094 | float hx = slotx + clampf((slider->value - slider->vmin) / (slider->vmax - slider->vmin), 0.0f, 1.0f) * slotw; 1095 | 1096 | if (event->type == MI_PRESSED) { 1097 | if (event->mx < (hx-hr) || event->mx > (hx+hr)) { 1098 | if (slotw > 0) { 1099 | float u = (event->mx - slotx) / slotw; 1100 | slider->value = slider->vmin + u * (slider->vmax - slider->vmin); 1101 | } 1102 | } 1103 | slider->vstart = slider->value; 1104 | } 1105 | if (event->type == MI_DRAGGED) { 1106 | float du = event->deltamx / slotw; 1107 | slider->value = clampf(slider->vstart + du * (slider->vmax - slider->vmin), slider->vmin, slider->vmax); 1108 | } 1109 | 1110 | return 0; 1111 | } 1112 | 1113 | static void milli__sliderParam(struct MIcell* cell, struct MIparam* p) 1114 | { 1115 | struct MIslider* slider = (struct MIslider*)cell; 1116 | int valid = 0; 1117 | 1118 | if (miCellParam(cell, p)) return; 1119 | 1120 | if (strcmp(p->key, "value") == 0) { 1121 | float v = 0; 1122 | if (sscanf(p->val, "%f", &v) == 1) { 1123 | slider->value = v; 1124 | valid = 1; 1125 | } 1126 | } else if (strcmp(p->key, "vmin") == 0) { 1127 | float v = 0; 1128 | if (sscanf(p->val, "%f", &v) == 1) { 1129 | slider->vmin = v; 1130 | valid = 1; 1131 | } 1132 | } else if (strcmp(p->key, "vmax") == 0) { 1133 | float v = 0; 1134 | if (sscanf(p->val, "%f", &v) == 1) { 1135 | slider->vmax = v; 1136 | valid = 1; 1137 | } 1138 | } else if (strcmp(p->key, "width") == 0) { 1139 | float width = -1; 1140 | sscanf(p->val, "%f", &width); 1141 | if (width > 0 && width < 10000) { 1142 | slider->width = width; 1143 | valid = 1; 1144 | } 1145 | } else if (strcmp(p->key, "height") == 0) { 1146 | float height = -1; 1147 | sscanf(p->val, "%f", &height); 1148 | if (height > 0 && height < 10000) { 1149 | slider->width = height; 1150 | valid = 1; 1151 | } 1152 | } 1153 | 1154 | if (!valid) 1155 | printf("Slider: invalid parameter: %s=%s\n", p->key, p->val); 1156 | } 1157 | 1158 | static void milli__sliderDtor(struct MIcell* cell) 1159 | { 1160 | struct MIslider* slider = (struct MIslider*)cell; 1161 | } 1162 | 1163 | struct MIcell* miCreateSlider(const char* params) 1164 | { 1165 | struct MIslider* slider = (struct MIslider*)calloc(1, sizeof(struct MIslider)); 1166 | if (slider == NULL) goto error; 1167 | slider->cell.render = milli__sliderRender; 1168 | slider->cell.layout = milli__sliderLayout; 1169 | slider->cell.logic = milli__sliderLogic; 1170 | slider->cell.measure = milli__sliderMeasure; 1171 | slider->cell.param = milli__sliderParam; 1172 | slider->cell.dtor = milli__sliderDtor; 1173 | slider->value = 0; 1174 | slider->vmin = 0; 1175 | slider->vmax = 1; 1176 | slider->width = -1; 1177 | slider->height = -1; 1178 | miSet((struct MIcell*)slider, params); 1179 | return (struct MIcell*)slider; 1180 | error: 1181 | return NULL; 1182 | } 1183 | 1184 | 1185 | 1186 | static void milli__templateRender(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view) 1187 | { 1188 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1189 | struct MIcell* host = tmpl->cell.children; 1190 | if (host->render) 1191 | host->render(host, vg, view); 1192 | } 1193 | 1194 | static int milli__templateLayout(struct MIcell* cell, struct NVGcontext* vg) 1195 | { 1196 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1197 | struct MIcell* host = tmpl->cell.children; 1198 | host->frame = tmpl->cell.frame; 1199 | if (host->layout) 1200 | return host->layout(host, vg); 1201 | return 0; 1202 | } 1203 | 1204 | static void milli__templateMeasure(struct MIcell* cell, struct NVGcontext* vg) 1205 | { 1206 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1207 | struct MIcell* host = tmpl->cell.children; 1208 | if (host->measure) { 1209 | host->measure(host, vg); 1210 | tmpl->cell.content = host->content; 1211 | } else { 1212 | tmpl->cell.content.width = tmpl->cell.content.height = 0; 1213 | } 1214 | } 1215 | 1216 | static int milli__templateLogic(struct MIcell* cell, struct MIevent* event) 1217 | { 1218 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1219 | struct MIcell* host = tmpl->cell.children; 1220 | if (host->logic) 1221 | return host->logic(host, event); 1222 | return 0; 1223 | } 1224 | 1225 | 1226 | static int milli__templateSetVar(struct MIcell* cell, char* name, char* val) 1227 | { 1228 | struct MIvar* var; 1229 | 1230 | if (cell == NULL) return 0; 1231 | 1232 | var = milli__findVar(cell, name); 1233 | if (var != NULL) { 1234 | struct MIparam p = {var->key, val, NULL}; 1235 | // printf("Template calling: %s=%s -> %s=%s\n", name,val, p.key, p.val); 1236 | if (cell->param != NULL) 1237 | cell->param(cell, &p); 1238 | return 1; 1239 | } 1240 | 1241 | if (milli__templateSetVar(cell->next, name, val)) return 1; 1242 | if (milli__templateSetVar(cell->children, name, val)) return 1; 1243 | return 0; 1244 | } 1245 | 1246 | static void milli__templateParam(struct MIcell* cell, struct MIparam* p) 1247 | { 1248 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1249 | struct MIcell* host = tmpl->cell.children; 1250 | 1251 | // printf("template %s=%s\n", p->key, p->val); 1252 | 1253 | // First try to user variables 1254 | if (milli__templateSetVar(host, p->key, p->val)) 1255 | return; 1256 | 1257 | // Finally pass to host 1258 | if (host->param) 1259 | host->param(host, p); 1260 | 1261 | tmpl->cell.grow = host->grow; 1262 | tmpl->cell.paddingx = host->paddingx; 1263 | tmpl->cell.paddingy = host->paddingy; 1264 | tmpl->cell.spacing = host->spacing; 1265 | 1266 | /* int valid = 0; 1267 | if (miCellParam(cell, p)) continue; 1268 | if (strcmp(p->key, "icon") == 0) { 1269 | struct MIiconImage* image = findIcon(p->val); 1270 | if (image != NULL) { 1271 | icon->image = image; 1272 | valid = 1; 1273 | } 1274 | } else if (strcmp(p->key, "width") == 0) { 1275 | float width = -1; 1276 | sscanf(p->val, "%f", &width); 1277 | if (width > 0 && width < 10000) { 1278 | icon->width = width; 1279 | valid = 1; 1280 | } 1281 | } else if (strcmp(p->key, "height") == 0) { 1282 | float height = -1; 1283 | sscanf(p->val, "%f", &height); 1284 | if (height > 0 && height < 10000) { 1285 | icon->width = height; 1286 | valid = 1; 1287 | } 1288 | } 1289 | if (!valid) 1290 | printf("Icon: invalid parameter: %s=%s\n", p->key, p->val);*/ 1291 | } 1292 | 1293 | static void milli__templateDtor(struct MIcell* cell) 1294 | { 1295 | struct MItemplate* tmpl = (struct MItemplate*)cell; 1296 | } 1297 | 1298 | struct MIcell* miCreateTemplate(struct MIcell* host) 1299 | { 1300 | struct MItemplate* tmpl = (struct MItemplate*)calloc(1, sizeof(struct MItemplate)); 1301 | if (tmpl == NULL) goto error; 1302 | tmpl->cell.render = milli__templateRender; 1303 | tmpl->cell.layout = milli__templateLayout; 1304 | tmpl->cell.logic = milli__templateLogic; 1305 | tmpl->cell.measure = milli__templateMeasure; 1306 | tmpl->cell.param = milli__templateParam; 1307 | tmpl->cell.dtor = milli__templateDtor; 1308 | 1309 | miAddChild(tmpl, host); 1310 | 1311 | tmpl->cell.grow = host->grow; 1312 | tmpl->cell.paddingx = host->paddingx; 1313 | tmpl->cell.paddingy = host->paddingy; 1314 | tmpl->cell.spacing = host->spacing; 1315 | 1316 | // miSet((struct MIcell*)tmpl, params); 1317 | return (struct MIcell*)tmpl; 1318 | error: 1319 | return NULL; 1320 | } 1321 | 1322 | 1323 | struct MIcell* miCreateButton(const char* params) 1324 | { 1325 | struct MIcell* tmpl = NULL; 1326 | struct MIcell* button = NULL; 1327 | 1328 | button = miCreateBox("id={id} dir=row align=justify padding='5 5' spacing=5 height=20"); 1329 | miAddChild(button, miCreateText("label={label}")); 1330 | 1331 | tmpl = miCreateTemplate(button); 1332 | miSet(tmpl, params); 1333 | 1334 | return tmpl; 1335 | } 1336 | 1337 | 1338 | 1339 | struct MIcell* miCreateIconButton(const char* params) 1340 | { 1341 | struct MIcell* tmpl = NULL; 1342 | struct MIcell* button = NULL; 1343 | 1344 | button = miCreateBox("id={id} dir=row align=justify padding='5 5' spacing=5 height=20"); 1345 | miAddChild(button, miCreateIcon("icon={icon} spacing=5")); 1346 | miAddChild(button, miCreateText("label={label}")); 1347 | 1348 | /* 1349 | miBegin(miBox("id={id} dir=row align=justify padding='5 5' spacing=5 height=20")); 1350 | miText("label={label}")); 1351 | miBegin(miPopup("")); 1352 | miCollection("id=options"); 1353 | miEnd(); 1354 | miEnd(); 1355 | 1356 | 1357 | miBegin(miSelect("id=blend-mode")); 1358 | miCollectionBegin("options"); 1359 | miMenuItem("label=Alpha value=0"); 1360 | miMenuItem("label=Screen value=1"); 1361 | miMenuItem("label=Multiply value=2"); 1362 | miMenuItem("label=Overlay value=3"); 1363 | miCollectionEnd(); 1364 | miEnd(); 1365 | 1366 | 1367 | miSetInt("blend-mode", blendMode); 1368 | if (miChanged("blend-mode")) { 1369 | miGetInt("blend-mode", &blendMode); 1370 | } 1371 | 1372 | 1373 | 1374 | for (i = 0; i < materialCount; i++) { 1375 | miBegin(materialItem); 1376 | miSetStr("name", materisl[i].name); 1377 | miSetFloat4("color", materisl[i].color); 1378 | if (miChanged("name") || miChanged("color")) { 1379 | miGetStr("name", materisl[i].name, sizeof(materisl[i].name)); 1380 | miGetFloat4("color", materisl[i].color); 1381 | saveUndo(); 1382 | } 1383 | miEnd(); 1384 | } 1385 | miCollectionEnd(); 1386 | 1387 | 1388 | */ 1389 | 1390 | tmpl = miCreateTemplate(button); 1391 | miSet(tmpl, params); 1392 | 1393 | return tmpl; 1394 | } 1395 | 1396 | 1397 | 1398 | void miAddChild(struct MIcell* parent, struct MIcell* child) 1399 | { 1400 | struct MIcell** prev = NULL; 1401 | if (parent == NULL) return; 1402 | if (child == NULL) return; 1403 | prev = &parent->children; 1404 | while (*prev != NULL) 1405 | prev = &(*prev)->next; 1406 | *prev = child; 1407 | child->parent = parent; 1408 | } 1409 | 1410 | 1411 | void miFreeCell(struct MIcell* cell) 1412 | { 1413 | if (cell == NULL) return; 1414 | miFreeCell(cell->children); 1415 | miFreeCell(cell->next); 1416 | if (cell->dtor) cell->dtor(cell); 1417 | free(cell->id); 1418 | milli__freeVars(cell->vars); 1419 | free(cell); 1420 | } 1421 | 1422 | void miSet(struct MIcell* cell, const char* params) 1423 | { 1424 | struct MIparam* p = miParseParams(params); 1425 | struct MIparam* it = p; 1426 | for (it = p; it != NULL; it = it->next) { 1427 | if (it->val[0] == '{') { 1428 | char* name = strdup(it->val+1); 1429 | name[strlen(name)-1] = '\0'; 1430 | milli__addVar(cell, name, it->key); 1431 | free(name); 1432 | } else { 1433 | cell->param(cell, it); 1434 | } 1435 | } 1436 | miFreeParams(p); 1437 | } 1438 | 1439 | void miLayout(struct MIcell* cell, struct NVGcontext* vg) 1440 | { 1441 | if (cell == NULL) return; 1442 | 1443 | if (cell->measure != NULL) 1444 | cell->measure(cell, vg); 1445 | 1446 | cell->frame.x = 0; 1447 | cell->frame.y = 0; 1448 | cell->frame.width = cell->content.width + cell->paddingx*2; 1449 | cell->frame.height = cell->content.height + cell->paddingy*2; 1450 | if (cell->layout != NULL) 1451 | cell->layout(cell, vg); 1452 | } 1453 | 1454 | 1455 | /*static struct MIcell* milli__clearState(struct MIcell* cell, struct MIinputState* input) 1456 | { 1457 | if (cell == NULL) return NULL; 1458 | if (milli_pointInRect(input->mx, input->my, cell->frame)) { 1459 | struct MIcell* childHit = milli__hitTest(cell->children, input); 1460 | return childHit != NULL ? childHit : cell; 1461 | } 1462 | return milli__hitTest(cell->next, input); 1463 | }*/ 1464 | 1465 | static void milli__setHover(struct MIcell* cell, struct MIcell* hover) 1466 | { 1467 | if (cell == NULL) return; 1468 | milli__setHover(cell->next, hover); 1469 | milli__setHover(cell->children, hover); 1470 | cell->hover = (cell == hover) ? 1 : 0; 1471 | } 1472 | 1473 | static void milli__setActive(struct MIcell* cell, struct MIcell* active) 1474 | { 1475 | if (cell == NULL) return; 1476 | milli__setActive(cell->next, active); 1477 | milli__setActive(cell->children, active); 1478 | cell->active = (cell == active) ? 1 : 0; 1479 | } 1480 | 1481 | static struct MIcell* milli__hitTest(struct MIcell* cell, struct MIinputState* input) 1482 | { 1483 | if (cell == NULL) return NULL; 1484 | if (milli_pointInRect(input->mx, input->my, cell->frame)) { 1485 | struct MIcell* hit = milli__hitTest(cell->children, input); 1486 | if (hit != NULL) return hit; 1487 | return cell; 1488 | } 1489 | return milli__hitTest(cell->next, input); 1490 | } 1491 | 1492 | static void fireLogic(struct MIcell* cell, int type, struct MIevent* event) 1493 | { 1494 | if (cell == NULL) return; 1495 | event->type = type; 1496 | if (cell->logic != NULL) 1497 | cell->logic(cell, event); 1498 | } 1499 | 1500 | void miInput(struct MIcell* cell, struct MIinputState* input) 1501 | { 1502 | struct MIcell* hit = NULL; 1503 | struct MIcell* entered = NULL; 1504 | struct MIcell* exited = NULL; 1505 | struct MIcell* focused = NULL; 1506 | struct MIcell* blurred = NULL; 1507 | struct MIcell* clicked = NULL; 1508 | struct MIcell* pressed = NULL; 1509 | struct MIcell* dragged = NULL; 1510 | struct MIcell* released = NULL; 1511 | struct MIevent event; 1512 | int i; 1513 | 1514 | if (cell == NULL) return; 1515 | 1516 | hit = milli__hitTest(cell, input); 1517 | 1518 | if (g_context.active == NULL) { 1519 | if (g_context.hover != hit) { 1520 | exited = g_context.hover; 1521 | entered = hit; 1522 | g_context.hover = hit; 1523 | } 1524 | if (input->mbut & MI_MOUSE_PRESSED) { 1525 | if (g_context.focus != hit) { 1526 | blurred = g_context.focus; 1527 | focused = hit; 1528 | } 1529 | g_context.focus = hit; 1530 | g_context.active = hit; 1531 | pressed = hit; 1532 | } 1533 | } 1534 | // Press and release can happen in same frame. 1535 | if (g_context.active != NULL) { 1536 | if (hit == NULL || hit == g_context.active) { 1537 | if (g_context.hover != hit) { 1538 | exited = g_context.hover; 1539 | entered = hit; 1540 | g_context.hover = hit; 1541 | } 1542 | // context.hover = hit->id; 1543 | } 1544 | if (input->mbut & MI_MOUSE_RELEASED) { 1545 | if (g_context.hover == g_context.active) 1546 | clicked = g_context.hover; 1547 | released = g_context.active; 1548 | g_context.active = NULL; 1549 | } else { 1550 | // if (g_context.moved) 1551 | dragged = g_context.active; 1552 | } 1553 | } 1554 | 1555 | // Update mouse positions. 1556 | if (pressed != NULL) { 1557 | g_context.startmx = input->mx; 1558 | g_context.startmy = input->my; 1559 | } 1560 | 1561 | event.mx = input->mx; 1562 | event.my = input->my; 1563 | event.mbut = input->mbut; 1564 | event.key = 0; 1565 | 1566 | if (g_context.active != NULL) { 1567 | event.deltamx = input->mx - g_context.startmx; 1568 | event.deltamy = input->my - g_context.startmy; 1569 | } 1570 | 1571 | // setHit(context.hover, &context.hoverHit); 1572 | // setHit(context.active, &context.activeHit); 1573 | 1574 | fireLogic(blurred, MI_BLURRED, &event); 1575 | fireLogic(focused, MI_FOCUSED, &event); 1576 | fireLogic(pressed, MI_PRESSED, &event); 1577 | fireLogic(dragged, MI_DRAGGED, &event); 1578 | fireLogic(released, MI_RELEASED, &event); 1579 | fireLogic(clicked, MI_CLICKED, &event); 1580 | fireLogic(exited, MI_EXITED, &event); 1581 | fireLogic(entered, MI_ENTERED, &event); 1582 | 1583 | if (g_context.focus != 0) { 1584 | for (i = 0; i < input->nkeys; i++) { 1585 | event.key = input->keys[i].code; 1586 | fireLogic(g_context.focus, input->keys[i].type, &event); 1587 | } 1588 | } 1589 | 1590 | 1591 | // if (cell->logic != NULL) 1592 | // cell->logic(cell, vg); 1593 | 1594 | // Mark input consumed 1595 | input->nkeys = 0; 1596 | input->mbut = 0; 1597 | } 1598 | 1599 | void miRender(struct MIcell* cell, struct NVGcontext* vg) 1600 | { 1601 | if (cell == NULL) return; 1602 | if (cell->render != NULL) 1603 | cell->render(cell, vg, NULL); 1604 | } 1605 | -------------------------------------------------------------------------------- /src/milli.h: -------------------------------------------------------------------------------- 1 | #ifndef MILLI_H 2 | #define MILLI_H 3 | 4 | struct NVGcontext; 5 | 6 | int miInit(); 7 | void miTerminate(); 8 | 9 | enum MImouseButton { 10 | MI_MOUSE_PRESSED = 1 << 0, 11 | MI_MOUSE_RELEASED = 1 << 1, 12 | }; 13 | 14 | enum MUIoverflow { 15 | MI_FIT, 16 | MI_HIDDEN, 17 | MI_SCROLL, 18 | MI_VISIBLE, 19 | }; 20 | 21 | enum MUIdir { 22 | MI_ROW, 23 | MI_COL, 24 | }; 25 | 26 | enum MUIalign { 27 | MI_START, 28 | MI_END, 29 | MI_CENTER, 30 | MI_JUSTIFY, 31 | }; 32 | 33 | //#define MI_AUTO_SIZE 0xffff 34 | 35 | struct MIrect { 36 | float x, y, width, height; 37 | }; 38 | 39 | struct MIpoint { 40 | float x, y; 41 | }; 42 | 43 | struct MIsize { 44 | float width, height; 45 | }; 46 | 47 | struct MIcolor { 48 | unsigned char r,g,b,a; 49 | }; 50 | 51 | enum MIwidgetEvent { 52 | MI_FOCUSED, 53 | MI_BLURRED, 54 | MI_CLICKED, 55 | MI_PRESSED, 56 | MI_RELEASED, 57 | MI_DRAGGED, 58 | MI_ENTERED, 59 | MI_EXITED, 60 | MI_KEYPRESSED, 61 | MI_KEYRELEASED, 62 | MI_CHARTYPED, 63 | }; 64 | 65 | 66 | #define MI_MAX_INPUTKEYS 32 67 | struct MIkeyPress { 68 | int type, code; 69 | }; 70 | 71 | struct MIevent { 72 | int type; 73 | float mx, my; 74 | float deltamx, deltamy; 75 | int mbut; 76 | int key; 77 | }; 78 | 79 | struct MIinputState 80 | { 81 | float mx, my; 82 | int mbut; 83 | struct MIkeyPress keys[MI_MAX_INPUTKEYS]; 84 | int nkeys; 85 | }; 86 | 87 | void miFrameBegin(struct NVGcontext* vg, int width, int height, struct MIinputState* input); 88 | void miFrameEnd(); 89 | 90 | struct MIparam { 91 | char* key; 92 | char* val; 93 | struct MIparam* next; 94 | }; 95 | 96 | struct MIcell; 97 | 98 | typedef void (*MIrenderFun)(struct MIcell* cell, struct NVGcontext* vg, struct MIrect* view); 99 | typedef int (*MIlayoutFun)(struct MIcell* cell, struct NVGcontext* vg); 100 | typedef int (*MIlogicFun)(struct MIcell* cell, struct MIevent* event); 101 | typedef void (*MImeasureFun)(struct MIcell* cell, struct NVGcontext* vg); 102 | typedef void (*MIparamFun)(struct MIcell* cell, struct MIparam* param); 103 | typedef void (*MIdtorFun)(struct MIcell* cell); 104 | 105 | struct MIvar { 106 | char* name; 107 | char* key; 108 | struct MIvar* next; 109 | }; 110 | 111 | struct MIcell { 112 | char* id; 113 | 114 | struct MIrect frame; 115 | struct MIsize content; 116 | unsigned char grow; 117 | unsigned char paddingx, paddingy; 118 | unsigned char spacing; 119 | 120 | unsigned char hover; 121 | unsigned char active; 122 | 123 | MIrenderFun render; 124 | MIlayoutFun layout; 125 | MIlogicFun logic; 126 | MIparamFun param; 127 | MImeasureFun measure; 128 | MIdtorFun dtor; 129 | 130 | struct MIvar* vars; 131 | 132 | struct MIcell* parent; 133 | struct MIcell* children; 134 | struct MIcell* next; 135 | }; 136 | 137 | struct MIbox { 138 | struct MIcell cell; 139 | unsigned char dir; 140 | unsigned char align; 141 | unsigned char pack; 142 | unsigned char overflow; 143 | float width, height; 144 | }; 145 | 146 | struct MItext { 147 | struct MIcell cell; 148 | char* text; 149 | // int maxText; 150 | char* fontFace; 151 | float fontSize; 152 | float lineHeight; 153 | unsigned char align; 154 | unsigned char pack; 155 | }; 156 | 157 | struct MIiconImage; 158 | 159 | struct MIicon { 160 | struct MIcell cell; 161 | struct MIiconImage* image; 162 | float width, height; 163 | }; 164 | 165 | struct MItemplate { 166 | struct MIcell cell; 167 | }; 168 | 169 | struct MIslider { 170 | struct MIcell cell; 171 | float width, height; 172 | float value; 173 | float vmin, vmax; 174 | float vstart; 175 | }; 176 | 177 | 178 | struct MIparam* miParseParams(const char* params); 179 | void miFreeParams(struct MIparam* p); 180 | int miCellParam(struct MIcell* cell, struct MIparam* p); 181 | 182 | struct MIcell* miCreateBox(const char* params); 183 | struct MIcell* miCreateText(const char* params); 184 | struct MIcell* miCreateIcon(const char* params); 185 | struct MIcell* miCreateSlider(const char* params); 186 | 187 | struct MIcell* miCreateButton(const char* params); 188 | struct MIcell* miCreateIconButton(const char* params); 189 | 190 | struct MIcell* miCreatetemplate(struct MIcell* host); 191 | 192 | void miAddChild(struct MIcell* parent, struct MIcell* child); 193 | 194 | void miFreeCell(struct MIcell* cell); 195 | 196 | void miSet(struct MIcell* cell, const char* params); 197 | 198 | #define MILLI_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) 199 | 200 | void miLayout(struct MIcell* cell, struct NVGcontext* vg); 201 | void miInput(struct MIcell* cell, struct MIinputState* input); 202 | void miRender(struct MIcell* cell, struct NVGcontext* vg); 203 | 204 | int miCreateIconImage(const char* name, const char* filename, float scale); 205 | 206 | //int mgCreateIcon(const char* name, const char* filename); 207 | 208 | #endif // MILLI_H 209 | -------------------------------------------------------------------------------- /src/milli2.h: -------------------------------------------------------------------------------- 1 | #ifndef MILLI_H 2 | #define MILLI_H 3 | 4 | struct NVGcontext; 5 | 6 | enum MImouseButton { 7 | MI_MOUSE_PRESSED = 1 << 0, 8 | MI_MOUSE_RELEASED = 1 << 1, 9 | }; 10 | 11 | enum MIoverflow { 12 | MI_FIT, 13 | MI_HIDDEN, 14 | MI_SCROLL, 15 | MI_VISIBLE, 16 | }; 17 | 18 | enum MIdir { 19 | MI_ROW, 20 | MI_COL, 21 | }; 22 | 23 | enum MIalign { 24 | MI_START, 25 | MI_END, 26 | MI_CENTER, 27 | MI_JUSTIFY, 28 | }; 29 | 30 | //#define MI_AUTO_SIZE 0xffff 31 | 32 | struct MIrect { 33 | float x, y, width, height; 34 | }; 35 | typedef struct MIrect MIrect; 36 | 37 | struct MIpoint { 38 | float x, y; 39 | }; 40 | typedef struct MIpoint MIpoint; 41 | 42 | struct MIsize { 43 | float width, height; 44 | }; 45 | typedef struct MIsize MIsize; 46 | 47 | struct MIcolor { 48 | unsigned char r,g,b,a; 49 | }; 50 | typedef struct MIcolor MIcolor; 51 | 52 | enum MIwidgetEvent { 53 | MI_NONE, 54 | MI_FOCUSED, 55 | MI_BLURRED, 56 | MI_CLICKED, 57 | MI_PRESSED, 58 | MI_RELEASED, 59 | MI_DRAGGED, 60 | MI_ENTERED, 61 | MI_EXITED, 62 | MI_KEYPRESSED, 63 | MI_KEYRELEASED, 64 | MI_CHARTYPED, 65 | }; 66 | 67 | #define MI_MAX_INPUTKEYS 32 68 | struct MIkeyPress { 69 | int type, code, mods; 70 | }; 71 | typedef struct MIkeyPress MIkeyPress; 72 | 73 | struct MIevent { 74 | int type; 75 | float mx, my; 76 | float deltamx, deltamy; 77 | int mbut; 78 | int key; 79 | }; 80 | typedef struct MIevent MIevent; 81 | 82 | struct MIinputState 83 | { 84 | float mx, my; 85 | int mbut; 86 | MIkeyPress keys[MI_MAX_INPUTKEYS]; 87 | int nkeys; 88 | }; 89 | typedef struct MIinputState MIinputState; 90 | 91 | #define MI_FIT (-1.0f) 92 | 93 | struct MIpopupState { 94 | int visible; 95 | int visited; 96 | int logic; 97 | MIrect rect; 98 | }; 99 | typedef struct MIpopupState MIpopupState; 100 | 101 | struct MIcanvasState { 102 | MIrect rect; 103 | }; 104 | typedef struct MIcanvasState MIcanvasState; 105 | 106 | typedef unsigned int MIhandle; 107 | 108 | enum MIfontFace { 109 | MI_FONT_NORMAL, 110 | MI_FONT_ITALIC, 111 | MI_FONT_BOLD, 112 | MI_COUNT_FONTS 113 | }; 114 | 115 | int miInit(struct NVGcontext* vg); 116 | void miTerminate(); 117 | 118 | int miCreateFont(int face, const char* filename); 119 | int miCreateIconImage(const char* name, const char* filename, float scale); 120 | 121 | 122 | void miFrameBegin(int width, int height, MIinputState* input, float dt); 123 | void miFrameEnd(); 124 | 125 | MIhandle miPanelBegin(float x, float y, float width, float height); 126 | MIhandle miPanelEnd(); 127 | 128 | int miIsHover(MIhandle handle); 129 | int miIsActive(MIhandle handle); 130 | int miIsFocus(MIhandle handle); 131 | 132 | int miFocused(MIhandle handle); 133 | int miBlurred(MIhandle handle); 134 | 135 | int miPressed(MIhandle handle); 136 | int miReleased(MIhandle handle); 137 | int miClicked(MIhandle handle); 138 | int miDragged(MIhandle handle); 139 | int miChanged(MIhandle handle); 140 | 141 | void miBlur(MIhandle handle); 142 | void miChange(MIhandle handle); 143 | 144 | MIpoint miMousePos(); 145 | int miMouseClickCount(); 146 | 147 | MIhandle miButton(const char* label); 148 | MIhandle miText(const char* text); 149 | MIhandle miSlider(float* value, float vmin, float vmax); 150 | MIhandle miInput(char* text, int maxText); 151 | 152 | MIhandle miSliderValue(float* value, float vmin, float vmax); 153 | 154 | enum MIpack { 155 | MI_TOP_BOTTOM, 156 | MI_BOTTOM_TOP, 157 | MI_LEFT_RIGHT, 158 | MI_RIGHT_LEFT, 159 | MI_FILLX, 160 | MI_FILLY, 161 | }; 162 | 163 | void miPack(int pack); 164 | void miColWidth(float width); 165 | void miRowHeight(float height); 166 | 167 | MIhandle miDockBegin(int pack); 168 | MIhandle miDockEnd(); 169 | 170 | MIhandle miLayoutBegin(int pack); 171 | MIhandle miLayoutEnd(); 172 | 173 | MIhandle miDivsBegin(int pack, int count, float* divs); 174 | MIhandle miDivsEnd(); 175 | 176 | MIhandle miSpacer(); 177 | 178 | enum MIpopupSide { 179 | MI_RIGHT, 180 | MI_BELOW, 181 | }; 182 | 183 | enum MIpopupLogic { 184 | MI_ONCLICK, 185 | MI_ONHOVER, 186 | }; 187 | 188 | MIhandle miPopupBegin(MIhandle base, int logic, int side); 189 | MIhandle miPopupEnd(); 190 | 191 | void miPopupShow(MIhandle handle); 192 | void miPopupHide(MIhandle handle); 193 | void miPopupToggle(MIhandle handle); 194 | 195 | MIhandle miCanvasBegin(MIcanvasState* state, float width, float height); 196 | MIhandle miCanvasEnd(); 197 | 198 | MIsize miMeasureText(const char* text, int face, float size); 199 | 200 | #define MI_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) 201 | 202 | #endif // MILLI_H 203 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | ##TODO 2 | 3 | - store scrollbar state internally 4 | - always scroll with scroll? (or 3ds ax style drag too?) 5 | 6 | - add styles 7 | - states: normal, hover, active, focus 8 | - color: bg, text, outline 9 | - outline width 10 | - font: face, weight, size 11 | - padding: x, y 12 | - spacing 13 | - rounded corners: tl, tr, br, bl 14 | 15 | - simplify MUIwidget (remove widget types) 16 | 17 | - make button a div + text 18 | - make checkbox label + div 19 | - make slider div + div 20 | - make select div+text + popup 21 | 22 | - add popup 23 | - always connected to previous widget 24 | - show on hover or click 25 | - hide on click outside or move outside 26 | 27 | - logic 28 | - hit testing 29 | - focus state 30 | - events 31 | - fall through tag 32 | 33 | - essential components 34 | - panel 35 | - div 36 | - text 37 | - icon/image 38 | - slider 39 | - text input --------------------------------------------------------------------------------