├── .gitignore ├── .hgignore ├── DejaVuSans.ttf ├── LICENSE ├── LICENSE-DejaVuSans.ttf.txt ├── LICENSE-blender_icons.svg.txt ├── README.md ├── blender_icons.svg ├── blender_icons16.png ├── blendish.h ├── example.c ├── images ├── blendish2.png ├── oui_frozen.png └── oui_logo.png ├── oui.h └── premake4.lua /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | .sconsign.dblite 4 | .settings 5 | *.orig 6 | .project 7 | .pydevproject 8 | 9 | syntax: regexp 10 | 11 | ^build 12 | ^nanovg 13 | -------------------------------------------------------------------------------- /DejaVuSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IceDragon200/oui-blendish/f8334a70393c961f23aaffff54d716e7345efa03/DejaVuSans.ttf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Blendish - Blender 2.5 UI based theming functions for NanoVG 2 | 3 | Copyright (c) 2014 Leonard Ritter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-DejaVuSans.ttf.txt: -------------------------------------------------------------------------------- 1 | Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. 2 | Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) 3 | 4 | Bitstream Vera Fonts Copyright 5 | ------------------------------ 6 | 7 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is 8 | a trademark of Bitstream, Inc. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of the fonts accompanying this license ("Fonts") and associated 12 | documentation files (the "Font Software"), to reproduce and distribute the 13 | Font Software, including without limitation the rights to use, copy, merge, 14 | publish, distribute, and/or sell copies of the Font Software, and to permit 15 | persons to whom the Font Software is furnished to do so, subject to the 16 | following conditions: 17 | 18 | The above copyright and trademark notices and this permission notice shall 19 | be included in all copies of one or more of the Font Software typefaces. 20 | 21 | The Font Software may be modified, altered, or added to, and in particular 22 | the designs of glyphs or characters in the Fonts may be modified and 23 | additional glyphs or characters may be added to the Fonts, only if the fonts 24 | are renamed to names not containing either the words "Bitstream" or the word 25 | "Vera". 26 | 27 | This License becomes null and void to the extent applicable to Fonts or Font 28 | Software that has been modified and is distributed under the "Bitstream 29 | Vera" names. 30 | 31 | The Font Software may be sold as part of a larger software package but no 32 | copy of one or more of the Font Software typefaces may be sold by itself. 33 | 34 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, 37 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME 38 | FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING 39 | ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 40 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 41 | THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE 42 | FONT SOFTWARE. 43 | 44 | Except as contained in this notice, the names of Gnome, the Gnome 45 | Foundation, and Bitstream Inc., shall not be used in advertising or 46 | otherwise to promote the sale, use or other dealings in this Font Software 47 | without prior written authorization from the Gnome Foundation or Bitstream 48 | Inc., respectively. For further information, contact: fonts at gnome dot 49 | org. 50 | 51 | Arev Fonts Copyright 52 | ------------------------------ 53 | 54 | Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining 57 | a copy of the fonts accompanying this license ("Fonts") and 58 | associated documentation files (the "Font Software"), to reproduce 59 | and distribute the modifications to the Bitstream Vera Font Software, 60 | including without limitation the rights to use, copy, merge, publish, 61 | distribute, and/or sell copies of the Font Software, and to permit 62 | persons to whom the Font Software is furnished to do so, subject to 63 | the following conditions: 64 | 65 | The above copyright and trademark notices and this permission notice 66 | shall be included in all copies of one or more of the Font Software 67 | typefaces. 68 | 69 | The Font Software may be modified, altered, or added to, and in 70 | particular the designs of glyphs or characters in the Fonts may be 71 | modified and additional glyphs or characters may be added to the 72 | Fonts, only if the fonts are renamed to names not containing either 73 | the words "Tavmjong Bah" or the word "Arev". 74 | 75 | This License becomes null and void to the extent applicable to Fonts 76 | or Font Software that has been modified and is distributed under the 77 | "Tavmjong Bah Arev" names. 78 | 79 | The Font Software may be sold as part of a larger software package but 80 | no copy of one or more of the Font Software typefaces may be sold by 81 | itself. 82 | 83 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 84 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 85 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 86 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 87 | TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 88 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 89 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 90 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 91 | OTHER DEALINGS IN THE FONT SOFTWARE. 92 | 93 | Except as contained in this notice, the name of Tavmjong Bah shall not 94 | be used in advertising or otherwise to promote the sale, use or other 95 | dealings in this Font Software without prior written authorization 96 | from Tavmjong Bah. For further information, contact: tavmjong @ free 97 | . fr. 98 | 99 | $Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ 100 | -------------------------------------------------------------------------------- /LICENSE-blender_icons.svg.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Blendish** is a small collection of drawing functions for [NanoVG](https://github.com/memononen/nanovg) in a single C header file, designed to replicate the look of the Blender 2.5+ User Interface. You can use these functions to theme your UI library. Some metric constants for faithful reproduction are also included. 2 | 3 | To render correctly, Blendish needs both [icon sheet](https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/datafiles/blender_icons16.png) and [font](https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/datafiles/fonts/) from the 4 | Blender repository. See source code for more information. 5 | 6 | ![oui_logo.png](images/oui_logo.png) 7 | 8 | The repository also hosts **OUI** (short for "Open UI", spoken like the french "oui" for "yes"), a platform agnostic single-header C library for layouting GUI elements and 9 | handling related user input. Together with a set of widget drawing and logic routines it can be used to build complex user interfaces. 10 | 11 | Here's a screenshot of Blendish styling a set of layouted OUI items (also contained in example.cpp). 12 | 13 | ![oui_frozen.png](images/oui_frozen.png) 14 | 15 | Here's a shot of all available Blendish theming functions: 16 | 17 | ![blendish2.png](images/blendish2.png) 18 | -------------------------------------------------------------------------------- /blender_icons16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IceDragon200/oui-blendish/f8334a70393c961f23aaffff54d716e7345efa03/blender_icons16.png -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | // 2 | // based on NanoVG's example code by Mikko Mononen 3 | 4 | #include 5 | #include 6 | #ifdef NANOVG_GLEW 7 | # include 8 | #endif 9 | #ifdef __APPLE__ 10 | # define GLFW_INCLUDE_GLCOREARB 11 | #endif 12 | #include 13 | #include "nanovg.h" 14 | #define NANOVG_GL3_IMPLEMENTATION 15 | #include "nanovg_gl.h" 16 | 17 | #define BLENDISH_IMPLEMENTATION 18 | #include "blendish.h" 19 | 20 | #define OUI_IMPLEMENTATION 21 | #include "oui.h" 22 | 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | typedef struct exContext { 26 | NVGcontext *vg; 27 | UIcontext *uictx; 28 | } exContext; 29 | 30 | typedef enum { 31 | // label 32 | ST_LABEL = 0, 33 | // button 34 | ST_BUTTON = 1, 35 | // radio button 36 | ST_RADIO = 2, 37 | // progress slider 38 | ST_SLIDER = 3, 39 | // column 40 | ST_COLUMN = 4, 41 | // row 42 | ST_ROW = 5, 43 | // check button 44 | ST_CHECK = 6, 45 | // panel 46 | ST_PANEL = 7, 47 | // text 48 | ST_TEXT = 8, 49 | // 50 | ST_IGNORE = 9, 51 | 52 | ST_DEMOSTUFF = 10, 53 | // colored rectangle 54 | ST_RECT = 11, 55 | 56 | ST_HBOX = 12, 57 | ST_VBOX = 13, 58 | } SubType; 59 | 60 | typedef struct { 61 | int subtype; 62 | UIhandler handler; 63 | } UIData; 64 | 65 | typedef struct { 66 | UIData head; 67 | const char *label; 68 | NVGcolor color; 69 | } UIRectData; 70 | 71 | typedef struct { 72 | UIData head; 73 | int iconid; 74 | const char *label; 75 | } UIButtonData; 76 | 77 | typedef struct { 78 | UIData head; 79 | const char *label; 80 | bool *option; 81 | } UICheckData; 82 | 83 | typedef struct { 84 | UIData head; 85 | int iconid; 86 | const char *label; 87 | int *value; 88 | } UIRadioData; 89 | 90 | typedef struct { 91 | UIData head; 92 | const char *label; 93 | float *progress; 94 | } UISliderData; 95 | 96 | typedef struct { 97 | UIData head; 98 | char *text; 99 | int maxsize; 100 | } UITextData; 101 | 102 | //////////////////////////////////////////////////////////////////////////////// 103 | 104 | void draw_demostuff(NVGcontext *vg, int x, int y, float w, float h); 105 | 106 | void ui_handler(UIcontext *uictx, int item, UIevent event) { 107 | UIData *data = (UIData *)uiGetHandle(uictx, item); 108 | if (data && data->handler) { 109 | data->handler(uictx, item, event); 110 | } 111 | } 112 | 113 | void init(NVGcontext *vg) { 114 | bndSetFont(nvgCreateFont(vg, "system", "../DejaVuSans.ttf")); 115 | bndSetIconImage(nvgCreateImage(vg, "../blender_icons16.png", 0)); 116 | } 117 | 118 | void testrect(NVGcontext *vg, UIrect rect) { 119 | #if 0 120 | nvgBeginPath(vg); 121 | nvgRect(vg,rect.x+0.5,rect.y+0.5,rect.w-1,rect.h-1); 122 | nvgStrokeColor(vg,nvgRGBf(1,0,0)); 123 | nvgStrokeWidth(vg,1); 124 | nvgStroke(vg); 125 | #endif 126 | } 127 | 128 | 129 | void drawUI(exContext *ectx, int item, int corners); 130 | 131 | void drawUIItems(exContext *ectx, int item, int corners) { 132 | int kid = uiFirstChild(ectx->uictx, item); 133 | while (kid > 0) { 134 | drawUI(ectx, kid, corners); 135 | kid = uiNextSibling(ectx->uictx, kid); 136 | } 137 | } 138 | 139 | void drawUIItemsHbox(exContext *ectx, int item) { 140 | int kid = uiFirstChild(ectx->uictx, item); 141 | if (kid < 0) return; 142 | int nextkid = uiNextSibling(ectx->uictx, kid); 143 | if (nextkid < 0) { 144 | drawUI(ectx, kid, BND_CORNER_NONE); 145 | } else { 146 | drawUI(ectx, kid, BND_CORNER_RIGHT); 147 | kid = nextkid; 148 | while (uiNextSibling(ectx->uictx, kid) > 0) { 149 | drawUI(ectx, kid, BND_CORNER_ALL); 150 | kid = uiNextSibling(ectx->uictx, kid); 151 | } 152 | drawUI(ectx, kid, BND_CORNER_LEFT); 153 | } 154 | } 155 | 156 | void drawUIItemsVbox(exContext *ectx, int item) { 157 | int kid = uiFirstChild(ectx->uictx, item); 158 | if (kid < 0) return; 159 | int nextkid = uiNextSibling(ectx->uictx, kid); 160 | if (nextkid < 0) { 161 | drawUI(ectx, kid, BND_CORNER_NONE); 162 | } else { 163 | drawUI(ectx, kid, BND_CORNER_DOWN); 164 | kid = nextkid; 165 | while (uiNextSibling(ectx->uictx, kid) > 0) { 166 | drawUI(ectx, kid, BND_CORNER_ALL); 167 | kid = uiNextSibling(ectx->uictx, kid); 168 | } 169 | drawUI(ectx, kid, BND_CORNER_TOP); 170 | } 171 | } 172 | 173 | void drawUI(exContext *ectx, int item, int corners) { 174 | NVGcontext *vg = ectx->vg; 175 | UIcontext *uictx = ectx->uictx; 176 | const UIData *head = (const UIData *)uiGetHandle(uictx, item); 177 | UIrect rect = uiGetRect(uictx, item); 178 | if (uiGetState(uictx, item) == UI_FROZEN) { 179 | nvgGlobalAlpha(vg, BND_DISABLED_ALPHA); 180 | } 181 | if (head) { 182 | switch(head->subtype) { 183 | default: { 184 | testrect(vg, rect); 185 | drawUIItems(ectx,item,corners); 186 | } break; 187 | case ST_HBOX: { 188 | drawUIItemsHbox(ectx, item); 189 | } break; 190 | case ST_VBOX: { 191 | drawUIItemsVbox(ectx, item); 192 | } break; 193 | case ST_PANEL: { 194 | bndBevel(vg,rect.x,rect.y,rect.w,rect.h); 195 | drawUIItems(ectx,item,corners); 196 | } break; 197 | case ST_LABEL: { 198 | assert(head); 199 | const UIButtonData *data = (UIButtonData*)head; 200 | bndLabel(vg,rect.x,rect.y,rect.w,rect.h, 201 | data->iconid,data->label); 202 | } break; 203 | case ST_BUTTON: { 204 | const UIButtonData *data = (UIButtonData*)head; 205 | bndToolButton(vg,rect.x,rect.y,rect.w,rect.h, 206 | corners,(BNDwidgetState)uiGetState(uictx, item), 207 | data->iconid,data->label); 208 | } break; 209 | case ST_CHECK: { 210 | const UICheckData *data = (UICheckData*)head; 211 | BNDwidgetState state = (BNDwidgetState)uiGetState(uictx, item); 212 | if (*data->option) 213 | state = BND_ACTIVE; 214 | bndOptionButton(vg,rect.x,rect.y,rect.w,rect.h, state, 215 | data->label); 216 | } break; 217 | case ST_RADIO:{ 218 | const UIRadioData *data = (UIRadioData*)head; 219 | BNDwidgetState state = (BNDwidgetState)uiGetState(uictx, item); 220 | if (*data->value == item) 221 | state = BND_ACTIVE; 222 | bndRadioButton(vg,rect.x,rect.y,rect.w,rect.h, 223 | corners,state, 224 | data->iconid,data->label); 225 | } break; 226 | case ST_SLIDER:{ 227 | const UISliderData *data = (UISliderData*)head; 228 | BNDwidgetState state = (BNDwidgetState)uiGetState(uictx, item); 229 | static char value[32]; 230 | sprintf(value,"%.0f%%",(*data->progress)*100.0f); 231 | bndSlider(vg,rect.x,rect.y,rect.w,rect.h, 232 | corners,state, 233 | *data->progress,data->label,value); 234 | } break; 235 | case ST_TEXT: { 236 | const UITextData *data = (UITextData*)head; 237 | BNDwidgetState state = (BNDwidgetState)uiGetState(uictx, item); 238 | int idx = strlen(data->text); 239 | bndTextField(vg,rect.x,rect.y,rect.w,rect.h, 240 | corners,state, -1, data->text, idx, idx); 241 | } break; 242 | case ST_DEMOSTUFF: { 243 | draw_demostuff(vg, rect.x, rect.y, rect.w, rect.h); 244 | } break; 245 | case ST_RECT: { 246 | const UIRectData *data = (UIRectData*)head; 247 | if (rect.w && rect.h) { 248 | BNDwidgetState state = (BNDwidgetState)uiGetState(uictx, item); 249 | nvgSave(vg); 250 | nvgStrokeColor(vg, nvgRGBAf(data->color.r,data->color.g,data->color.b,0.9f)); 251 | if (state != BND_DEFAULT) { 252 | nvgFillColor(vg, nvgRGBAf(data->color.r,data->color.g,data->color.b,0.5f)); 253 | } else { 254 | nvgFillColor(vg, nvgRGBAf(data->color.r,data->color.g,data->color.b,0.1f)); 255 | } 256 | nvgStrokeWidth(vg,2); 257 | nvgBeginPath(vg); 258 | #if 0 259 | nvgRect(vg,rect.x,rect.y,rect.w,rect.h); 260 | #else 261 | nvgRoundedRect(vg,rect.x,rect.y,rect.w,rect.h,3); 262 | #endif 263 | nvgFill(vg); 264 | nvgStroke(vg); 265 | 266 | if (state != BND_DEFAULT) { 267 | nvgFillColor(vg, nvgRGBAf(0.0f,0.0f,0.0f,1.0f)); 268 | nvgFontSize(vg, 15.0f); 269 | nvgBeginPath(vg); 270 | nvgTextAlign(vg, NVG_ALIGN_TOP|NVG_ALIGN_CENTER); 271 | nvgTextBox(vg, rect.x, rect.y+rect.h*0.3f, rect.w, data->label, NULL); 272 | } 273 | 274 | nvgRestore(vg); 275 | } 276 | nvgSave(vg); 277 | nvgIntersectScissor(vg, rect.x, rect.y, rect.w, rect.h); 278 | 279 | drawUIItems(ectx,item,corners); 280 | 281 | nvgRestore(vg); 282 | } break; 283 | } 284 | } else { 285 | testrect(vg,rect); 286 | drawUIItems(ectx,item,corners); 287 | } 288 | 289 | if (uiGetState(uictx, item) == UI_FROZEN) { 290 | nvgGlobalAlpha(vg, 1.0); 291 | } 292 | } 293 | 294 | 295 | int colorrect(UIcontext *uictx, const char *label, NVGcolor color) { 296 | int item = uiItem(uictx); 297 | UIRectData *data = (UIRectData *)uiAllocHandle(uictx, item, sizeof(UIRectData)); 298 | data->head.subtype = ST_RECT; 299 | data->head.handler = NULL; 300 | data->label = label; 301 | data->color = color; 302 | uiSetEvents(uictx, item, UI_BUTTON0_DOWN); 303 | return item; 304 | } 305 | 306 | int label(UIcontext *uictx, int iconid, const char *label) { 307 | int item = uiItem(uictx); 308 | uiSetSize(uictx, item, 0, BND_WIDGET_HEIGHT); 309 | UIButtonData *data = (UIButtonData *)uiAllocHandle(uictx, item, sizeof(UIButtonData)); 310 | data->head.subtype = ST_LABEL; 311 | data->head.handler = NULL; 312 | data->iconid = iconid; 313 | data->label = label; 314 | return item; 315 | } 316 | 317 | void demohandler(UIcontext *uictx, int item, UIevent event) { 318 | const UIButtonData *data = (const UIButtonData *)uiGetHandle(uictx, item); 319 | printf("clicked: %p %s\n", uiGetHandle(uictx, item), data->label); 320 | } 321 | 322 | int button(UIcontext *uictx, int iconid, const char *label, UIhandler handler) { 323 | // create new ui item 324 | int item = uiItem(uictx); 325 | // set size of wiget; horizontal size is dynamic, vertical is fixed 326 | uiSetSize(uictx, item, 0, BND_WIDGET_HEIGHT); 327 | uiSetEvents(uictx, item, UI_BUTTON0_HOT_UP); 328 | // store some custom data with the button that we use for styling 329 | UIButtonData *data = (UIButtonData *)uiAllocHandle(uictx, item, sizeof(UIButtonData)); 330 | data->head.subtype = ST_BUTTON; 331 | data->head.handler = handler; 332 | data->iconid = iconid; 333 | data->label = label; 334 | return item; 335 | } 336 | 337 | void checkhandler(UIcontext *uictx, int item, UIevent event) { 338 | const UICheckData *data = (const UICheckData *)uiGetHandle(uictx, item); 339 | *data->option = !(*data->option); 340 | } 341 | 342 | int check(UIcontext *uictx, const char *label, bool *option) { 343 | // create new ui item 344 | int item = uiItem(uictx); 345 | // set size of wiget; horizontal size is dynamic, vertical is fixed 346 | uiSetSize(uictx, item, 0, BND_WIDGET_HEIGHT); 347 | // attach event handler e.g. demohandler above 348 | uiSetEvents(uictx, item, UI_BUTTON0_DOWN); 349 | // store some custom data with the button that we use for styling 350 | UICheckData *data = (UICheckData *)uiAllocHandle(uictx, item, sizeof(UICheckData)); 351 | data->head.subtype = ST_CHECK; 352 | data->head.handler = checkhandler; 353 | data->label = label; 354 | data->option = option; 355 | return item; 356 | } 357 | 358 | // simple logic for a slider 359 | 360 | // starting offset of the currently active slider 361 | static float sliderstart = 0.0; 362 | 363 | // event handler for slider (same handler for all sliders) 364 | void sliderhandler(UIcontext *uictx, int item, UIevent event) { 365 | // retrieve the custom data we saved with the slider 366 | UISliderData *data = (UISliderData *)uiGetHandle(uictx, item); 367 | switch(event) { 368 | default: break; 369 | case UI_BUTTON0_DOWN: { 370 | // button was pressed for the first time; capture initial 371 | // slider value. 372 | sliderstart = *data->progress; 373 | } break; 374 | case UI_BUTTON0_CAPTURE: { 375 | // called for every frame that the button is pressed. 376 | // get the delta between the click point and the current 377 | // mouse position 378 | UIvec2 pos = uiGetCursorStartDelta(uictx); 379 | // get the items layouted rectangle 380 | UIrect rc = uiGetRect(uictx, item); 381 | // calculate our new offset and clamp 382 | float value = sliderstart + ((float)pos.x / (float)rc.w); 383 | value = (value<0)?0:(value>1)?1:value; 384 | // assign the new value 385 | *data->progress = value; 386 | } break; 387 | } 388 | } 389 | 390 | int slider(UIcontext *uictx, const char *label, float *progress) { 391 | // create new ui item 392 | int item = uiItem(uictx); 393 | // set size of wiget; horizontal size is dynamic, vertical is fixed 394 | uiSetSize(uictx, item, 0, BND_WIDGET_HEIGHT); 395 | // attach our slider event handler and capture two classes of events 396 | uiSetEvents(uictx, item, UI_BUTTON0_DOWN | UI_BUTTON0_CAPTURE); 397 | // store some custom data with the button that we use for styling 398 | // and logic, e.g. the pointer to the data we want to alter. 399 | UISliderData *data = (UISliderData *)uiAllocHandle(uictx, item, sizeof(UISliderData)); 400 | data->head.subtype = ST_SLIDER; 401 | data->head.handler = sliderhandler; 402 | data->label = label; 403 | data->progress = progress; 404 | return item; 405 | } 406 | 407 | void textboxhandler(UIcontext *uictx, int item, UIevent event) { 408 | UITextData *data = (UITextData *)uiGetHandle(uictx, item); 409 | switch(event) { 410 | default: break; 411 | case UI_BUTTON0_DOWN: { 412 | uiFocus(uictx, item); 413 | } break; 414 | case UI_KEY_DOWN: { 415 | unsigned int key = uiGetKey(uictx); 416 | switch(key) { 417 | default: break; 418 | case GLFW_KEY_BACKSPACE: { 419 | int size = strlen(data->text); 420 | if (!size) return; 421 | data->text[size-1] = 0; 422 | } break; 423 | case GLFW_KEY_ENTER: { 424 | uiFocus(uictx, -1); 425 | } break; 426 | } 427 | } break; 428 | case UI_CHAR: { 429 | unsigned int key = uiGetKey(uictx); 430 | if ((key > 255)||(key < 32)) return; 431 | int size = strlen(data->text); 432 | if (size >= (data->maxsize-1)) return; 433 | data->text[size] = (char)key; 434 | } break; 435 | } 436 | } 437 | 438 | int textbox(UIcontext *uictx, char *text, int maxsize) { 439 | int item = uiItem(uictx); 440 | uiSetSize(uictx, item, 0, BND_WIDGET_HEIGHT); 441 | uiSetEvents(uictx, item, UI_BUTTON0_DOWN | UI_KEY_DOWN | UI_CHAR); 442 | // store some custom data with the button that we use for styling 443 | // and logic, e.g. the pointer to the data we want to alter. 444 | UITextData *data = (UITextData *)uiAllocHandle(uictx, item, sizeof(UITextData)); 445 | data->head.subtype = ST_TEXT; 446 | data->head.handler = textboxhandler; 447 | data->text = text; 448 | data->maxsize = maxsize; 449 | return item; 450 | } 451 | 452 | // simple logic for a radio button 453 | void radiohandler(UIcontext *uictx, int item, UIevent event) { 454 | UIRadioData *data = (UIRadioData *)uiGetHandle(uictx, item); 455 | *data->value = item; 456 | } 457 | 458 | int radio(UIcontext *uictx, int iconid, const char *label, int *value) { 459 | int item = uiItem(uictx); 460 | uiSetSize(uictx, item, label?0:BND_TOOL_WIDTH, BND_WIDGET_HEIGHT); 461 | UIRadioData *data = (UIRadioData *)uiAllocHandle(uictx, item, sizeof(UIRadioData)); 462 | data->head.subtype = ST_RADIO; 463 | data->head.handler = radiohandler; 464 | data->iconid = iconid; 465 | data->label = label; 466 | data->value = value; 467 | uiSetEvents(uictx, item, UI_BUTTON0_DOWN); 468 | return item; 469 | } 470 | 471 | int panel(UIcontext *uictx) { 472 | int item = uiItem(uictx); 473 | UIData *data = (UIData *)uiAllocHandle(uictx, item, sizeof(UIData)); 474 | data->subtype = ST_PANEL; 475 | data->handler = NULL; 476 | return item; 477 | } 478 | 479 | int hbox(UIcontext *uictx) { 480 | int item = uiItem(uictx); 481 | UIData *data = (UIData *)uiAllocHandle(uictx, item, sizeof(UIData)); 482 | data->subtype = ST_HBOX; 483 | data->handler = NULL; 484 | uiSetBox(uictx, item, UI_ROW); 485 | return item; 486 | } 487 | 488 | 489 | int vbox(UIcontext *uictx) { 490 | int item = uiItem(uictx); 491 | UIData *data = (UIData *)uiAllocHandle(uictx, item, sizeof(UIData)); 492 | data->subtype = ST_VBOX; 493 | data->handler = NULL; 494 | uiSetBox(uictx, item, UI_COLUMN); 495 | return item; 496 | } 497 | 498 | 499 | int column_append(UIcontext *uictx, int parent, int item) { 500 | uiInsert(uictx, parent, item); 501 | // fill parent horizontally, anchor to previous item vertically 502 | uiSetLayout(uictx, item, UI_HFILL); 503 | uiSetMargins(uictx, item, 0, 1, 0, 0); 504 | return item; 505 | } 506 | 507 | int column(UIcontext *uictx) { 508 | int item = uiItem(uictx); 509 | uiSetBox(uictx, item, UI_COLUMN); 510 | return item; 511 | } 512 | 513 | int vgroup_append(UIcontext *uictx, int parent, int item) { 514 | uiInsert(uictx, parent, item); 515 | // fill parent horizontally, anchor to previous item vertically 516 | uiSetLayout(uictx, item, UI_HFILL); 517 | return item; 518 | } 519 | 520 | int vgroup(UIcontext *uictx) { 521 | int item = uiItem(uictx); 522 | uiSetBox(uictx, item, UI_COLUMN); 523 | return item; 524 | } 525 | 526 | int hgroup_append(UIcontext *uictx, int parent, int item) { 527 | uiInsert(uictx, parent, item); 528 | uiSetLayout(uictx, item, UI_HFILL); 529 | return item; 530 | } 531 | 532 | int hgroup_append_fixed(UIcontext *uictx, int parent, int item) { 533 | uiInsert(uictx, parent, item); 534 | return item; 535 | } 536 | 537 | int hgroup(UIcontext *uictx) { 538 | int item = uiItem(uictx); 539 | uiSetBox(uictx, item, UI_ROW); 540 | return item; 541 | } 542 | 543 | int row_append(UIcontext *uictx, int parent, int item) { 544 | uiInsert(uictx, parent, item); 545 | uiSetLayout(uictx, item, UI_HFILL); 546 | return item; 547 | } 548 | 549 | int row(UIcontext *uictx) { 550 | int item = uiItem(uictx); 551 | uiSetBox(uictx, item, UI_ROW); 552 | return item; 553 | } 554 | 555 | void draw_noodles(NVGcontext *vg, int x, int y) { 556 | int w = 200; 557 | int s = 70; 558 | int i; 559 | 560 | bndNodeBackground(vg, x+w, y-50, 100, 200, BND_DEFAULT, BND_ICONID(6,3), 561 | "Default", nvgRGBf(0.392f,0.392f,0.392f)); 562 | bndNodeBackground(vg, x+w+120, y-50, 100, 200, BND_HOVER, BND_ICONID(6,3), 563 | "Hover", nvgRGBf(0.392f,0.392f,0.392f)); 564 | bndNodeBackground(vg, x+w+240, y-50, 100, 200, BND_ACTIVE, BND_ICONID(6,3), 565 | "Active", nvgRGBf(0.392f,0.392f,0.392f)); 566 | 567 | for (i = 0; i < 9; ++i) { 568 | int a = i%3; 569 | int b = i/3; 570 | bndNodeWire(vg, x, y+s*a, x+w, y+s*b, (BNDwidgetState)a, (BNDwidgetState)b); 571 | } 572 | 573 | bndNodePort(vg, x, y, BND_DEFAULT, nvgRGBf(0.5f, 0.5f, 0.5f)); 574 | bndNodePort(vg, x+w, y, BND_DEFAULT, nvgRGBf(0.5f, 0.5f, 0.5f)); 575 | bndNodePort(vg, x, y+s, BND_HOVER, nvgRGBf(0.5f, 0.5f, 0.5f)); 576 | bndNodePort(vg, x+w, y+s, BND_HOVER, nvgRGBf(0.5f, 0.5f, 0.5f)); 577 | bndNodePort(vg, x, y+2*s, BND_ACTIVE, nvgRGBf(0.5f, 0.5f, 0.5f)); 578 | bndNodePort(vg, x+w, y+2*s, BND_ACTIVE, nvgRGBf(0.5f, 0.5f, 0.5f)); 579 | } 580 | 581 | static void roothandler(UIcontext *uictx, int parent, UIevent event) { 582 | switch(event) { 583 | default: break; 584 | case UI_SCROLL: { 585 | UIvec2 pos = uiGetScroll(uictx); 586 | printf("scroll! %d %d\n", pos.x, pos.y); 587 | } break; 588 | case UI_BUTTON0_DOWN: { 589 | printf("%d clicks\n", uiGetClicks(uictx)); 590 | } break; 591 | } 592 | } 593 | 594 | void draw_demostuff(NVGcontext *vg, int x, int y, float w, float h) { 595 | nvgSave(vg); 596 | nvgTranslate(vg, x, y); 597 | 598 | bndSplitterWidgets(vg, 0, 0, w, h); 599 | 600 | x = 10; 601 | y = 10; 602 | 603 | bndToolButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 604 | BND_ICONID(6,3),"Default"); 605 | y += 25; 606 | bndToolButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 607 | BND_ICONID(6,3),"Hovered"); 608 | y += 25; 609 | bndToolButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 610 | BND_ICONID(6,3),"Active"); 611 | 612 | y += 40; 613 | bndRadioButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 614 | -1,"Default"); 615 | y += 25; 616 | bndRadioButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 617 | -1,"Hovered"); 618 | y += 25; 619 | bndRadioButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 620 | -1,"Active"); 621 | 622 | y += 25; 623 | bndLabel(vg,x,y,120,BND_WIDGET_HEIGHT,-1,"Label:"); 624 | y += BND_WIDGET_HEIGHT; 625 | bndChoiceButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 626 | -1, "Default"); 627 | y += 25; 628 | bndChoiceButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 629 | -1, "Hovered"); 630 | y += 25; 631 | bndChoiceButton(vg,x,y,80,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 632 | -1, "Active"); 633 | 634 | y += 25; 635 | int ry = y; 636 | int rx = x; 637 | 638 | y = 10; 639 | x += 130; 640 | bndOptionButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_DEFAULT,"Default"); 641 | y += 25; 642 | bndOptionButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_HOVER,"Hovered"); 643 | y += 25; 644 | bndOptionButton(vg,x,y,120,BND_WIDGET_HEIGHT,BND_ACTIVE,"Active"); 645 | 646 | y += 40; 647 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_DOWN,BND_DEFAULT, 648 | "Top","100"); 649 | y += BND_WIDGET_HEIGHT-2; 650 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, 651 | "Center","100"); 652 | y += BND_WIDGET_HEIGHT-2; 653 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_TOP,BND_DEFAULT, 654 | "Bottom","100"); 655 | 656 | int mx = x-30; 657 | int my = y-12; 658 | int mw = 120; 659 | bndMenuBackground(vg,mx,my,mw,120,BND_CORNER_TOP); 660 | bndMenuLabel(vg,mx,my,mw,BND_WIDGET_HEIGHT,-1,"Menu Title"); 661 | my += BND_WIDGET_HEIGHT-2; 662 | bndMenuItem(vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_DEFAULT, 663 | BND_ICONID(17,3),"Default"); 664 | my += BND_WIDGET_HEIGHT-2; 665 | bndMenuItem(vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_HOVER, 666 | BND_ICONID(18,3),"Hovered"); 667 | my += BND_WIDGET_HEIGHT-2; 668 | bndMenuItem(vg,mx,my,mw,BND_WIDGET_HEIGHT,BND_ACTIVE, 669 | BND_ICONID(19,3),"Active"); 670 | 671 | y = 10; 672 | x += 130; 673 | int ox = x; 674 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 675 | "Default","100"); 676 | y += 25; 677 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 678 | "Hovered","100"); 679 | y += 25; 680 | bndNumberField(vg,x,y,120,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 681 | "Active","100"); 682 | 683 | y += 40; 684 | bndRadioButton(vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT,BND_DEFAULT, 685 | -1,"One"); 686 | x += 60-1; 687 | bndRadioButton(vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, 688 | -1,"Two"); 689 | x += 60-1; 690 | bndRadioButton(vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_ALL,BND_DEFAULT, 691 | -1,"Three"); 692 | x += 60-1; 693 | bndRadioButton(vg,x,y,60,BND_WIDGET_HEIGHT,BND_CORNER_LEFT,BND_ACTIVE, 694 | -1,"Butts"); 695 | 696 | x = ox; 697 | y += 40; 698 | float progress_value = fmodf(glfwGetTime()/10.0,1.0); 699 | char progress_label[32]; 700 | sprintf(progress_label, "%d%%", (int)(progress_value*100+0.5f)); 701 | bndSlider(vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 702 | progress_value,"Default",progress_label); 703 | y += 25; 704 | bndSlider(vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 705 | progress_value,"Hovered",progress_label); 706 | y += 25; 707 | bndSlider(vg,x,y,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 708 | progress_value,"Active",progress_label); 709 | 710 | int rw = x+240-rx; 711 | float s_offset = sinf(glfwGetTime()/2.0)*0.5+0.5; 712 | float s_size = cosf(glfwGetTime()/3.11)*0.5+0.5; 713 | 714 | bndScrollBar(vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_DEFAULT,s_offset,s_size); 715 | ry += 20; 716 | bndScrollBar(vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_HOVER,s_offset,s_size); 717 | ry += 20; 718 | bndScrollBar(vg,rx,ry,rw,BND_SCROLLBAR_HEIGHT,BND_ACTIVE,s_offset,s_size); 719 | 720 | const char edit_text[] = "The quick brown fox"; 721 | int textlen = strlen(edit_text)+1; 722 | int t = (int)(glfwGetTime()*2); 723 | int idx1 = (t/textlen)%textlen; 724 | int idx2 = idx1 + (t%(textlen-idx1)); 725 | 726 | ry += 25; 727 | bndTextField(vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_DEFAULT, 728 | -1, edit_text, idx1, idx2); 729 | ry += 25; 730 | bndTextField(vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_HOVER, 731 | -1, edit_text, idx1, idx2); 732 | ry += 25; 733 | bndTextField(vg,rx,ry,240,BND_WIDGET_HEIGHT,BND_CORNER_NONE,BND_ACTIVE, 734 | -1, edit_text, idx1, idx2); 735 | 736 | draw_noodles(vg, 20, ry+50); 737 | 738 | rx += rw + 20; 739 | ry = 10; 740 | bndScrollBar(vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_DEFAULT,s_offset,s_size); 741 | rx += 20; 742 | bndScrollBar(vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_HOVER,s_offset,s_size); 743 | rx += 20; 744 | bndScrollBar(vg,rx,ry,BND_SCROLLBAR_WIDTH,240,BND_ACTIVE,s_offset,s_size); 745 | 746 | x = ox; 747 | y += 40; 748 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT, 749 | BND_DEFAULT,BND_ICONID(0,10),NULL); 750 | x += BND_TOOL_WIDTH-1; 751 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 752 | BND_DEFAULT,BND_ICONID(1,10),NULL); 753 | x += BND_TOOL_WIDTH-1; 754 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 755 | BND_DEFAULT,BND_ICONID(2,10),NULL); 756 | x += BND_TOOL_WIDTH-1; 757 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 758 | BND_DEFAULT,BND_ICONID(3,10),NULL); 759 | x += BND_TOOL_WIDTH-1; 760 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 761 | BND_DEFAULT,BND_ICONID(4,10),NULL); 762 | x += BND_TOOL_WIDTH-1; 763 | bndToolButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, 764 | BND_DEFAULT,BND_ICONID(5,10),NULL); 765 | x += BND_TOOL_WIDTH-1; 766 | x += 5; 767 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_RIGHT, 768 | BND_DEFAULT,BND_ICONID(0,11),NULL); 769 | x += BND_TOOL_WIDTH-1; 770 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 771 | BND_DEFAULT,BND_ICONID(1,11),NULL); 772 | x += BND_TOOL_WIDTH-1; 773 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 774 | BND_DEFAULT,BND_ICONID(2,11),NULL); 775 | x += BND_TOOL_WIDTH-1; 776 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 777 | BND_DEFAULT,BND_ICONID(3,11),NULL); 778 | x += BND_TOOL_WIDTH-1; 779 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_ALL, 780 | BND_ACTIVE,BND_ICONID(4,11),NULL); 781 | x += BND_TOOL_WIDTH-1; 782 | bndRadioButton(vg,x,y,BND_TOOL_WIDTH,BND_WIDGET_HEIGHT,BND_CORNER_LEFT, 783 | BND_DEFAULT,BND_ICONID(5,11),NULL); 784 | 785 | nvgRestore(vg); 786 | } 787 | 788 | static int enum1 = -1; 789 | 790 | void build_democontent(UIcontext *uictx, int parent) { 791 | // some persistent variables for demonstration 792 | static float progress1 = 0.25f; 793 | static float progress2 = 0.75f; 794 | static bool option1 = true; 795 | static bool option2 = false; 796 | static bool option3 = false; 797 | 798 | int col = column(uictx); 799 | uiInsert(uictx, parent, col); 800 | uiSetMargins(uictx, col, 10, 10, 10, 10); 801 | uiSetLayout(uictx, col, UI_TOP|UI_HFILL); 802 | 803 | column_append(uictx, col, button(uictx, BND_ICON_GHOST, "Item 1", demohandler)); 804 | if (option3) 805 | column_append(uictx, col, button(uictx, BND_ICON_GHOST, "Item 2", demohandler)); 806 | 807 | { 808 | int h = column_append(uictx, col, hbox(uictx)); 809 | hgroup_append(uictx, h, radio(uictx, BND_ICON_GHOST, "Item 3.0", &enum1)); 810 | if (option2) 811 | uiSetMargins(uictx, hgroup_append_fixed(uictx, h, radio(uictx, BND_ICON_REC, NULL, &enum1)), -1,0,0,0); 812 | uiSetMargins(uictx, hgroup_append_fixed(uictx, h, radio(uictx, BND_ICON_PLAY, NULL, &enum1)), -1,0,0,0); 813 | uiSetMargins(uictx, hgroup_append(uictx, h, radio(uictx, BND_ICON_GHOST, "Item 3.3", &enum1)), -1,0,0,0); 814 | } 815 | 816 | { 817 | int rows = column_append(uictx, col, row(uictx)); 818 | int coll = row_append(uictx, rows, vgroup(uictx)); 819 | vgroup_append(uictx, coll, label(uictx, -1, "Items 4.0:")); 820 | coll = vgroup_append(uictx, coll, vbox(uictx)); 821 | vgroup_append(uictx, coll, button(uictx, BND_ICON_GHOST, "Item 4.0.0", demohandler)); 822 | uiSetMargins(uictx, vgroup_append(uictx, coll, button(uictx, BND_ICON_GHOST, "Item 4.0.1", demohandler)),0,-2,0,0); 823 | int colr = row_append(uictx, rows, vgroup(uictx)); 824 | uiSetMargins(uictx, colr, 8, 0, 0, 0); 825 | uiSetFrozen(uictx, colr, option1); 826 | vgroup_append(uictx, colr, label(uictx, -1, "Items 4.1:")); 827 | colr = vgroup_append(uictx, colr, vbox(uictx)); 828 | vgroup_append(uictx, colr, slider(uictx, "Item 4.1.0", &progress1)); 829 | uiSetMargins(uictx, vgroup_append(uictx, colr, slider(uictx, "Item 4.1.1", &progress2)),0,-2,0,0); 830 | } 831 | 832 | column_append(uictx, col, button(uictx, BND_ICON_GHOST, "Item 5", NULL)); 833 | 834 | static char textbuffer[1024] = "The quick brown fox."; 835 | column_append(uictx, col, textbox(uictx, textbuffer, 1024)); 836 | 837 | column_append(uictx, col, check(uictx, "Frozen", &option1)); 838 | column_append(uictx, col, check(uictx, "Item 7", &option2)); 839 | column_append(uictx, col, check(uictx, "Item 8", &option3)); 840 | } 841 | 842 | int demorect(UIcontext *uictx, int parent, const char *label, float hue, int box, int layout, int w, int h, int m1, int m2, int m3, int m4) { 843 | int item = colorrect(uictx, label, nvgHSL(hue, 1.0f, 0.8f)); 844 | uiSetLayout(uictx, item, layout); 845 | uiSetBox(uictx, item, box); 846 | uiSetMargins(uictx, item, m1, m2, m3, m4); 847 | uiSetSize(uictx, item, w, h); 848 | uiInsert(uictx, parent, item); 849 | return item; 850 | } 851 | 852 | void build_layoutdemo(UIcontext *uictx, int parent) { 853 | const int M = 10; 854 | const int S = 150; 855 | 856 | int box = demorect(uictx, parent, "Box( UI_LAYOUT )\nLayout( UI_FILL )", 0.6f, UI_LAYOUT, UI_FILL, 0, 0, M, M, M, M); 857 | demorect(uictx, box, "Layout( UI_HFILL | UI_TOP )", 0.7f, 0, UI_HFILL|UI_TOP, S, S+M, M, M, M, 0); 858 | demorect(uictx, box, "Layout( UI_HFILL )", 0.7f, 0, UI_HFILL, S, S+2*M, M, 0, M, 0); 859 | demorect(uictx, box, "Layout( UI_HFILL | UI_DOWN )", 0.7f, 0, UI_HFILL|UI_DOWN, S, S+M, M, 0, M, M); 860 | 861 | demorect(uictx, box, "Layout( UI_LEFT | UI_VFILL )", 0.7f, 0, UI_LEFT|UI_VFILL, S+M, S, M, M, 0, M); 862 | demorect(uictx, box, "Layout( UI_VFILL )", 0.7f, 0, UI_VFILL, S+2*M, S, 0, M, 0, M); 863 | demorect(uictx, box, "Layout( UI_RIGHT | UI_VFILL )", 0.7f, 0, UI_RIGHT|UI_VFILL, S+M, S, 0, M, M, M); 864 | 865 | demorect(uictx, box, "Layout( UI_LEFT | UI_TOP )", 0.55f, 0, UI_LEFT|UI_TOP, S, S, M, M, 0, 0); 866 | demorect(uictx, box, "Layout( UI_TOP )", 0.57f, 0, UI_TOP, S, S, 0, M, 0, 0); 867 | demorect(uictx, box, "Layout( UI_RIGHT | UI_TOP )", 0.55f, 0, UI_RIGHT|UI_TOP, S, S, 0, M, M, 0); 868 | demorect(uictx, box, "Layout( UI_LEFT )", 0.57f, 0, UI_LEFT, S, S, M, 0, 0, 0); 869 | demorect(uictx, box, "Layout( UI_CENTER )", 0.59f, 0, UI_CENTER, S, S, 0, 0, 0, 0); 870 | demorect(uictx, box, "Layout( UI_RIGHT )", 0.57f, 0, UI_RIGHT, S, S, 0, 0, M, 0); 871 | demorect(uictx, box, "Layout( UI_LEFT | UI_DOWN )", 0.55f, 0, UI_LEFT|UI_DOWN, S, S, M, 0, 0, M); 872 | demorect(uictx, box, "Layout( UI_DOWN)", 0.57f, 0, UI_DOWN, S, S, 0, 0, 0, M); 873 | demorect(uictx, box, "Layout( UI_RIGHT | UI_DOWN )", 0.55f, 0, UI_RIGHT|UI_DOWN, S, S, 0, 0, M, M); 874 | } 875 | 876 | void build_rowdemo(UIcontext *uictx, int parent) { 877 | uiSetBox(uictx, parent, UI_COLUMN); 878 | 879 | const int M = 10; 880 | const int S = 200; 881 | const int T = 100; 882 | 883 | { 884 | int box = demorect(uictx, parent, "Box( UI_ROW )\nLayout( UI_LEFT | UI_VFILL )", 0.6f, UI_ROW, UI_LEFT|UI_VFILL, 0, S, M, M, M, M); 885 | 886 | demorect(uictx, box, "Layout( UI_TOP )", 0.05f, 0, UI_TOP, T, T, M, M, M, 0); 887 | demorect(uictx, box, "Layout( UI_VCENTER )", 0.1f, 0, UI_VCENTER, T, T, 0, 0, M, 0); 888 | demorect(uictx, box, "Layout( UI_VFILL )", 0.15f, 0, UI_VFILL, T, T, 0, M, M, M); 889 | demorect(uictx, box, "Layout( UI_DOWN )", 0.25f, 0, UI_DOWN, T, T, 0, 0, M, M); 890 | } 891 | { 892 | int box = demorect(uictx, parent, "Box( UI_ROW | UI_JUSTIFY )\nLayout( UI_FILL )", 0.6f, UI_ROW|UI_JUSTIFY, UI_FILL, 0, S, M, 0, M, M); 893 | 894 | demorect(uictx, box, "Layout( UI_TOP )", 0.05f, 0, UI_TOP, T, T, M, M, M, 0); 895 | demorect(uictx, box, "Layout( UI_VCENTER )", 0.1f, 0, UI_VCENTER, T, T, 0, 0, M, 0); 896 | demorect(uictx, box, "Layout( UI_VFILL )", 0.15f, 0, UI_VFILL, T, T, 0, M, M, M); 897 | demorect(uictx, box, "Layout( UI_DOWN )", 0.25f, 0, UI_DOWN, T, T, 0, 0, M, M); 898 | } 899 | { 900 | int box = demorect(uictx, parent, "Box( UI_ROW )\nLayout( UI_FILL )", 0.6f, UI_ROW, UI_FILL, 0, S, M, 0, M, M); 901 | 902 | demorect(uictx, box, "Layout( UI_TOP )", 0.05f, 0, UI_TOP, T, T, M, M, M, 0); 903 | demorect(uictx, box, "Layout( UI_VCENTER )", 0.1f, 0, UI_VCENTER, T, T, 0, 0, M, 0); 904 | demorect(uictx, box, "Layout( UI_VFILL )", 0.15f, 0, UI_VFILL, T, T, 0, M, M, M); 905 | demorect(uictx, box, "Layout( UI_HFILL )", 0.2f, 0, UI_HFILL, T, T, 0, 0, M, 0); 906 | demorect(uictx, box, "Layout( UI_HFILL )", 0.2f, 0, UI_HFILL, T, T, 0, 0, M, 0); 907 | demorect(uictx, box, "Layout( UI_HFILL )", 0.2f, 0, UI_HFILL, T, T, 0, 0, M, 0); 908 | demorect(uictx, box, "Layout( UI_DOWN )", 0.25f, 0, UI_DOWN, T, T, 0, 0, M, M); 909 | } 910 | } 911 | 912 | void build_columndemo(UIcontext *uictx, int parent) { 913 | uiSetBox(uictx, parent, UI_ROW); 914 | 915 | const int M = 10; 916 | const int S = 200; 917 | const int T = 100; 918 | 919 | { 920 | int box = demorect(uictx, parent, "Box( UI_COLUMN )\nLayout( UI_TOP | UI_HFILL )", 0.6f, UI_COLUMN, UI_TOP|UI_HFILL, S, 0, M, M, M, M); 921 | 922 | demorect(uictx, box, "Layout( UI_LEFT )", 0.05f, 0, UI_LEFT, T, T, M, M, 0, M); 923 | demorect(uictx, box, "Layout( UI_HCENTER )", 0.1f, 0, UI_HCENTER, T, T, 0, 0, 0, M); 924 | demorect(uictx, box, "Layout( UI_HFILL )", 0.15f, 0, UI_HFILL, T, T, M, 0, M, M); 925 | demorect(uictx, box, "Layout( UI_RIGHT )", 0.25f, 0, UI_RIGHT, T, T, 0, 0, M, M); 926 | } 927 | { 928 | int box = demorect(uictx, parent, "Box( UI_COLUMN )\nLayout( UI_FILL )", 0.6f, UI_COLUMN, UI_FILL, S, 0, 0, M, M, M); 929 | 930 | demorect(uictx, box, "Layout( UI_LEFT )", 0.05f, 0, UI_LEFT, T, T, M, M, 0, M); 931 | demorect(uictx, box, "Layout( UI_HCENTER )", 0.1f, 0, UI_HCENTER, T, T, 0, 0, 0, M); 932 | demorect(uictx, box, "Layout( UI_HFILL )", 0.15f, 0, UI_HFILL, T, T, M, 0, M, M); 933 | demorect(uictx, box, "Layout( UI_RIGHT )", 0.25f, 0, UI_RIGHT, T, T, 0, 0, M, M); 934 | } 935 | { 936 | int box = demorect(uictx, parent, "Box( UI_COLUMN )\nLayout( UI_FILL )", 0.6f, UI_COLUMN, UI_FILL, S, 0, 0, M, M, M); 937 | 938 | demorect(uictx, box, "Layout( UI_LEFT )", 0.05f, 0, UI_LEFT, T, T, M, M, 0, M); 939 | demorect(uictx, box, "Layout( UI_HCENTER )", 0.1f, 0, UI_HCENTER, T, T, 0, 0, 0, M); 940 | demorect(uictx, box, "Layout( UI_HFILL )", 0.15f, 0, UI_HFILL, T, T, M, 0, M, M); 941 | demorect(uictx, box, "Layout( UI_VFILL )", 0.2f, 0, UI_VFILL, T, T, 0, 0, 0, M); 942 | demorect(uictx, box, "Layout( UI_VFILL )", 0.2f, 0, UI_VFILL, T, T, 0, 0, 0, M); 943 | demorect(uictx, box, "Layout( UI_VFILL )", 0.2f, 0, UI_VFILL, T, T, 0, 0, 0, M); 944 | demorect(uictx, box, "Layout( UI_RIGHT )", 0.25f, 0, UI_RIGHT, T, T, 0, 0, M, M); 945 | } 946 | } 947 | 948 | void fill_wrap_row_box(UIcontext *uictx, int box) { 949 | const int M = 5; 950 | const int T = 50; 951 | int i; 952 | 953 | srand(303); 954 | for (i = 0; i < 20; ++i) { 955 | float hue = (float)(rand()%360)/360.0f; 956 | int width = 10 + (rand()%5)*10; 957 | 958 | int u; 959 | switch(rand()%4) { 960 | default: break; 961 | case 0: { 962 | u = demorect(uictx, box, "Layout( UI_TOP )", 963 | hue, 0, UI_TOP, width, T, M, M, M, M); 964 | } break; 965 | case 1: { 966 | u = demorect(uictx, box, "Layout( UI_VCENTER )", 967 | hue, 0, UI_VCENTER, width, T/2, M, M, M, M); 968 | } break; 969 | case 2: { 970 | u = demorect(uictx, box, "Layout( UI_VFILL )", 971 | hue, 0, UI_VFILL, width, T, M, M, M, M); 972 | } break; 973 | case 3: { 974 | u = demorect(uictx, box, "Layout( UI_DOWN )", 975 | hue, 0, UI_DOWN, width, T/2, M, M, M, M); 976 | } break; 977 | } 978 | 979 | if (rand()%10 == 0) 980 | uiSetLayout(uictx, u, uiGetLayout(uictx, u)|UI_BREAK); 981 | 982 | } 983 | 984 | } 985 | 986 | void fill_wrap_column_box(UIcontext *uictx, int box) { 987 | const int M = 5; 988 | const int T = 50; 989 | int i; 990 | srand(303); 991 | for (i = 0; i < 20; ++i) { 992 | float hue = (float)(rand()%360)/360.0f; 993 | int height = 10 + (rand()%5)*10; 994 | 995 | int u; 996 | switch(rand()%4) { 997 | default: break; 998 | case 0: { 999 | u = demorect(uictx, box, "Layout( UI_LEFT )", 1000 | hue, 0, UI_LEFT, T, height, M, M, M, M); 1001 | } break; 1002 | case 1: { 1003 | u = demorect(uictx, box, "Layout( UI_HCENTER )", 1004 | hue, 0, UI_HCENTER, T/2, height, M, M, M, M); 1005 | } break; 1006 | case 2: { 1007 | u = demorect(uictx, box, "Layout( UI_HFILL )", 1008 | hue, 0, UI_HFILL, T, height, M, M, M, M); 1009 | } break; 1010 | case 3: { 1011 | u = demorect(uictx, box, "Layout( UI_RIGHT )", 1012 | hue, 0, UI_RIGHT, T/2, height, M, M, M, M); 1013 | } break; 1014 | } 1015 | 1016 | if (rand()%10 == 0) 1017 | uiSetLayout(uictx, u, uiGetLayout(uictx, u)|UI_BREAK); 1018 | } 1019 | 1020 | } 1021 | 1022 | void build_wrapdemo(UIcontext *uictx, int parent) { 1023 | int col = uiItem(uictx); 1024 | uiInsert(uictx, parent, col); 1025 | uiSetBox(uictx, col, UI_COLUMN); 1026 | uiSetLayout(uictx, col, UI_FILL); 1027 | 1028 | const int M = 5; 1029 | 1030 | int box; 1031 | box = demorect(uictx, col, "Box( UI_ROW | UI_WRAP | UI_START )\nLayout( UI_HFILL | UI_TOP )", 1032 | 0.6f, UI_ROW | UI_WRAP | UI_START, UI_TOP, 0, 0, M, M, M, M); 1033 | fill_wrap_row_box(uictx, box); 1034 | 1035 | box = demorect(uictx, col, "Box( UI_ROW | UI_WRAP | UI_MIDDLE )\nLayout( UI_HFILL | UI_TOP )", 1036 | 0.6f, UI_ROW | UI_WRAP, UI_HFILL | UI_TOP, 0, 0, M, M, M, M); 1037 | fill_wrap_row_box(uictx, box); 1038 | 1039 | box = demorect(uictx, col, "Box( UI_ROW | UI_WRAP | UI_END )\nLayout( UI_HFILL | UI_TOP )", 1040 | 0.6f, UI_ROW | UI_WRAP | UI_END, UI_HFILL | UI_TOP, 0, 0, M, M, M, M); 1041 | fill_wrap_row_box(uictx, box); 1042 | 1043 | box = demorect(uictx, col, "Box( UI_ROW | UI_WRAP | UI_JUSTIFY )\nLayout( UI_HFILL | UI_TOP )", 1044 | 0.6f, UI_ROW | UI_WRAP | UI_JUSTIFY, UI_HFILL | UI_TOP, 0, 0, M, M, M, M); 1045 | fill_wrap_row_box(uictx, box); 1046 | 1047 | box = demorect(uictx, col, "Box( UI_COLUMN | UI_WRAP | UI_START )\nLayout( UI_LEFT | UI_VFILL )", 1048 | 0.6f, UI_COLUMN | UI_WRAP | UI_START, UI_LEFT | UI_VFILL, 0, 0, M, M, M, M); 1049 | fill_wrap_column_box(uictx, box); 1050 | } 1051 | 1052 | 1053 | int add_menu_option(UIcontext *uictx, int parent, const char *name, int *choice) { 1054 | int opt = radio(uictx, -1, name, choice); 1055 | uiInsert(uictx, parent, opt); 1056 | uiSetLayout(uictx, opt, UI_HFILL|UI_TOP); 1057 | uiSetMargins(uictx, opt, 1, 1, 1, 1); 1058 | return opt; 1059 | } 1060 | 1061 | void draw(exContext *ec, float w, float h) { 1062 | UIcontext *uictx = ec->uictx; 1063 | NVGcontext *vg = ec->vg; 1064 | bndBackground(vg, 0, 0, w, h); 1065 | 1066 | // some OUI stuff 1067 | 1068 | uiBeginLayout(uictx); 1069 | 1070 | int root = panel(uictx); 1071 | // position root element 1072 | uiSetSize(uictx, 0,w,h); 1073 | ((UIData*)uiGetHandle(uictx, root))->handler = roothandler; 1074 | uiSetEvents(uictx, root, UI_SCROLL|UI_BUTTON0_DOWN); 1075 | uiSetBox(uictx, root, UI_COLUMN); 1076 | 1077 | static int choice = -1; 1078 | 1079 | int menu = uiItem(uictx); 1080 | uiSetLayout(uictx, menu, UI_HFILL|UI_TOP); 1081 | uiSetBox(uictx, menu, UI_ROW); 1082 | uiInsert(uictx, root, menu); 1083 | 1084 | int opt_blendish_demo = add_menu_option(uictx, menu, "Blendish Demo", &choice); 1085 | int opt_oui_demo = add_menu_option(uictx, menu, "OUI Demo", &choice); 1086 | int opt_layouts = add_menu_option(uictx, menu, "UI_LAYOUT", &choice); 1087 | int opt_row = add_menu_option(uictx, menu, "UI_ROW", &choice); 1088 | int opt_column = add_menu_option(uictx, menu, "UI_COLUMN", &choice); 1089 | int opt_wrap = add_menu_option(uictx, menu, "UI_WRAP", &choice); 1090 | if (choice < 0) 1091 | choice = opt_blendish_demo; 1092 | 1093 | int content = uiItem(uictx); 1094 | uiSetLayout(uictx, content, UI_FILL); 1095 | uiInsert(uictx, root, content); 1096 | 1097 | if (choice == opt_blendish_demo) { 1098 | int democontent = uiItem(uictx); 1099 | uiSetLayout(uictx, democontent, UI_FILL); 1100 | uiInsert(uictx, content, democontent); 1101 | 1102 | UIData *data = (UIData *)uiAllocHandle(uictx, democontent, sizeof(UIData)); 1103 | data->handler = 0; 1104 | data->subtype = ST_DEMOSTUFF; 1105 | } else if (choice == opt_oui_demo) { 1106 | int democontent = uiItem(uictx); 1107 | uiSetLayout(uictx, democontent, UI_TOP); 1108 | uiSetSize(uictx, democontent, 250, 0); 1109 | uiInsert(uictx, content, democontent); 1110 | 1111 | build_democontent(uictx, democontent); 1112 | } else if (choice == opt_layouts) { 1113 | build_layoutdemo(uictx, content); 1114 | } else if (choice == opt_row) { 1115 | build_rowdemo(uictx, content); 1116 | } else if (choice == opt_column) { 1117 | build_columndemo(uictx, content); 1118 | } else if (choice == opt_wrap) { 1119 | build_wrapdemo(uictx, content); 1120 | } 1121 | 1122 | uiEndLayout(uictx); 1123 | 1124 | drawUI(ec, 0, BND_CORNER_NONE); 1125 | 1126 | #if 0 1127 | for (int i = 0; i < uiGetLastItemCount(); ++i) { 1128 | if (uiRecoverItem(i) == -1) { 1129 | UIitem *pitem = uiLastItemPtr(i); 1130 | nvgBeginPath(vg); 1131 | nvgRect(vg,pitem->margins[0],pitem->margins[1],pitem->size[0],pitem->size[1]); 1132 | nvgStrokeWidth(vg, 2); 1133 | nvgStrokeColor(vg, nvgRGBAf(1.0f,0.0f,0.0f,0.5f)); 1134 | nvgStroke(vg); 1135 | } 1136 | } 1137 | #endif 1138 | 1139 | if (choice == opt_blendish_demo) { 1140 | UIvec2 cursor = uiGetCursor(uictx); 1141 | cursor.x -= w/2; 1142 | cursor.y -= h/2; 1143 | if (abs(cursor.x) > (w/3)) { 1144 | bndJoinAreaOverlay(vg, 0, 0, w, h, 0, (cursor.x > 0)); 1145 | } else if (abs(cursor.y) > (h/3)) { 1146 | bndJoinAreaOverlay(vg, 0, 0, w, h, 1, (cursor.y > 0)); 1147 | } 1148 | } 1149 | 1150 | uiProcess(uictx, (int)(glfwGetTime()*1000.0)); 1151 | } 1152 | 1153 | //////////////////////////////////////////////////////////////////////////////// 1154 | 1155 | void errorcb(int error, const char* desc) 1156 | { 1157 | printf("GLFW error %d: %s\n", error, desc); 1158 | } 1159 | 1160 | static void mousebutton(GLFWwindow *window, int button, int action, int mods) { 1161 | exContext *ec; 1162 | switch(button) { 1163 | case 1: button = 2; break; 1164 | case 2: button = 1; break; 1165 | } 1166 | ec = (exContext*)glfwGetWindowUserPointer(window); 1167 | uiSetButton(ec->uictx, button, mods, (action == GLFW_PRESS)); 1168 | } 1169 | 1170 | static void cursorpos(GLFWwindow *window, double x, double y) { 1171 | exContext *ec = (exContext*)glfwGetWindowUserPointer(window); 1172 | uiSetCursor(ec->uictx, (int)x,(int)y); 1173 | } 1174 | 1175 | static void scrollevent(GLFWwindow *window, double x, double y) { 1176 | exContext *ec = (exContext*)glfwGetWindowUserPointer(window); 1177 | uiSetScroll(ec->uictx, (int)x, (int)y); 1178 | } 1179 | 1180 | static void charevent(GLFWwindow *window, unsigned int value) { 1181 | exContext *ec = (exContext*)glfwGetWindowUserPointer(window); 1182 | uiSetChar(ec->uictx, value); 1183 | } 1184 | 1185 | static void key(GLFWwindow* window, int key, int scancode, int action, int mods) 1186 | { 1187 | exContext *ec; 1188 | NVG_NOTUSED(scancode); 1189 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 1190 | glfwSetWindowShouldClose(window, GL_TRUE); 1191 | ec = (exContext*)glfwGetWindowUserPointer(window); 1192 | uiSetKey(ec->uictx, key, mods, action ? true : false); 1193 | } 1194 | 1195 | int main() 1196 | { 1197 | GLFWwindow* window; 1198 | exContext ec; 1199 | memset(&ec, 0, sizeof(exContext)); 1200 | 1201 | ec.uictx = uiCreateContext(4096, 1<<20); 1202 | uiSetHandler(ec.uictx, ui_handler); 1203 | 1204 | if (!glfwInit()) { 1205 | printf("Failed to init GLFW."); 1206 | return -1; 1207 | } 1208 | 1209 | glfwSetErrorCallback(errorcb); 1210 | #ifndef _WIN32 // don't require this on win32, and works with more cards 1211 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 1212 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 1213 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 1214 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 1215 | #endif 1216 | glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1); 1217 | 1218 | window = glfwCreateWindow(650, 650, "OUI Blendish Demo", NULL, NULL); 1219 | if (!window) { 1220 | glfwTerminate(); 1221 | return -1; 1222 | } 1223 | 1224 | glfwSetWindowUserPointer(window, &ec); 1225 | glfwSetKeyCallback(window, key); 1226 | glfwSetCharCallback(window, charevent); 1227 | glfwSetCursorPosCallback(window, cursorpos); 1228 | glfwSetMouseButtonCallback(window, mousebutton); 1229 | glfwSetScrollCallback(window, scrollevent); 1230 | 1231 | glfwMakeContextCurrent(window); 1232 | #ifdef NANOVG_GLEW 1233 | glewExperimental = GL_TRUE; 1234 | if(glewInit() != GLEW_OK) { 1235 | printf("Could not init glew.\n"); 1236 | return -1; 1237 | } 1238 | // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. 1239 | glGetError(); 1240 | #endif 1241 | 1242 | //ec.vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); 1243 | ec.vg = nvgCreateGL3(NVG_ANTIALIAS); 1244 | if (ec.vg == NULL) { 1245 | printf("Could not init nanovg.\n"); 1246 | return -1; 1247 | } 1248 | 1249 | init(ec.vg); 1250 | 1251 | printf("sizeof(UIitem)=%lu\n", sizeof(UIitem)); 1252 | 1253 | glfwSwapInterval(0); 1254 | 1255 | glfwSetTime(0); 1256 | 1257 | double c = 0.0; 1258 | int total = 0; 1259 | 1260 | int peak_items = 0; 1261 | unsigned int peak_alloc = 0; 1262 | 1263 | while (!glfwWindowShouldClose(window)) 1264 | { 1265 | double mx, my; 1266 | int winWidth, winHeight; 1267 | int fbWidth, fbHeight; 1268 | float pxRatio; 1269 | 1270 | glfwGetCursorPos(window, &mx, &my); 1271 | glfwGetWindowSize(window, &winWidth, &winHeight); 1272 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 1273 | // Calculate pixel ration for hi-dpi devices. 1274 | pxRatio = (float)fbWidth / (float)winWidth; 1275 | 1276 | // Update and render 1277 | glViewport(0, 0, fbWidth, fbHeight); 1278 | glClearColor(0,0,0,1); 1279 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 1280 | 1281 | double t = glfwGetTime(); 1282 | nvgBeginFrame(ec.vg, winWidth, winHeight, pxRatio); 1283 | 1284 | draw(&ec, winWidth, winHeight); 1285 | peak_items = (peak_items > uiGetItemCount(ec.uictx))?peak_items:uiGetItemCount(ec.uictx); 1286 | peak_alloc = (peak_alloc > uiGetAllocSize(ec.uictx))?peak_alloc:uiGetAllocSize(ec.uictx); 1287 | 1288 | nvgEndFrame(ec.vg); 1289 | double t2 = glfwGetTime(); 1290 | c += (t2 - t); 1291 | total++; 1292 | if (total > (1*60)) { 1293 | printf("%fms\n", (c / (double)total)*1000.0); 1294 | total = 0; 1295 | c = 0.0; 1296 | } 1297 | 1298 | glfwSwapBuffers(window); 1299 | glfwPollEvents(); 1300 | } 1301 | printf("Peak item count: %i (%lu bytes)\nPeak allocated handles: %u bytes\n", 1302 | peak_items, peak_items * sizeof(UIitem), peak_alloc); 1303 | 1304 | uiDestroyContext(ec.uictx); 1305 | 1306 | nvgDeleteGL3(ec.vg); 1307 | 1308 | glfwTerminate(); 1309 | return 0; 1310 | } 1311 | -------------------------------------------------------------------------------- /images/blendish2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IceDragon200/oui-blendish/f8334a70393c961f23aaffff54d716e7345efa03/images/blendish2.png -------------------------------------------------------------------------------- /images/oui_frozen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IceDragon200/oui-blendish/f8334a70393c961f23aaffff54d716e7345efa03/images/oui_frozen.png -------------------------------------------------------------------------------- /images/oui_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IceDragon200/oui-blendish/f8334a70393c961f23aaffff54d716e7345efa03/images/oui_logo.png -------------------------------------------------------------------------------- /oui.h: -------------------------------------------------------------------------------- 1 | /* 2 | OUI - A minimal semi-immediate GUI handling & layouting library 3 | 4 | Copyright (c) 2014 Leonard Ritter 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | #ifndef _OUI_H_ 26 | #define _OUI_H_ 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /* 33 | Revision 4 (2014-12-17) 34 | 35 | OUI (short for "Open UI", spoken like the french "oui" for "yes") is a 36 | platform agnostic single-header C library for layouting GUI elements and 37 | handling related user input. Together with a set of widget drawing and logic 38 | routines it can be used to build complex user interfaces. 39 | 40 | OUI is a semi-immediate GUI. Widget declarations are persistent for the duration 41 | of the setup and evaluation, but do not need to be kept around longer than one 42 | frame. 43 | 44 | OUI has no widget types; instead, it provides only one kind of element, "Items", 45 | which can be tailored to the application by the user and expanded with custom 46 | buffers and event handlers to behave as containers, buttons, sliders, radio 47 | buttons, and so on. 48 | 49 | OUI also does not draw anything; Instead it provides a set of functions to 50 | iterate and query the layouted items in order to allow client code to render 51 | each widget with its current state using a preferred graphics library. 52 | 53 | See example.cpp in the repository for a full usage example. 54 | 55 | A basic setup for OUI usage in C looks like this: 56 | ================================================= 57 | 58 | // a header for each widget 59 | typedef struct Data { 60 | int type; 61 | UIhandler handler; 62 | } Data; 63 | 64 | /// global event dispatch 65 | void ui_handler(int item, UIevent event) { 66 | Data *data = (Data *)uiGetHandle(item); 67 | if (data && data->handler) { 68 | data->handler(item, event); 69 | } 70 | } 71 | 72 | void app_main(...) { 73 | UIcontext *context = uiCreateContext(4096, 1<<20); 74 | uiMakeCurrent(context); 75 | uiSetHandler(ui_handler); 76 | 77 | while (app_running()) { 78 | // update position of mouse cursor; the ui can also be updated 79 | // from received events. 80 | uiSetCursor(app_get_mouse_x(), app_get_mouse_y()); 81 | 82 | // update button state 83 | for (int i = 0; i < 3; ++i) 84 | uiSetButton(i, app_get_button_state(i)); 85 | 86 | // you can also send keys and scroll events; see example.cpp for more 87 | 88 | // -------------- 89 | // this section does not have to be regenerated on frame; a good 90 | // policy is to invalidate it on events, as this usually alters 91 | // structure and layout. 92 | 93 | // begin new UI declarations 94 | uiBeginLayout(); 95 | 96 | // - UI setup code goes here - 97 | app_setup_ui(); 98 | 99 | // layout UI 100 | uiEndLayout(); 101 | 102 | // -------------- 103 | 104 | // draw UI, starting with the first item, index 0 105 | app_draw_ui(render_context,0); 106 | 107 | // update states and fire handlers 108 | uiProcess(get_time_ms()); 109 | } 110 | 111 | uiDestroyContext(context); 112 | } 113 | 114 | Here's an example setup for a checkbox control: 115 | =============================================== 116 | 117 | typedef struct CheckBoxData { 118 | Data head; 119 | const char *label; 120 | bool *checked; 121 | } CheckBoxData; 122 | 123 | // called when the item is clicked (see checkbox()) 124 | void app_checkbox_handler(int item, UIevent event) { 125 | // retrieve custom data (see checkbox()) 126 | CheckBoxData *data = (CheckBoxData *)uiGetHandle(item); 127 | 128 | switch(event) { 129 | default: break; 130 | case UI_BUTTON0_DOWN: { 131 | // toggle value 132 | *data->checked = !(*data->checked); 133 | } break; 134 | } 135 | } 136 | 137 | // creates a checkbox control for a pointer to a boolean 138 | int checkbox(const char *label, bool *checked) { 139 | 140 | // create new ui item 141 | int item = uiItem(); 142 | 143 | // set minimum size of wiget; horizontal size is dynamic, vertical is fixed 144 | uiSetSize(item, 0, APP_WIDGET_HEIGHT); 145 | 146 | // store some custom data with the checkbox that we use for rendering 147 | // and value changes. 148 | CheckBoxData *data = (CheckBoxData *)uiAllocHandle(item, sizeof(CheckBoxData)); 149 | 150 | // assign a custom typeid to the data so the renderer knows how to 151 | // render this control, and our event handler 152 | data->head.type = APP_WIDGET_CHECKBOX; 153 | data->head.handler = app_checkbox_handler; 154 | data->label = label; 155 | data->checked = checked; 156 | 157 | // set to fire as soon as the left button is 158 | // pressed; UI_BUTTON0_HOT_UP is also a popular alternative. 159 | uiSetEvents(item, UI_BUTTON0_DOWN); 160 | 161 | return item; 162 | } 163 | 164 | A simple recursive drawing routine can look like this: 165 | ====================================================== 166 | 167 | void app_draw_ui(AppRenderContext *ctx, int item) { 168 | // retrieve custom data and cast it to Data; we assume the first member 169 | // of every widget data item to be a Data field. 170 | Data *head = (Data *)uiGetHandle(item); 171 | 172 | // if a handle is set, this is a specialized widget 173 | if (head) { 174 | // get the widgets absolute rectangle 175 | UIrect rect = uiGetRect(item); 176 | 177 | switch(head->type) { 178 | default: break; 179 | case APP_WIDGET_LABEL: { 180 | // ... 181 | } break; 182 | case APP_WIDGET_BUTTON: { 183 | // ... 184 | } break; 185 | case APP_WIDGET_CHECKBOX: { 186 | // cast to the full data type 187 | CheckBoxData *data = (CheckBoxData*)head; 188 | 189 | // get the widgets current state 190 | int state = uiGetState(item); 191 | 192 | // if the value is set, the state is always active 193 | if (*data->checked) 194 | state = UI_ACTIVE; 195 | 196 | // draw the checkbox 197 | app_draw_checkbox(ctx, rect, state, data->label); 198 | } break; 199 | } 200 | } 201 | 202 | // iterate through all children and draw 203 | int kid = uiFirstChild(item); 204 | while (kid != -1) { 205 | app_draw_ui(ctx, kid); 206 | kid = uiNextSibling(kid); 207 | } 208 | } 209 | 210 | Layouting items works like this: 211 | ================================ 212 | 213 | void layout_window(int w, int h) { 214 | // create root item; the first item always has index 0 215 | int parent = uiItem(); 216 | // assign fixed size 217 | uiSetSize(parent, w, h); 218 | 219 | // create column box and use as new parent 220 | parent = uiInsert(parent, uiItem()); 221 | // configure as column 222 | uiSetBox(parent, UI_COLUMN); 223 | // span horizontally, attach to top 224 | uiSetLayout(parent, UI_HFILL | UI_TOP); 225 | 226 | // add a label - we're assuming custom control functions to exist 227 | int item = uiInsert(parent, label("Hello World")); 228 | // set a fixed height for the label 229 | uiSetSize(item, 0, APP_WIDGET_HEIGHT); 230 | // span the label horizontally 231 | uiSetLayout(item, UI_HFILL); 232 | 233 | static bool checked = false; 234 | 235 | // add a checkbox to the same parent as item; this is faster than 236 | // calling uiInsert on the same parent repeatedly. 237 | item = uiAppend(item, checkbox("Checked:", &checked)); 238 | // set a fixed height for the checkbox 239 | uiSetSize(item, 0, APP_WIDGET_HEIGHT); 240 | // span the checkbox in the same way as the label 241 | uiSetLayout(item, UI_HFILL); 242 | } 243 | 244 | 245 | 246 | */ 247 | 248 | // you can override this from the outside to pick 249 | // the export level you need 250 | #ifndef OUI_EXPORT 251 | #define OUI_EXPORT 252 | #endif 253 | 254 | // limits 255 | 256 | enum { 257 | // maximum size in bytes of a single data buffer passed to uiAllocData(). 258 | UI_MAX_DATASIZE = 4096, 259 | // maximum depth of nested containers 260 | UI_MAX_DEPTH = 64, 261 | // maximum number of buffered input events 262 | UI_MAX_INPUT_EVENTS = 64, 263 | // consecutive click threshold in ms 264 | UI_CLICK_THRESHOLD = 250, 265 | }; 266 | 267 | typedef unsigned int UIuint; 268 | 269 | // opaque UI context 270 | typedef struct UIcontext UIcontext; 271 | 272 | // item states as returned by uiGetState() 273 | 274 | typedef enum UIitemState { 275 | // the item is inactive 276 | UI_COLD = 0, 277 | // the item is inactive, but the cursor is hovering over this item 278 | UI_HOT = 1, 279 | // the item is toggled, activated, focused (depends on item kind) 280 | UI_ACTIVE = 2, 281 | // the item is unresponsive 282 | UI_FROZEN = 3, 283 | } UIitemState; 284 | 285 | // container flags to pass to uiSetBox() 286 | typedef enum UIboxFlags { 287 | // flex-direction (bit 0+1) 288 | 289 | // left to right 290 | UI_ROW = 0x002, 291 | // top to bottom 292 | UI_COLUMN = 0x003, 293 | 294 | // model (bit 1) 295 | 296 | // free layout 297 | UI_LAYOUT = 0x000, 298 | // flex model 299 | UI_FLEX = 0x002, 300 | 301 | // flex-wrap (bit 2) 302 | 303 | // single-line 304 | UI_NOWRAP = 0x000, 305 | // multi-line, wrap left to right 306 | UI_WRAP = 0x004, 307 | 308 | 309 | // justify-content (start, end, center, space-between) 310 | // at start of row/column 311 | UI_START = 0x008, 312 | // at center of row/column 313 | UI_MIDDLE = 0x000, 314 | // at end of row/column 315 | UI_END = 0x010, 316 | // insert spacing to stretch across whole row/column 317 | UI_JUSTIFY = 0x018, 318 | 319 | // align-items 320 | // can be implemented by putting a flex container in a layout container, 321 | // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc. 322 | // FILL is equivalent to stretch/grow 323 | 324 | // align-content (start, end, center, stretch) 325 | // can be implemented by putting a flex container in a layout container, 326 | // then using UI_TOP, UI_DOWN, UI_VFILL, UI_VCENTER, etc. 327 | // FILL is equivalent to stretch; space-between is not supported. 328 | } UIboxFlags; 329 | 330 | // child layout flags to pass to uiSetLayout() 331 | typedef enum UIlayoutFlags { 332 | // attachments (bit 5-8) 333 | // fully valid when parent uses UI_LAYOUT model 334 | // partially valid when in UI_FLEX model 335 | 336 | // anchor to left item or left side of parent 337 | UI_LEFT = 0x020, 338 | // anchor to top item or top side of parent 339 | UI_TOP = 0x040, 340 | // anchor to right item or right side of parent 341 | UI_RIGHT = 0x080, 342 | // anchor to bottom item or bottom side of parent 343 | UI_DOWN = 0x100, 344 | // anchor to both left and right item or parent borders 345 | UI_HFILL = 0x0a0, 346 | // anchor to both top and bottom item or parent borders 347 | UI_VFILL = 0x140, 348 | // center horizontally, with left margin as offset 349 | UI_HCENTER = 0x000, 350 | // center vertically, with top margin as offset 351 | UI_VCENTER = 0x000, 352 | // center in both directions, with left/top margin as offset 353 | UI_CENTER = 0x000, 354 | // anchor to all four directions 355 | UI_FILL = 0x1e0, 356 | // when wrapping, put this element on a new line 357 | // wrapping layout code auto-inserts UI_BREAK flags, 358 | // drawing routines can read them with uiGetLayout() 359 | UI_BREAK = 0x200 360 | } UIlayoutFlags; 361 | 362 | // event flags 363 | typedef enum UIevent { 364 | // on button 0 down 365 | UI_BUTTON0_DOWN = 0x0400, 366 | // on button 0 up 367 | // when this event has a handler, uiGetState() will return UI_ACTIVE as 368 | // long as button 0 is down. 369 | UI_BUTTON0_UP = 0x0800, 370 | // on button 0 up while item is hovered 371 | // when this event has a handler, uiGetState() will return UI_ACTIVE 372 | // when the cursor is hovering the items rectangle; this is the 373 | // behavior expected for buttons. 374 | UI_BUTTON0_HOT_UP = 0x1000, 375 | // item is being captured (button 0 constantly pressed); 376 | // when this event has a handler, uiGetState() will return UI_ACTIVE as 377 | // long as button 0 is down. 378 | UI_BUTTON0_CAPTURE = 0x2000, 379 | // on button 2 down (right mouse button, usually triggers context menu) 380 | UI_BUTTON2_DOWN = 0x4000, 381 | // item has received a scrollwheel event 382 | // the accumulated wheel offset can be queried with uiGetScroll() 383 | UI_SCROLL = 0x8000, 384 | // item is focused and has received a key-down event 385 | // the respective key can be queried using uiGetKey() and uiGetModifier() 386 | UI_KEY_DOWN = 0x10000, 387 | // item is focused and has received a key-up event 388 | // the respective key can be queried using uiGetKey() and uiGetModifier() 389 | UI_KEY_UP = 0x20000, 390 | // item is focused and has received a character event 391 | // the respective character can be queried using uiGetKey() 392 | UI_CHAR = 0x40000, 393 | } UIevent; 394 | 395 | enum { 396 | // these bits, starting at bit 24, can be safely assigned by the 397 | // application, e.g. as item types, other event types, drop targets, etc. 398 | // they can be set and queried using uiSetFlags() and uiGetFlags() 399 | UI_USERMASK = 0xff000000, 400 | 401 | // a special mask passed to uiFindItem() 402 | UI_ANY = 0xffffffff, 403 | }; 404 | 405 | // handler callback; event is one of UI_EVENT_* 406 | typedef void (*UIhandler)(UIcontext* ui_context, int item, UIevent event); 407 | 408 | // for cursor positions, mainly 409 | typedef struct UIvec2 { 410 | union { 411 | int v[2]; 412 | struct { int x, y; }; 413 | }; 414 | } UIvec2; 415 | 416 | // layout rectangle 417 | typedef struct UIrect { 418 | union { 419 | int v[4]; 420 | struct { int x, y, w, h; }; 421 | }; 422 | } UIrect; 423 | 424 | // unless declared otherwise, all operations have the complexity O(1). 425 | 426 | // Context Management 427 | // ------------------ 428 | 429 | // create a new UI context; call uiMakeCurrent() to make this context the 430 | // current context. The context is managed by the client and must be released 431 | // using uiDestroyContext() 432 | // item_capacity is the maximum of number of items that can be declared. 433 | // buffer_capacity is the maximum total size of bytes that can be allocated 434 | // using uiAllocHandle(); you may pass 0 if you don't need to allocate 435 | // handles. 436 | // 4096 and (1<<20) are good starting values. 437 | OUI_EXPORT UIcontext *uiCreateContext( 438 | unsigned int item_capacity, 439 | unsigned int buffer_capacity); 440 | 441 | // release the memory of an UI context created with uiCreateContext(); if the 442 | // context is the current context, the current context will be set to NULL 443 | OUI_EXPORT void uiDestroyContext(UIcontext *ctx); 444 | 445 | // User Data 446 | OUI_EXPORT void uiSetContextHandle(UIcontext *ui_context, void *handle); 447 | OUI_EXPORT void *uiGetContextHandle(UIcontext *ui_context); 448 | 449 | // Input Control 450 | // ------------- 451 | 452 | // sets the current cursor position (usually belonging to a mouse) to the 453 | // screen coordinates at (x,y) 454 | OUI_EXPORT void uiSetCursor(UIcontext *ui_context, int x, int y); 455 | 456 | // returns the current cursor position in screen coordinates as set by 457 | // uiSetCursor() 458 | OUI_EXPORT UIvec2 uiGetCursor(UIcontext *ui_context); 459 | 460 | // returns the offset of the cursor relative to the last call to uiProcess() 461 | OUI_EXPORT UIvec2 uiGetCursorDelta(UIcontext *ui_context); 462 | 463 | // returns the beginning point of a drag operation. 464 | OUI_EXPORT UIvec2 uiGetCursorStart(UIcontext *ui_context); 465 | 466 | // returns the offset of the cursor relative to the beginning point of a drag 467 | // operation. 468 | OUI_EXPORT UIvec2 uiGetCursorStartDelta(UIcontext *ui_context); 469 | 470 | // sets a mouse or gamepad button as pressed/released 471 | // button is in the range 0..63 and maps to an application defined input 472 | // source. 473 | // mod is an application defined set of flags for modifier keys 474 | // enabled is 1 for pressed, 0 for released 475 | OUI_EXPORT void uiSetButton(UIcontext *ui_context, unsigned int button, unsigned int mod, bool enabled); 476 | 477 | // returns the current state of an application dependent input button 478 | // as set by uiSetButton(). 479 | // the function returns 1 if the button has been set to pressed, 0 for released. 480 | OUI_EXPORT int uiGetButton(UIcontext *ui_context, unsigned int button); 481 | 482 | // returns the number of chained clicks; 1 is a single click, 483 | // 2 is a double click, etc. 484 | OUI_EXPORT int uiGetClicks(UIcontext *ui_context); 485 | 486 | // sets a key as down/up; the key can be any application defined keycode 487 | // mod is an application defined set of flags for modifier keys 488 | // enabled is 1 for key down, 0 for key up 489 | // all key events are being buffered until the next call to uiProcess() 490 | OUI_EXPORT void uiSetKey(UIcontext *ui_context, unsigned int key, unsigned int mod, bool enabled); 491 | 492 | // sends a single character for text input; the character is usually in the 493 | // unicode range, but can be application defined. 494 | // all char events are being buffered until the next call to uiProcess() 495 | OUI_EXPORT void uiSetChar(UIcontext *ui_context, unsigned int value); 496 | 497 | // accumulates scroll wheel offsets for the current frame 498 | // all offsets are being accumulated until the next call to uiProcess() 499 | OUI_EXPORT void uiSetScroll(UIcontext *ui_context, int x, int y); 500 | 501 | // returns the currently accumulated scroll wheel offsets for this frame 502 | OUI_EXPORT UIvec2 uiGetScroll(UIcontext *ui_context); 503 | 504 | 505 | 506 | 507 | 508 | // Stages 509 | // ------ 510 | 511 | // clear the item buffer; uiBeginLayout() should be called before the first 512 | // UI declaration for this frame to avoid concatenation of the same UI multiple 513 | // times. 514 | // After the call, all previously declared item IDs are invalid, and all 515 | // application dependent context data has been freed. 516 | // uiBeginLayout() must be followed by uiEndLayout(). 517 | OUI_EXPORT void uiBeginLayout(UIcontext *ui_context); 518 | 519 | // layout all added items starting from the root item 0. 520 | // after calling uiEndLayout(), no further modifications to the item tree should 521 | // be done until the next call to uiBeginLayout(). 522 | // It is safe to immediately draw the items after a call to uiEndLayout(). 523 | // this is an O(N) operation for N = number of declared items. 524 | OUI_EXPORT void uiEndLayout(UIcontext *ui_context); 525 | 526 | // update the current hot item; this only needs to be called if items are kept 527 | // for more than one frame and uiEndLayout() is not called 528 | OUI_EXPORT void uiUpdateHotItem(UIcontext *ui_context); 529 | 530 | // update the internal state according to the current cursor position and 531 | // button states, and call all registered handlers. 532 | // timestamp is the time in milliseconds relative to the last call to uiProcess() 533 | // and is used to estimate the threshold for double-clicks 534 | // after calling uiProcess(), no further modifications to the item tree should 535 | // be done until the next call to uiBeginLayout(). 536 | // Items should be drawn before a call to uiProcess() 537 | // this is an O(N) operation for N = number of declared items. 538 | OUI_EXPORT void uiProcess(UIcontext *ui_context, int timestamp); 539 | 540 | // reset the currently stored hot/active etc. handles; this should be called when 541 | // a re-declaration of the UI changes the item indices, to avoid state 542 | // related glitches because item identities have changed. 543 | OUI_EXPORT void uiClearState(UIcontext *ui_context); 544 | 545 | // UI Declaration 546 | // -------------- 547 | 548 | // create a new UI item and return the new items ID. 549 | OUI_EXPORT int uiItem(UIcontext *ui_context); 550 | 551 | // set an items state to frozen; the UI will not recurse into frozen items 552 | // when searching for hot or active items; subsequently, frozen items and 553 | // their child items will not cause mouse event notifications. 554 | // The frozen state is not applied recursively; uiGetState() will report 555 | // UI_COLD for child items. Upon encountering a frozen item, the drawing 556 | // routine needs to handle rendering of child items appropriately. 557 | // see example.cpp for a demonstration. 558 | OUI_EXPORT void uiSetFrozen(UIcontext *ui_context, int item, bool enable); 559 | 560 | // set the application-dependent handle of an item. 561 | // handle is an application defined 64-bit handle. If handle is NULL, the item 562 | // will not be interactive. 563 | OUI_EXPORT void uiSetHandle(UIcontext *ui_context, int item, void *handle); 564 | 565 | // allocate space for application-dependent context data and assign it 566 | // as the handle to the item. 567 | // The memory of the pointer is managed by the UI context and released 568 | // upon the next call to uiBeginLayout() 569 | OUI_EXPORT void *uiAllocHandle(UIcontext *ui_context, int item, unsigned int size); 570 | 571 | // set the global handler callback for interactive items. 572 | // the handler will be called for each item whose event flags are set using 573 | // uiSetEvents. 574 | OUI_EXPORT void uiSetHandler(UIcontext *ui_context, UIhandler handler); 575 | 576 | // flags is a combination of UI_EVENT_* and designates for which events the 577 | // handler should be called. 578 | OUI_EXPORT void uiSetEvents(UIcontext *ui_context, int item, unsigned int flags); 579 | 580 | // flags is a user-defined set of flags defined by UI_USERMASK. 581 | OUI_EXPORT void uiSetFlags(UIcontext *ui_context, int item, unsigned int flags); 582 | 583 | // assign an item to a container. 584 | // an item ID of 0 refers to the root item. 585 | // the function returns the child item ID 586 | // if the container has already added items, the function searches 587 | // for the last item and calls uiAppend() on it, which is an 588 | // O(N) operation for N siblings. 589 | // it is usually more efficient to call uiInsert() for the first child, 590 | // then chain additional siblings using uiAppend(). 591 | OUI_EXPORT int uiInsert(UIcontext *ui_context, int item, int child); 592 | 593 | // assign an item to the same container as another item 594 | // sibling is inserted after item. 595 | OUI_EXPORT int uiAppend(UIcontext *ui_context, int item, int sibling); 596 | 597 | // insert child into container item like uiInsert(), but prepend 598 | // it to the first child item, effectively putting it in 599 | // the background. 600 | // it is efficient to call uiInsertBack() repeatedly 601 | // in cases where drawing or layout order doesn't matter. 602 | OUI_EXPORT int uiInsertBack(UIcontext *ui_context, int item, int child); 603 | 604 | // same as uiInsert() 605 | OUI_EXPORT int uiInsertFront(UIcontext *ui_context, int item, int child); 606 | 607 | // set the size of the item; a size of 0 indicates the dimension to be 608 | // dynamic; if the size is set, the item can not expand beyond that size. 609 | OUI_EXPORT void uiSetSize(UIcontext *ui_context, int item, int w, int h); 610 | 611 | // set the anchoring behavior of the item to one or multiple UIlayoutFlags 612 | OUI_EXPORT void uiSetLayout(UIcontext *ui_context, int item, unsigned int flags); 613 | 614 | // set the box model behavior of the item to one or multiple UIboxFlags 615 | OUI_EXPORT void uiSetBox(UIcontext *ui_context, int item, unsigned int flags); 616 | 617 | // set the left, top, right and bottom margins of an item; when the item is 618 | // anchored to the parent or another item, the margin controls the distance 619 | // from the neighboring element. 620 | OUI_EXPORT void uiSetMargins(UIcontext *ui_context, int item, short l, short t, short r, short b); 621 | 622 | // set item as recipient of all keyboard events; if item is -1, no item will 623 | // be focused. 624 | OUI_EXPORT void uiFocus(UIcontext *ui_context, int item); 625 | 626 | // Iteration 627 | // --------- 628 | 629 | // returns the first child item of a container item. If the item is not 630 | // a container or does not contain any items, -1 is returned. 631 | // if item is 0, the first child item of the root item will be returned. 632 | OUI_EXPORT int uiFirstChild(UIcontext *ui_context, int item); 633 | 634 | // returns an items next sibling in the list of the parent containers children. 635 | // if item is 0 or the item is the last child item, -1 will be returned. 636 | OUI_EXPORT int uiNextSibling(UIcontext *ui_context, int item); 637 | 638 | // Querying 639 | // -------- 640 | 641 | // return the total number of allocated items 642 | OUI_EXPORT int uiGetItemCount(UIcontext *ui_context); 643 | 644 | // return the total bytes that have been allocated by uiAllocHandle() 645 | OUI_EXPORT unsigned int uiGetAllocSize(UIcontext *ui_context); 646 | 647 | // return the current state of the item. This state is only valid after 648 | // a call to uiProcess(). 649 | // The returned value is one of UI_COLD, UI_HOT, UI_ACTIVE, UI_FROZEN. 650 | OUI_EXPORT UIitemState uiGetState(UIcontext *ui_context, int item); 651 | 652 | // return the application-dependent handle of the item as passed to uiSetHandle() 653 | // or uiAllocHandle(). 654 | OUI_EXPORT void *uiGetHandle(UIcontext *ui_context, int item); 655 | 656 | // return the item that is currently under the cursor or -1 for none 657 | OUI_EXPORT int uiGetHotItem(UIcontext *ui_context); 658 | 659 | // return the item that is currently focused or -1 for none 660 | OUI_EXPORT int uiGetFocusedItem(UIcontext *ui_context); 661 | 662 | // returns the topmost item containing absolute location (x,y), starting with 663 | // item as parent, using a set of flags and masks as filter: 664 | // if both flags and mask are UI_ANY, the first topmost item is returned. 665 | // if mask is UI_ANY, the first topmost item matching *any* of flags is returned. 666 | // otherwise the first item matching (item.flags & flags) == mask is returned. 667 | // you may combine box, layout, event and user flags. 668 | // frozen items will always be ignored. 669 | OUI_EXPORT int uiFindItem(UIcontext *ui_context, int item, int x, int y, 670 | unsigned int flags, unsigned int mask); 671 | 672 | // return the handler callback as passed to uiSetHandler() 673 | OUI_EXPORT UIhandler uiGetHandler(UIcontext *ui_context); 674 | // return the event flags for an item as passed to uiSetEvents() 675 | OUI_EXPORT unsigned int uiGetEvents(UIcontext *ui_context, int item); 676 | // return the user-defined flags for an item as passed to uiSetFlags() 677 | OUI_EXPORT unsigned int uiGetFlags(UIcontext *ui_context, int item); 678 | 679 | // when handling a KEY_DOWN/KEY_UP event: the key that triggered this event 680 | OUI_EXPORT unsigned int uiGetKey(UIcontext *ui_context); 681 | // when handling a keyboard or mouse event: the active modifier keys 682 | OUI_EXPORT unsigned int uiGetModifier(UIcontext *ui_context); 683 | 684 | // returns the items layout rectangle in absolute coordinates. If 685 | // uiGetRect() is called before uiEndLayout(), the values of the returned 686 | // rectangle are undefined. 687 | OUI_EXPORT UIrect uiGetRect(UIcontext *ui_context, int item); 688 | 689 | // returns 1 if an items absolute rectangle contains a given coordinate 690 | // otherwise 0 691 | OUI_EXPORT int uiContains(UIcontext *ui_context, int item, int x, int y); 692 | 693 | // return the width of the item as set by uiSetSize() 694 | OUI_EXPORT int uiGetWidth(UIcontext *ui_context, int item); 695 | // return the height of the item as set by uiSetSize() 696 | OUI_EXPORT int uiGetHeight(UIcontext *ui_context, int item); 697 | 698 | // return the anchoring behavior as set by uiSetLayout() 699 | OUI_EXPORT unsigned int uiGetLayout(UIcontext *ui_context, int item); 700 | // return the box model as set by uiSetBox() 701 | OUI_EXPORT unsigned int uiGetBox(UIcontext *ui_context, int item); 702 | 703 | // return the left margin of the item as set with uiSetMargins() 704 | OUI_EXPORT short uiGetMarginLeft(UIcontext *ui_context, int item); 705 | // return the top margin of the item as set with uiSetMargins() 706 | OUI_EXPORT short uiGetMarginTop(UIcontext *ui_context, int item); 707 | // return the right margin of the item as set with uiSetMargins() 708 | OUI_EXPORT short uiGetMarginRight(UIcontext *ui_context, int item); 709 | // return the bottom margin of the item as set with uiSetMargins() 710 | OUI_EXPORT short uiGetMarginDown(UIcontext *ui_context, int item); 711 | 712 | // when uiBeginLayout() is called, the most recently declared items are retained. 713 | // when uiEndLayout() completes, it matches the old item hierarchy to the new one 714 | // and attempts to map old items to new items as well as possible. 715 | // when passed an item Id from the previous frame, uiRecoverItem() returns the 716 | // items new assumed Id, or -1 if the item could not be mapped. 717 | // it is valid to pass -1 as item. 718 | OUI_EXPORT int uiRecoverItem(UIcontext *ui_context, int olditem); 719 | 720 | // in cases where it is important to recover old state over changes in 721 | // the view, and the built-in remapping fails, the UI declaration can manually 722 | // remap old items to new IDs in cases where e.g. the previous item ID has been 723 | // temporarily saved; uiRemapItem() would then be called after creating the 724 | // new item using uiItem(). 725 | OUI_EXPORT void uiRemapItem(UIcontext *ui_context, int olditem, int newitem); 726 | 727 | // returns the number if items that have been allocated in the last frame 728 | OUI_EXPORT int uiGetLastItemCount(UIcontext *ui_context); 729 | 730 | #ifdef __cplusplus 731 | }; 732 | #endif 733 | 734 | #endif // _OUI_H_ 735 | 736 | #ifdef OUI_IMPLEMENTATION 737 | 738 | #include 739 | 740 | #ifdef _MSC_VER 741 | #pragma warning (disable: 4996) // Switch off security warnings 742 | #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings 743 | #pragma warning (disable: 4244) 744 | #pragma warning (disable: 4305) 745 | 746 | #ifdef __cplusplus 747 | #define UI_INLINE inline 748 | #else 749 | #define UI_INLINE 750 | #endif 751 | #else 752 | #ifdef __clang__ 753 | #define UI_INLINE static inline 754 | #else 755 | #define UI_INLINE inline 756 | #endif 757 | #endif 758 | 759 | #define UI_MAX_KIND 16 760 | 761 | #define UI_ANY_BUTTON0_INPUT (UI_BUTTON0_DOWN \ 762 | |UI_BUTTON0_UP \ 763 | |UI_BUTTON0_HOT_UP \ 764 | |UI_BUTTON0_CAPTURE) 765 | 766 | #define UI_ANY_BUTTON2_INPUT (UI_BUTTON2_DOWN) 767 | 768 | #define UI_ANY_MOUSE_INPUT (UI_ANY_BUTTON0_INPUT \ 769 | |UI_ANY_BUTTON2_INPUT) 770 | 771 | #define UI_ANY_KEY_INPUT (UI_KEY_DOWN \ 772 | |UI_KEY_UP \ 773 | |UI_CHAR) 774 | 775 | #define UI_ANY_INPUT (UI_ANY_MOUSE_INPUT \ 776 | |UI_ANY_KEY_INPUT) 777 | 778 | enum { 779 | // extra item flags 780 | 781 | // bit 0-2 782 | UI_ITEM_BOX_MODEL_MASK = 0x000007, 783 | // bit 0-4 784 | UI_ITEM_BOX_MASK = 0x00001F, 785 | // bit 5-8 786 | UI_ITEM_LAYOUT_MASK = 0x0003E0, 787 | // bit 9-18 788 | UI_ITEM_EVENT_MASK = 0x07FC00, 789 | // item is frozen (bit 19) 790 | UI_ITEM_FROZEN = 0x080000, 791 | // item handle is pointer to data (bit 20) 792 | UI_ITEM_DATA = 0x100000, 793 | // item has been inserted (bit 21) 794 | UI_ITEM_INSERTED = 0x200000, 795 | // horizontal size has been explicitly set (bit 22) 796 | UI_ITEM_HFIXED = 0x400000, 797 | // vertical size has been explicitly set (bit 23) 798 | UI_ITEM_VFIXED = 0x800000, 799 | // bit 22-23 800 | UI_ITEM_FIXED_MASK = 0xC00000, 801 | 802 | // which flag bits will be compared 803 | UI_ITEM_COMPARE_MASK = UI_ITEM_BOX_MODEL_MASK 804 | | (UI_ITEM_LAYOUT_MASK & ~UI_BREAK) 805 | | UI_ITEM_EVENT_MASK 806 | | UI_USERMASK, 807 | }; 808 | 809 | typedef struct UIitem { 810 | // data handle 811 | void *handle; 812 | 813 | // about 27 bits worth of flags 814 | unsigned int flags; 815 | 816 | // index of first kid 817 | // if old item: index of equivalent new item 818 | int firstkid; 819 | // index of next sibling with same parent 820 | int nextitem; 821 | 822 | // margin offsets, interpretation depends on flags 823 | // after layouting, the first two components are absolute coordinates 824 | short margins[4]; 825 | // size 826 | short size[2]; 827 | } UIitem; 828 | 829 | typedef enum UIstate { 830 | UI_STATE_IDLE = 0, 831 | UI_STATE_CAPTURE, 832 | } UIstate; 833 | 834 | typedef enum UIstage { 835 | UI_STAGE_LAYOUT = 0, 836 | UI_STAGE_POST_LAYOUT, 837 | UI_STAGE_PROCESS, 838 | } UIstage; 839 | 840 | typedef struct UIhandleEntry { 841 | unsigned int key; 842 | int item; 843 | } UIhandleEntry; 844 | 845 | typedef struct UIinputEvent { 846 | unsigned int key; 847 | unsigned int mod; 848 | UIevent event; 849 | } UIinputEvent; 850 | 851 | struct UIcontext { 852 | unsigned int item_capacity; 853 | unsigned int buffer_capacity; 854 | 855 | // handler 856 | UIhandler handler; 857 | // User data 858 | void *handle; 859 | 860 | // button state in this frame 861 | unsigned long long buttons; 862 | // button state in the previous frame 863 | unsigned long long last_buttons; 864 | 865 | // where the cursor was at the beginning of the active state 866 | UIvec2 start_cursor; 867 | // where the cursor was last frame 868 | UIvec2 last_cursor; 869 | // where the cursor is currently 870 | UIvec2 cursor; 871 | // accumulated scroll wheel offsets 872 | UIvec2 scroll; 873 | 874 | int active_item; 875 | int focus_item; 876 | int last_hot_item; 877 | int last_click_item; 878 | int hot_item; 879 | 880 | UIstate state; 881 | UIstage stage; 882 | unsigned int active_key; 883 | unsigned int active_modifier; 884 | unsigned int active_button_modifier; 885 | int last_timestamp; 886 | int last_click_timestamp; 887 | int clicks; 888 | 889 | int count; 890 | int last_count; 891 | int eventcount; 892 | unsigned int datasize; 893 | 894 | UIitem *items; 895 | unsigned char *data; 896 | UIitem *last_items; 897 | int *item_map; 898 | UIinputEvent events[UI_MAX_INPUT_EVENTS]; 899 | }; 900 | 901 | UI_INLINE int ui_max(int a, int b) { 902 | return (a>b)?a:b; 903 | } 904 | 905 | UI_INLINE int ui_min(int a, int b) { 906 | return (ab)?a:b; 911 | } 912 | 913 | UI_INLINE float ui_minf(float a, float b) { 914 | return (alast_count = ui_context->count; 920 | ui_context->count = 0; 921 | ui_context->datasize = 0; 922 | ui_context->hot_item = -1; 923 | // swap buffers 924 | UIitem *items = ui_context->items; 925 | ui_context->items = ui_context->last_items; 926 | ui_context->last_items = items; 927 | for (i = 0; i < ui_context->last_count; ++i) { 928 | ui_context->item_map[i] = -1; 929 | } 930 | } 931 | 932 | static UIcontext *uiInitializeContext( 933 | UIcontext *ctx, 934 | unsigned int item_capacity, 935 | unsigned int buffer_capacity) { 936 | memset(ctx, 0, sizeof(UIcontext)); 937 | ctx->item_capacity = item_capacity; 938 | ctx->buffer_capacity = buffer_capacity; 939 | ctx->stage = UI_STAGE_PROCESS; 940 | ctx->items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); 941 | ctx->last_items = (UIitem *)malloc(sizeof(UIitem) * item_capacity); 942 | ctx->item_map = (int *)malloc(sizeof(int) * item_capacity); 943 | if (buffer_capacity) { 944 | ctx->data = (unsigned char *)malloc(buffer_capacity); 945 | } 946 | return ctx; 947 | } 948 | 949 | UIcontext *uiCreateContext( 950 | unsigned int item_capacity, 951 | unsigned int buffer_capacity) { 952 | assert(item_capacity); 953 | UIcontext *ctx = (UIcontext *)malloc(sizeof(UIcontext)); 954 | uiInitializeContext(ctx, item_capacity, buffer_capacity); 955 | uiClear(ctx); 956 | uiClearState(ctx); 957 | return ctx; 958 | } 959 | 960 | void uiDestroyContext(UIcontext *ctx) { 961 | free(ctx->items); 962 | free(ctx->last_items); 963 | free(ctx->item_map); 964 | free(ctx->data); 965 | free(ctx); 966 | } 967 | 968 | void uiSetContextHandle(UIcontext *ui_context, void *handle) { 969 | assert(ui_context); 970 | ui_context->handle = handle; 971 | } 972 | 973 | void *uiGetContextHandle(UIcontext *ui_context) { 974 | assert(ui_context); 975 | return ui_context->handle; 976 | } 977 | 978 | 979 | void uiSetButton(UIcontext *ui_context, unsigned int button, unsigned int mod, bool enabled) { 980 | assert(ui_context); 981 | unsigned long long mask = 1ull<buttons = (enabled)? 984 | (ui_context->buttons | mask): 985 | (ui_context->buttons & ~mask); 986 | ui_context->active_button_modifier = mod; 987 | } 988 | 989 | static void uiAddInputEvent(UIcontext *ui_context, UIinputEvent event) { 990 | assert(ui_context); 991 | if (ui_context->eventcount == UI_MAX_INPUT_EVENTS) return; 992 | ui_context->events[ui_context->eventcount++] = event; 993 | } 994 | 995 | static void uiClearInputEvents(UIcontext *ui_context) { 996 | assert(ui_context); 997 | ui_context->eventcount = 0; 998 | ui_context->scroll.x = 0; 999 | ui_context->scroll.y = 0; 1000 | } 1001 | 1002 | void uiSetKey(UIcontext *ui_context, unsigned int key, unsigned int mod, bool enabled) { 1003 | assert(ui_context); 1004 | UIinputEvent event = { key, mod, enabled?UI_KEY_DOWN:UI_KEY_UP }; 1005 | uiAddInputEvent(ui_context, event); 1006 | } 1007 | 1008 | void uiSetChar(UIcontext *ui_context, unsigned int value) { 1009 | assert(ui_context); 1010 | UIinputEvent event = { value, 0, UI_CHAR }; 1011 | uiAddInputEvent(ui_context, event); 1012 | } 1013 | 1014 | void uiSetScroll(UIcontext *ui_context, int x, int y) { 1015 | assert(ui_context); 1016 | ui_context->scroll.x += x; 1017 | ui_context->scroll.y += y; 1018 | } 1019 | 1020 | UIvec2 uiGetScroll(UIcontext *ui_context) { 1021 | assert(ui_context); 1022 | return ui_context->scroll; 1023 | } 1024 | 1025 | int uiGetLastButton(UIcontext *ui_context, unsigned int button) { 1026 | assert(ui_context); 1027 | return (ui_context->last_buttons & (1ull<buttons & (1ull<cursor.x = x; 1048 | ui_context->cursor.y = y; 1049 | } 1050 | 1051 | UIvec2 uiGetCursor(UIcontext *ui_context) { 1052 | assert(ui_context); 1053 | return ui_context->cursor; 1054 | } 1055 | 1056 | UIvec2 uiGetCursorStart(UIcontext *ui_context) { 1057 | assert(ui_context); 1058 | return ui_context->start_cursor; 1059 | } 1060 | 1061 | UIvec2 uiGetCursorDelta(UIcontext *ui_context) { 1062 | assert(ui_context); 1063 | UIvec2 result = {{{ 1064 | ui_context->cursor.x - ui_context->last_cursor.x, 1065 | ui_context->cursor.y - ui_context->last_cursor.y 1066 | }}}; 1067 | return result; 1068 | } 1069 | 1070 | UIvec2 uiGetCursorStartDelta(UIcontext *ui_context) { 1071 | assert(ui_context); 1072 | UIvec2 result = {{{ 1073 | ui_context->cursor.x - ui_context->start_cursor.x, 1074 | ui_context->cursor.y - ui_context->start_cursor.y 1075 | }}}; 1076 | return result; 1077 | } 1078 | 1079 | unsigned int uiGetKey(UIcontext *ui_context) { 1080 | assert(ui_context); 1081 | return ui_context->active_key; 1082 | } 1083 | 1084 | unsigned int uiGetModifier(UIcontext *ui_context) { 1085 | assert(ui_context); 1086 | return ui_context->active_modifier; 1087 | } 1088 | 1089 | int uiGetItemCount(UIcontext *ui_context) { 1090 | assert(ui_context); 1091 | return ui_context->count; 1092 | } 1093 | 1094 | int uiGetLastItemCount(UIcontext *ui_context) { 1095 | assert(ui_context); 1096 | return ui_context->last_count; 1097 | } 1098 | 1099 | unsigned int uiGetAllocSize(UIcontext *ui_context) { 1100 | assert(ui_context); 1101 | return ui_context->datasize; 1102 | } 1103 | 1104 | UIitem *uiItemPtr(UIcontext *ui_context, int item) { 1105 | assert(ui_context && (item >= 0) && (item < ui_context->count)); 1106 | return ui_context->items + item; 1107 | } 1108 | 1109 | UIitem *uiLastItemPtr(UIcontext *ui_context, int item) { 1110 | assert(ui_context && (item >= 0) && (item < ui_context->last_count)); 1111 | return ui_context->last_items + item; 1112 | } 1113 | 1114 | int uiGetHotItem(UIcontext *ui_context) { 1115 | assert(ui_context); 1116 | return ui_context->hot_item; 1117 | } 1118 | 1119 | void uiFocus(UIcontext *ui_context, int item) { 1120 | assert(ui_context && (item >= -1) && (item < ui_context->count)); 1121 | assert(ui_context->stage != UI_STAGE_LAYOUT); 1122 | ui_context->focus_item = item; 1123 | } 1124 | 1125 | static void uiValidateStateItems(UIcontext *ui_context) { 1126 | assert(ui_context); 1127 | ui_context->last_hot_item = uiRecoverItem(ui_context, ui_context->last_hot_item); 1128 | ui_context->active_item = uiRecoverItem(ui_context, ui_context->active_item); 1129 | ui_context->focus_item = uiRecoverItem(ui_context, ui_context->focus_item); 1130 | ui_context->last_click_item = uiRecoverItem(ui_context, ui_context->last_click_item); 1131 | } 1132 | 1133 | int uiGetFocusedItem(UIcontext *ui_context) { 1134 | assert(ui_context); 1135 | return ui_context->focus_item; 1136 | } 1137 | 1138 | 1139 | void uiBeginLayout(UIcontext *ui_context) { 1140 | assert(ui_context); 1141 | assert(ui_context->stage == UI_STAGE_PROCESS); // must run uiEndLayout(), uiProcess() first 1142 | uiClear(ui_context); 1143 | ui_context->stage = UI_STAGE_LAYOUT; 1144 | } 1145 | 1146 | void uiClearState(UIcontext *ui_context) { 1147 | assert(ui_context); 1148 | ui_context->last_hot_item = -1; 1149 | ui_context->active_item = -1; 1150 | ui_context->focus_item = -1; 1151 | ui_context->last_click_item = -1; 1152 | } 1153 | 1154 | int uiItem(UIcontext *ui_context) { 1155 | assert(ui_context); 1156 | assert(ui_context->stage == UI_STAGE_LAYOUT); // must run between uiBeginLayout() and uiEndLayout() 1157 | assert(ui_context->count < (int)ui_context->item_capacity); 1158 | int idx = ui_context->count++; 1159 | UIitem *item = uiItemPtr(ui_context, idx); 1160 | memset(item, 0, sizeof(UIitem)); 1161 | item->firstkid = -1; 1162 | item->nextitem = -1; 1163 | return idx; 1164 | } 1165 | 1166 | void uiNotifyItem(UIcontext *ui_context, int item, UIevent event) { 1167 | assert(ui_context); 1168 | if (!ui_context->handler) 1169 | return; 1170 | assert((event & UI_ITEM_EVENT_MASK) == event); 1171 | UIitem *pitem = uiItemPtr(ui_context, item); 1172 | if (pitem->flags & event) { 1173 | ui_context->handler(ui_context, item, event); 1174 | } 1175 | } 1176 | 1177 | UI_INLINE int uiLastChild(UIcontext *ui_context, int item) { 1178 | item = uiFirstChild(ui_context, item); 1179 | if (item < 0) 1180 | return -1; 1181 | while (true) { 1182 | int nextitem = uiNextSibling(ui_context, item); 1183 | if (nextitem < 0) 1184 | return item; 1185 | item = nextitem; 1186 | } 1187 | } 1188 | 1189 | int uiAppend(UIcontext *ui_context, int item, int sibling) { 1190 | assert(sibling > 0); 1191 | UIitem *pitem = uiItemPtr(ui_context, item); 1192 | UIitem *psibling = uiItemPtr(ui_context, sibling); 1193 | assert(!(psibling->flags & UI_ITEM_INSERTED)); 1194 | psibling->nextitem = pitem->nextitem; 1195 | psibling->flags |= UI_ITEM_INSERTED; 1196 | pitem->nextitem = sibling; 1197 | return sibling; 1198 | } 1199 | 1200 | int uiInsert(UIcontext *ui_context, int item, int child) { 1201 | assert(child > 0); 1202 | UIitem *pparent = uiItemPtr(ui_context, item); 1203 | UIitem *pchild = uiItemPtr(ui_context, child); 1204 | assert(!(pchild->flags & UI_ITEM_INSERTED)); 1205 | if (pparent->firstkid < 0) { 1206 | pparent->firstkid = child; 1207 | pchild->flags |= UI_ITEM_INSERTED; 1208 | } else { 1209 | uiAppend(ui_context, uiLastChild(ui_context, item), child); 1210 | } 1211 | return child; 1212 | } 1213 | 1214 | int uiInsertFront(UIcontext *ui_context, int item, int child) { 1215 | return uiInsert(ui_context, item, child); 1216 | } 1217 | 1218 | int uiInsertBack(UIcontext *ui_context, int item, int child) { 1219 | assert(child > 0); 1220 | UIitem *pparent = uiItemPtr(ui_context, item); 1221 | UIitem *pchild = uiItemPtr(ui_context, child); 1222 | assert(!(pchild->flags & UI_ITEM_INSERTED)); 1223 | pchild->nextitem = pparent->firstkid; 1224 | pparent->firstkid = child; 1225 | pchild->flags |= UI_ITEM_INSERTED; 1226 | return child; 1227 | } 1228 | 1229 | void uiSetFrozen(UIcontext *ui_context, int item, bool enable) { 1230 | UIitem *pitem = uiItemPtr(ui_context, item); 1231 | if (enable) 1232 | pitem->flags |= UI_ITEM_FROZEN; 1233 | else 1234 | pitem->flags &= ~UI_ITEM_FROZEN; 1235 | } 1236 | 1237 | void uiSetSize(UIcontext *ui_context, int item, int w, int h) { 1238 | UIitem *pitem = uiItemPtr(ui_context, item); 1239 | pitem->size[0] = w; 1240 | pitem->size[1] = h; 1241 | if (!w) 1242 | pitem->flags &= ~UI_ITEM_HFIXED; 1243 | else 1244 | pitem->flags |= UI_ITEM_HFIXED; 1245 | if (!h) 1246 | pitem->flags &= ~UI_ITEM_VFIXED; 1247 | else 1248 | pitem->flags |= UI_ITEM_VFIXED; 1249 | } 1250 | 1251 | int uiGetWidth(UIcontext *ui_context, int item) { 1252 | return uiItemPtr(ui_context, item)->size[0]; 1253 | } 1254 | 1255 | int uiGetHeight(UIcontext *ui_context, int item) { 1256 | return uiItemPtr(ui_context, item)->size[1]; 1257 | } 1258 | 1259 | void uiSetLayout(UIcontext *ui_context, int item, unsigned int flags) { 1260 | UIitem *pitem = uiItemPtr(ui_context, item); 1261 | assert((flags & UI_ITEM_LAYOUT_MASK) == (unsigned int)flags); 1262 | pitem->flags &= ~UI_ITEM_LAYOUT_MASK; 1263 | pitem->flags |= flags & UI_ITEM_LAYOUT_MASK; 1264 | } 1265 | 1266 | unsigned int uiGetLayout(UIcontext *ui_context, int item) { 1267 | return uiItemPtr(ui_context, item)->flags & UI_ITEM_LAYOUT_MASK; 1268 | } 1269 | 1270 | void uiSetBox(UIcontext *ui_context, int item, unsigned int flags) { 1271 | UIitem *pitem = uiItemPtr(ui_context, item); 1272 | assert((flags & UI_ITEM_BOX_MASK) == (unsigned int)flags); 1273 | pitem->flags &= ~UI_ITEM_BOX_MASK; 1274 | pitem->flags |= flags & UI_ITEM_BOX_MASK; 1275 | } 1276 | 1277 | unsigned int uiGetBox(UIcontext *ui_context, int item) { 1278 | return uiItemPtr(ui_context, item)->flags & UI_ITEM_BOX_MASK; 1279 | } 1280 | 1281 | void uiSetMargins(UIcontext *ui_context, int item, short l, short t, short r, short b) { 1282 | UIitem *pitem = uiItemPtr(ui_context, item); 1283 | pitem->margins[0] = l; 1284 | pitem->margins[1] = t; 1285 | pitem->margins[2] = r; 1286 | pitem->margins[3] = b; 1287 | } 1288 | 1289 | short uiGetMarginLeft(UIcontext *ui_context, int item) { 1290 | return uiItemPtr(ui_context, item)->margins[0]; 1291 | } 1292 | short uiGetMarginTop(UIcontext *ui_context, int item) { 1293 | return uiItemPtr(ui_context, item)->margins[1]; 1294 | } 1295 | short uiGetMarginRight(UIcontext *ui_context, int item) { 1296 | return uiItemPtr(ui_context, item)->margins[2]; 1297 | } 1298 | short uiGetMarginDown(UIcontext *ui_context, int item) { 1299 | return uiItemPtr(ui_context, item)->margins[3]; 1300 | } 1301 | 1302 | // compute bounding box of all items super-imposed 1303 | UI_INLINE void uiComputeImposedSize(UIcontext *ui_context, UIitem *pitem, int dim) { 1304 | int wdim = dim+2; 1305 | // largest size is required size 1306 | short need_size = 0; 1307 | int kid = pitem->firstkid; 1308 | while (kid >= 0) { 1309 | UIitem *pkid = uiItemPtr(ui_context, kid); 1310 | 1311 | // width = start margin + calculated width + end margin 1312 | int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1313 | need_size = ui_max(need_size, kidsize); 1314 | kid = uiNextSibling(ui_context, kid); 1315 | } 1316 | pitem->size[dim] = need_size; 1317 | } 1318 | 1319 | // compute bounding box of all items stacked 1320 | UI_INLINE void uiComputeStackedSize(UIcontext *ui_context, UIitem *pitem, int dim) { 1321 | int wdim = dim+2; 1322 | short need_size = 0; 1323 | int kid = pitem->firstkid; 1324 | while (kid >= 0) { 1325 | UIitem *pkid = uiItemPtr(ui_context, kid); 1326 | // width += start margin + calculated width + end margin 1327 | need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1328 | kid = uiNextSibling(ui_context, kid); 1329 | } 1330 | pitem->size[dim] = need_size; 1331 | } 1332 | 1333 | // compute bounding box of all items stacked, repeating when breaking 1334 | UI_INLINE void uiComputeWrappedStackedSize(UIcontext *ui_context, UIitem *pitem, int dim) { 1335 | int wdim = dim+2; 1336 | 1337 | short need_size = 0; 1338 | short need_size2 = 0; 1339 | int kid = pitem->firstkid; 1340 | while (kid >= 0) { 1341 | UIitem *pkid = uiItemPtr(ui_context, kid); 1342 | 1343 | // if next position moved back, we assume a new line 1344 | if (pkid->flags & UI_BREAK) { 1345 | need_size2 = ui_max(need_size2, need_size); 1346 | // newline 1347 | need_size = 0; 1348 | } 1349 | 1350 | // width = start margin + calculated width + end margin 1351 | need_size += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1352 | kid = uiNextSibling(ui_context, kid); 1353 | } 1354 | pitem->size[dim] = ui_max(need_size2, need_size); 1355 | } 1356 | 1357 | // compute bounding box of all items stacked + wrapped 1358 | UI_INLINE void uiComputeWrappedSize(UIcontext *ui_context, UIitem *pitem, int dim) { 1359 | int wdim = dim+2; 1360 | 1361 | short need_size = 0; 1362 | short need_size2 = 0; 1363 | int kid = pitem->firstkid; 1364 | while (kid >= 0) { 1365 | UIitem *pkid = uiItemPtr(ui_context, kid); 1366 | 1367 | // if next position moved back, we assume a new line 1368 | if (pkid->flags & UI_BREAK) { 1369 | need_size2 += need_size; 1370 | // newline 1371 | need_size = 0; 1372 | } 1373 | 1374 | // width = start margin + calculated width + end margin 1375 | int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1376 | need_size = ui_max(need_size, kidsize); 1377 | kid = uiNextSibling(ui_context, kid); 1378 | } 1379 | pitem->size[dim] = need_size2 + need_size; 1380 | } 1381 | 1382 | static void uiComputeSize(UIcontext *ui_context, int item, int dim) { 1383 | UIitem *pitem = uiItemPtr(ui_context, item); 1384 | 1385 | // children expand the size 1386 | int kid = pitem->firstkid; 1387 | while (kid >= 0) { 1388 | uiComputeSize(ui_context, kid, dim); 1389 | kid = uiNextSibling(ui_context, kid); 1390 | } 1391 | 1392 | if (pitem->size[dim]) 1393 | return; 1394 | switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) { 1395 | case UI_COLUMN|UI_WRAP: { 1396 | // flex model 1397 | if (dim) // direction 1398 | uiComputeStackedSize(ui_context, pitem, 1); 1399 | else 1400 | uiComputeImposedSize(ui_context, pitem, 0); 1401 | } break; 1402 | case UI_ROW|UI_WRAP: { 1403 | // flex model 1404 | if (!dim) // direction 1405 | uiComputeWrappedStackedSize(ui_context, pitem, 0); 1406 | else 1407 | uiComputeWrappedSize(ui_context, pitem, 1); 1408 | } break; 1409 | case UI_COLUMN: 1410 | case UI_ROW: { 1411 | // flex model 1412 | if ((pitem->flags & 1) == (unsigned int)dim) // direction 1413 | uiComputeStackedSize(ui_context, pitem, dim); 1414 | else 1415 | uiComputeImposedSize(ui_context, pitem, dim); 1416 | } break; 1417 | default: { 1418 | // layout model 1419 | uiComputeImposedSize(ui_context, pitem, dim); 1420 | } break; 1421 | } 1422 | } 1423 | 1424 | // stack all items according to their alignment 1425 | UI_INLINE void uiArrangeStacked(UIcontext *ui_context, UIitem *pitem, int dim, bool wrap) { 1426 | int wdim = dim+2; 1427 | 1428 | short space = pitem->size[dim]; 1429 | float max_x2 = (float)pitem->margins[dim] + (float)space; 1430 | 1431 | int start_kid = pitem->firstkid; 1432 | while (start_kid >= 0) { 1433 | short used = 0; 1434 | 1435 | int count = 0; // count of fillers 1436 | int squeezed_count = 0; // count of squeezable elements 1437 | int total = 0; 1438 | bool hardbreak = false; 1439 | // first pass: count items that need to be expanded, 1440 | // and the space that is used 1441 | int kid = start_kid; 1442 | int end_kid = -1; 1443 | while (kid >= 0) { 1444 | UIitem *pkid = uiItemPtr(ui_context, kid); 1445 | int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; 1446 | int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim; 1447 | short extend = used; 1448 | if ((flags & UI_HFILL) == UI_HFILL) { // grow 1449 | count++; 1450 | extend += pkid->margins[dim] + pkid->margins[wdim]; 1451 | } else { 1452 | if ((fflags & UI_ITEM_HFIXED) != UI_ITEM_HFIXED) 1453 | squeezed_count++; 1454 | extend += pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1455 | } 1456 | // wrap on end of line or manual flag 1457 | if (wrap && (total && ((extend > space) || (pkid->flags & UI_BREAK)))) { 1458 | end_kid = kid; 1459 | hardbreak = ((pkid->flags & UI_BREAK) == UI_BREAK); 1460 | // add marker for subsequent queries 1461 | pkid->flags |= UI_BREAK; 1462 | break; 1463 | } else { 1464 | used = extend; 1465 | kid = uiNextSibling(ui_context, kid); 1466 | } 1467 | total++; 1468 | } 1469 | 1470 | int extra_space = space - used; 1471 | float filler = 0.0f; 1472 | float spacer = 0.0f; 1473 | float extra_margin = 0.0f; 1474 | float eater = 0.0f; 1475 | 1476 | if (extra_space > 0) { 1477 | if (count) { 1478 | filler = (float)extra_space / (float)count; 1479 | } else if (total) { 1480 | switch(pitem->flags & UI_JUSTIFY) { 1481 | default: { 1482 | extra_margin = extra_space / 2.0f; 1483 | } break; 1484 | case UI_JUSTIFY: { 1485 | // justify when not wrapping or not in last line, 1486 | // or not manually breaking 1487 | if (!wrap || ((end_kid != -1) && !hardbreak)) 1488 | spacer = (float)extra_space / (float)(total-1); 1489 | } break; 1490 | case UI_START: { 1491 | } break; 1492 | case UI_END: { 1493 | extra_margin = extra_space; 1494 | } break; 1495 | } 1496 | } 1497 | } else if (!wrap && (extra_space < 0)) { 1498 | eater = (float)extra_space / (float)squeezed_count; 1499 | } 1500 | 1501 | // distribute width among items 1502 | float x = (float)pitem->margins[dim]; 1503 | float x1; 1504 | // second pass: distribute and rescale 1505 | kid = start_kid; 1506 | while (kid != end_kid) { 1507 | short ix0,ix1; 1508 | UIitem *pkid = uiItemPtr(ui_context, kid); 1509 | int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; 1510 | int fflags = (pkid->flags & UI_ITEM_FIXED_MASK) >> dim; 1511 | 1512 | x += (float)pkid->margins[dim] + extra_margin; 1513 | if ((flags & UI_HFILL) == UI_HFILL) { // grow 1514 | x1 = x+filler; 1515 | } else if ((fflags & UI_ITEM_HFIXED) == UI_ITEM_HFIXED) { 1516 | x1 = x+(float)pkid->size[dim]; 1517 | } else { 1518 | // squeeze 1519 | x1 = x+ui_maxf(0.0f,(float)pkid->size[dim]+eater); 1520 | } 1521 | ix0 = (short)x; 1522 | if (wrap) 1523 | ix1 = (short)ui_minf(max_x2-(float)pkid->margins[wdim], x1); 1524 | else 1525 | ix1 = (short)x1; 1526 | pkid->margins[dim] = ix0; 1527 | pkid->size[dim] = ix1-ix0; 1528 | x = x1 + (float)pkid->margins[wdim]; 1529 | 1530 | kid = uiNextSibling(ui_context, kid); 1531 | extra_margin = spacer; 1532 | } 1533 | 1534 | start_kid = end_kid; 1535 | } 1536 | } 1537 | 1538 | // superimpose all items according to their alignment 1539 | UI_INLINE void uiArrangeImposedRange(UIcontext *ui_context, UIitem *pitem, int dim, 1540 | int start_kid, int end_kid, short offset, short space) { 1541 | int wdim = dim+2; 1542 | 1543 | int kid = start_kid; 1544 | while (kid != end_kid) { 1545 | UIitem *pkid = uiItemPtr(ui_context, kid); 1546 | 1547 | int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; 1548 | 1549 | switch(flags & UI_HFILL) { 1550 | default: break; 1551 | case UI_HCENTER: { 1552 | pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim]; 1553 | } break; 1554 | case UI_RIGHT: { 1555 | pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim]; 1556 | } break; 1557 | case UI_HFILL: { 1558 | pkid->size[dim] = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]); 1559 | } break; 1560 | } 1561 | pkid->margins[dim] += offset; 1562 | 1563 | kid = uiNextSibling(ui_context, kid); 1564 | } 1565 | } 1566 | 1567 | UI_INLINE void uiArrangeImposed(UIcontext *ui_context, UIitem *pitem, int dim) { 1568 | uiArrangeImposedRange(ui_context, pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]); 1569 | } 1570 | 1571 | // superimpose all items according to their alignment, 1572 | // squeeze items that expand the available space 1573 | UI_INLINE void uiArrangeImposedSqueezedRange(UIcontext *ui_context, UIitem *pitem, int dim, 1574 | int start_kid, int end_kid, short offset, short space) { 1575 | int wdim = dim+2; 1576 | 1577 | int kid = start_kid; 1578 | while (kid != end_kid) { 1579 | UIitem *pkid = uiItemPtr(ui_context, kid); 1580 | 1581 | int flags = (pkid->flags & UI_ITEM_LAYOUT_MASK) >> dim; 1582 | 1583 | short min_size = ui_max(0,space-pkid->margins[dim]-pkid->margins[wdim]); 1584 | switch(flags & UI_HFILL) { 1585 | default: { 1586 | pkid->size[dim] = ui_min(pkid->size[dim], min_size); 1587 | } break; 1588 | case UI_HCENTER: { 1589 | pkid->size[dim] = ui_min(pkid->size[dim], min_size); 1590 | pkid->margins[dim] += (space-pkid->size[dim])/2 - pkid->margins[wdim]; 1591 | } break; 1592 | case UI_RIGHT: { 1593 | pkid->size[dim] = ui_min(pkid->size[dim], min_size); 1594 | pkid->margins[dim] = space-pkid->size[dim]-pkid->margins[wdim]; 1595 | } break; 1596 | case UI_HFILL: { 1597 | pkid->size[dim] = min_size; 1598 | } break; 1599 | } 1600 | pkid->margins[dim] += offset; 1601 | 1602 | kid = uiNextSibling(ui_context, kid); 1603 | } 1604 | } 1605 | 1606 | UI_INLINE void uiArrangeImposedSqueezed(UIcontext *ui_context, UIitem *pitem, int dim) { 1607 | uiArrangeImposedSqueezedRange(ui_context, pitem, dim, pitem->firstkid, -1, pitem->margins[dim], pitem->size[dim]); 1608 | } 1609 | 1610 | // superimpose all items according to their alignment 1611 | UI_INLINE short uiArrangeWrappedImposedSqueezed(UIcontext *ui_context, UIitem *pitem, int dim) { 1612 | int wdim = dim+2; 1613 | 1614 | short offset = pitem->margins[dim]; 1615 | 1616 | short need_size = 0; 1617 | int kid = pitem->firstkid; 1618 | int start_kid = kid; 1619 | while (kid >= 0) { 1620 | UIitem *pkid = uiItemPtr(ui_context, kid); 1621 | 1622 | if (pkid->flags & UI_BREAK) { 1623 | uiArrangeImposedSqueezedRange(ui_context, pitem, dim, start_kid, kid, offset, need_size); 1624 | offset += need_size; 1625 | start_kid = kid; 1626 | // newline 1627 | need_size = 0; 1628 | } 1629 | 1630 | // width = start margin + calculated width + end margin 1631 | int kidsize = pkid->margins[dim] + pkid->size[dim] + pkid->margins[wdim]; 1632 | need_size = ui_max(need_size, kidsize); 1633 | kid = uiNextSibling(ui_context, kid); 1634 | } 1635 | 1636 | uiArrangeImposedSqueezedRange(ui_context, pitem, dim, start_kid, -1, offset, need_size); 1637 | offset += need_size; 1638 | return offset; 1639 | } 1640 | 1641 | static void uiArrange(UIcontext *ui_context, int item, int dim) { 1642 | UIitem *pitem = uiItemPtr(ui_context, item); 1643 | 1644 | switch(pitem->flags & UI_ITEM_BOX_MODEL_MASK) { 1645 | case UI_COLUMN|UI_WRAP: { 1646 | // flex model, wrapping 1647 | if (dim) { // direction 1648 | uiArrangeStacked(ui_context, pitem, 1, true); 1649 | // this retroactive resize will not effect parent widths 1650 | short offset = uiArrangeWrappedImposedSqueezed(ui_context, pitem, 0); 1651 | pitem->size[0] = offset - pitem->margins[0]; 1652 | } 1653 | } break; 1654 | case UI_ROW|UI_WRAP: { 1655 | // flex model, wrapping 1656 | if (!dim) { // direction 1657 | uiArrangeStacked(ui_context, pitem, 0, true); 1658 | } else { 1659 | uiArrangeWrappedImposedSqueezed(ui_context, pitem, 1); 1660 | } 1661 | } break; 1662 | case UI_COLUMN: 1663 | case UI_ROW: { 1664 | // flex model 1665 | if ((pitem->flags & 1) == (unsigned int)dim) // direction 1666 | uiArrangeStacked(ui_context, pitem, dim, false); 1667 | else 1668 | uiArrangeImposedSqueezed(ui_context, pitem, dim); 1669 | } break; 1670 | default: { 1671 | // layout model 1672 | uiArrangeImposed(ui_context, pitem, dim); 1673 | } break; 1674 | } 1675 | 1676 | int kid = uiFirstChild(ui_context, item); 1677 | while (kid >= 0) { 1678 | uiArrange(ui_context, kid, dim); 1679 | kid = uiNextSibling(ui_context, kid); 1680 | } 1681 | } 1682 | 1683 | UI_INLINE bool uiCompareItems(UIcontext *ui_context, UIitem *item1, UIitem *item2) { 1684 | return ((item1->flags & UI_ITEM_COMPARE_MASK) == (item2->flags & UI_ITEM_COMPARE_MASK)); 1685 | 1686 | } 1687 | 1688 | static bool uiMapItems(UIcontext *ui_context, int item1, int item2) { 1689 | UIitem *pitem1 = uiLastItemPtr(ui_context, item1); 1690 | if (item2 == -1) { 1691 | return false; 1692 | } 1693 | 1694 | UIitem *pitem2 = uiItemPtr(ui_context, item2); 1695 | if (!uiCompareItems(ui_context, pitem1, pitem2)) { 1696 | return false; 1697 | } 1698 | 1699 | int count = 0; 1700 | int failed = 0; 1701 | int kid1 = pitem1->firstkid; 1702 | int kid2 = pitem2->firstkid; 1703 | while (kid1 != -1) { 1704 | UIitem *pkid1 = uiLastItemPtr(ui_context, kid1); 1705 | count++; 1706 | if (!uiMapItems(ui_context, kid1, kid2)) { 1707 | failed = count; 1708 | break; 1709 | } 1710 | kid1 = pkid1->nextitem; 1711 | if (kid2 != -1) { 1712 | kid2 = uiItemPtr(ui_context, kid2)->nextitem; 1713 | } 1714 | } 1715 | 1716 | if (count && (failed == 1)) { 1717 | return false; 1718 | } 1719 | 1720 | ui_context->item_map[item1] = item2; 1721 | return true; 1722 | } 1723 | 1724 | int uiRecoverItem(UIcontext *ui_context, int olditem) { 1725 | assert(ui_context); 1726 | assert((olditem >= -1) && (olditem < ui_context->last_count)); 1727 | if (olditem == -1) return -1; 1728 | return ui_context->item_map[olditem]; 1729 | } 1730 | 1731 | void uiRemapItem(UIcontext *ui_context, int olditem, int newitem) { 1732 | assert(ui_context); 1733 | assert((olditem >= 0) && (olditem < ui_context->last_count)); 1734 | assert((newitem >= -1) && (newitem < ui_context->count)); 1735 | ui_context->item_map[olditem] = newitem; 1736 | } 1737 | 1738 | void uiEndLayout(UIcontext *ui_context) { 1739 | assert(ui_context); 1740 | assert(ui_context->stage == UI_STAGE_LAYOUT); // must run uiBeginLayout() first 1741 | 1742 | if (ui_context->count) { 1743 | uiComputeSize(ui_context, 0, 0); 1744 | uiArrange(ui_context, 0, 0); 1745 | uiComputeSize(ui_context, 0, 1); 1746 | uiArrange(ui_context, 0, 1); 1747 | 1748 | if (ui_context->last_count) { 1749 | // map old item id to new item id 1750 | uiMapItems(ui_context, 0, 0); 1751 | } 1752 | } 1753 | 1754 | uiValidateStateItems(ui_context); 1755 | if (ui_context->count) { 1756 | // drawing routines may require this to be set already 1757 | uiUpdateHotItem(ui_context); 1758 | } 1759 | 1760 | ui_context->stage = UI_STAGE_POST_LAYOUT; 1761 | } 1762 | 1763 | UIrect uiGetRect(UIcontext *ui_context, int item) { 1764 | UIitem *pitem = uiItemPtr(ui_context, item); 1765 | UIrect rc = {{{ 1766 | pitem->margins[0], pitem->margins[1], 1767 | pitem->size[0], pitem->size[1] 1768 | }}}; 1769 | return rc; 1770 | } 1771 | 1772 | int uiFirstChild(UIcontext *ui_context, int item) { 1773 | return uiItemPtr(ui_context, item)->firstkid; 1774 | } 1775 | 1776 | int uiNextSibling(UIcontext *ui_context, int item) { 1777 | return uiItemPtr(ui_context, item)->nextitem; 1778 | } 1779 | 1780 | void *uiAllocHandle(UIcontext *ui_context, int item, unsigned int size) { 1781 | assert((size > 0) && (size < UI_MAX_DATASIZE)); 1782 | UIitem *pitem = uiItemPtr(ui_context, item); 1783 | assert(pitem->handle == NULL); 1784 | assert((ui_context->datasize+size) <= ui_context->buffer_capacity); 1785 | pitem->handle = ui_context->data + ui_context->datasize; 1786 | pitem->flags |= UI_ITEM_DATA; 1787 | ui_context->datasize += size; 1788 | return pitem->handle; 1789 | } 1790 | 1791 | void uiSetHandle(UIcontext *ui_context, int item, void *handle) { 1792 | UIitem *pitem = uiItemPtr(ui_context, item); 1793 | assert(pitem->handle == NULL); 1794 | pitem->handle = handle; 1795 | } 1796 | 1797 | void *uiGetHandle(UIcontext *ui_context, int item) { 1798 | return uiItemPtr(ui_context, item)->handle; 1799 | } 1800 | 1801 | void uiSetHandler(UIcontext *ui_context, UIhandler handler) { 1802 | assert(ui_context); 1803 | ui_context->handler = handler; 1804 | } 1805 | 1806 | UIhandler uiGetHandler(UIcontext *ui_context) { 1807 | assert(ui_context); 1808 | return ui_context->handler; 1809 | } 1810 | 1811 | void uiSetEvents(UIcontext *ui_context, int item, unsigned int flags) { 1812 | UIitem *pitem = uiItemPtr(ui_context, item); 1813 | pitem->flags &= ~UI_ITEM_EVENT_MASK; 1814 | pitem->flags |= flags & UI_ITEM_EVENT_MASK; 1815 | } 1816 | 1817 | unsigned int uiGetEvents(UIcontext *ui_context, int item) { 1818 | return uiItemPtr(ui_context, item)->flags & UI_ITEM_EVENT_MASK; 1819 | } 1820 | 1821 | void uiSetFlags(UIcontext *ui_context, int item, unsigned int flags) { 1822 | UIitem *pitem = uiItemPtr(ui_context, item); 1823 | pitem->flags &= ~UI_USERMASK; 1824 | pitem->flags |= flags & UI_USERMASK; 1825 | } 1826 | 1827 | unsigned int uiGetFlags(UIcontext *ui_context, int item) { 1828 | return uiItemPtr(ui_context, item)->flags & UI_USERMASK; 1829 | } 1830 | 1831 | int uiContains(UIcontext *ui_context, int item, int x, int y) { 1832 | UIrect rect = uiGetRect(ui_context, item); 1833 | x -= rect.x; 1834 | y -= rect.y; 1835 | if ((x>=0) 1836 | && (y>=0) 1837 | && (xflags & UI_ITEM_FROZEN) return -1; 1845 | if (uiContains(ui_context, item, x, y)) { 1846 | int best_hit = -1; 1847 | int kid = uiFirstChild(ui_context, item); 1848 | while (kid >= 0) { 1849 | int hit = uiFindItem(ui_context, kid, x, y, flags, mask); 1850 | if (hit >= 0) { 1851 | best_hit = hit; 1852 | } 1853 | kid = uiNextSibling(ui_context, kid); 1854 | } 1855 | if (best_hit >= 0) { 1856 | return best_hit; 1857 | } 1858 | if (((mask == UI_ANY) && ((flags == UI_ANY) 1859 | || (pitem->flags & flags))) 1860 | || ((pitem->flags & flags) == mask)) { 1861 | return item; 1862 | } 1863 | } 1864 | return -1; 1865 | } 1866 | 1867 | void uiUpdateHotItem(UIcontext *ui_context) { 1868 | assert(ui_context); 1869 | if (!ui_context->count) return; 1870 | ui_context->hot_item = uiFindItem(ui_context, 0, 1871 | ui_context->cursor.x, ui_context->cursor.y, 1872 | UI_ANY_MOUSE_INPUT, UI_ANY); 1873 | } 1874 | 1875 | int uiGetClicks(UIcontext *ui_context) { 1876 | return ui_context->clicks; 1877 | } 1878 | 1879 | void uiProcess(UIcontext *ui_context, int timestamp) { 1880 | assert(ui_context); 1881 | 1882 | assert(ui_context->stage != UI_STAGE_LAYOUT); // must run uiBeginLayout(), uiEndLayout() first 1883 | 1884 | if (ui_context->stage == UI_STAGE_PROCESS) { 1885 | uiUpdateHotItem(ui_context); 1886 | } 1887 | ui_context->stage = UI_STAGE_PROCESS; 1888 | 1889 | if (!ui_context->count) { 1890 | uiClearInputEvents(ui_context); 1891 | return; 1892 | } 1893 | 1894 | int hot_item = ui_context->last_hot_item; 1895 | int active_item = ui_context->active_item; 1896 | int focus_item = ui_context->focus_item; 1897 | 1898 | // send all keyboard events 1899 | if (focus_item >= 0) { 1900 | int i; 1901 | for (i = 0; i < ui_context->eventcount; ++i) { 1902 | ui_context->active_key = ui_context->events[i].key; 1903 | ui_context->active_modifier = ui_context->events[i].mod; 1904 | uiNotifyItem(ui_context, focus_item, ui_context->events[i].event); 1905 | } 1906 | } else { 1907 | ui_context->focus_item = -1; 1908 | } 1909 | if (ui_context->scroll.x || ui_context->scroll.y) { 1910 | int scroll_item = uiFindItem(ui_context, 0, 1911 | ui_context->cursor.x, ui_context->cursor.y, 1912 | UI_SCROLL, UI_ANY); 1913 | if (scroll_item >= 0) { 1914 | uiNotifyItem(ui_context, scroll_item, UI_SCROLL); 1915 | } 1916 | } 1917 | 1918 | uiClearInputEvents(ui_context); 1919 | 1920 | int hot = ui_context->hot_item; 1921 | 1922 | switch(ui_context->state) { 1923 | default: 1924 | case UI_STATE_IDLE: { 1925 | ui_context->start_cursor = ui_context->cursor; 1926 | if (uiGetButton(ui_context, 0)) { 1927 | hot_item = -1; 1928 | active_item = hot; 1929 | 1930 | if (active_item != focus_item) { 1931 | focus_item = -1; 1932 | ui_context->focus_item = -1; 1933 | } 1934 | 1935 | if (active_item >= 0) { 1936 | if ( 1937 | ((timestamp - ui_context->last_click_timestamp) > UI_CLICK_THRESHOLD) 1938 | || (ui_context->last_click_item != active_item)) { 1939 | ui_context->clicks = 0; 1940 | } 1941 | ui_context->clicks++; 1942 | 1943 | ui_context->last_click_timestamp = timestamp; 1944 | ui_context->last_click_item = active_item; 1945 | ui_context->active_modifier = ui_context->active_button_modifier; 1946 | uiNotifyItem(ui_context, active_item, UI_BUTTON0_DOWN); 1947 | } 1948 | ui_context->state = UI_STATE_CAPTURE; 1949 | } else if (uiGetButton(ui_context, 2) && !uiGetLastButton(ui_context, 2)) { 1950 | hot_item = -1; 1951 | hot = uiFindItem(ui_context, 0, ui_context->cursor.x, ui_context->cursor.y, 1952 | UI_BUTTON2_DOWN, UI_ANY); 1953 | if (hot >= 0) { 1954 | ui_context->active_modifier = ui_context->active_button_modifier; 1955 | uiNotifyItem(ui_context, hot, UI_BUTTON2_DOWN); 1956 | } 1957 | } else { 1958 | hot_item = hot; 1959 | } 1960 | } break; 1961 | case UI_STATE_CAPTURE: { 1962 | if (!uiGetButton(ui_context, 0)) { 1963 | if (active_item >= 0) { 1964 | ui_context->active_modifier = ui_context->active_button_modifier; 1965 | uiNotifyItem(ui_context, active_item, UI_BUTTON0_UP); 1966 | if (active_item == hot) { 1967 | uiNotifyItem(ui_context, active_item, UI_BUTTON0_HOT_UP); 1968 | } 1969 | } 1970 | active_item = -1; 1971 | ui_context->state = UI_STATE_IDLE; 1972 | } else { 1973 | if (active_item >= 0) { 1974 | ui_context->active_modifier = ui_context->active_button_modifier; 1975 | uiNotifyItem(ui_context, active_item, UI_BUTTON0_CAPTURE); 1976 | } 1977 | if (hot == active_item) 1978 | hot_item = hot; 1979 | else 1980 | hot_item = -1; 1981 | } 1982 | } break; 1983 | } 1984 | 1985 | ui_context->last_cursor = ui_context->cursor; 1986 | ui_context->last_hot_item = hot_item; 1987 | ui_context->active_item = active_item; 1988 | 1989 | ui_context->last_timestamp = timestamp; 1990 | ui_context->last_buttons = ui_context->buttons; 1991 | } 1992 | 1993 | static int uiIsActive(UIcontext *ui_context, int item) { 1994 | assert(ui_context); 1995 | return ui_context->active_item == item; 1996 | } 1997 | 1998 | static int uiIsHot(UIcontext *ui_context, int item) { 1999 | assert(ui_context); 2000 | return ui_context->last_hot_item == item; 2001 | } 2002 | 2003 | static int uiIsFocused(UIcontext *ui_context, int item) { 2004 | assert(ui_context); 2005 | return ui_context->focus_item == item; 2006 | } 2007 | 2008 | UIitemState uiGetState(UIcontext *ui_context, int item) { 2009 | UIitem *pitem = uiItemPtr(ui_context, item); 2010 | if (pitem->flags & UI_ITEM_FROZEN) return UI_FROZEN; 2011 | if (uiIsFocused(ui_context, item)) { 2012 | if (pitem->flags & (UI_KEY_DOWN|UI_CHAR|UI_KEY_UP)) return UI_ACTIVE; 2013 | } 2014 | if (uiIsActive(ui_context, item)) { 2015 | if (pitem->flags & (UI_BUTTON0_CAPTURE|UI_BUTTON0_UP)) return UI_ACTIVE; 2016 | if ((pitem->flags & UI_BUTTON0_HOT_UP) 2017 | && uiIsHot(ui_context, item)) return UI_ACTIVE; 2018 | return UI_COLD; 2019 | } else if (uiIsHot(ui_context, item)) { 2020 | return UI_HOT; 2021 | } 2022 | return UI_COLD; 2023 | } 2024 | 2025 | #endif // OUI_IMPLEMENTATION 2026 | -------------------------------------------------------------------------------- /premake4.lua: -------------------------------------------------------------------------------- 1 | 2 | local action = _ACTION or "" 3 | 4 | solution "blendish" 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.c", "nanovg/src/nanovg.c" } 13 | includedirs { "nanovg/src" } 14 | targetdir("build") 15 | 16 | configuration { "linux" } 17 | linkoptions { "`pkg-config --libs glfw3 --static`" } 18 | links { "GL", "GLU", "m", "GLEW" } 19 | defines { "NANOVG_GLEW" } 20 | 21 | configuration { "windows" } 22 | links { "glfw3", "gdi32", "winmm", "user32", "GLEW", "glu32","opengl32" } 23 | defines { "NANOVG_GLEW" } 24 | 25 | configuration { "macosx" } 26 | links { "glfw3" } 27 | linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" } 28 | 29 | configuration "Debug" 30 | defines { "DEBUG" } 31 | flags { "Symbols", "ExtraWarnings", "FatalWarnings" } 32 | 33 | configuration "Release" 34 | defines { "NDEBUG" } 35 | flags { "Optimize", "ExtraWarnings", "FatalWarnings" } 36 | 37 | --------------------------------------------------------------------------------