├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── examples ├── Makefile ├── container.cpp ├── rootcontainer.cpp └── widgets.cpp ├── images ├── dock_example.gif ├── ogv2gif.sh └── tooltip_example.gif ├── imgui ├── Makefile ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_glfw.cpp ├── imgui_impl_glfw.h ├── imgui_internal.h ├── stb_rect_pack.h ├── stb_textedit.h └── stb_truetype.h ├── imgui_dock.cpp ├── imgui_dock.h ├── imgui_widgets.cpp └── imgui_widgets.h /.gitignore: -------------------------------------------------------------------------------- 1 | .depend 2 | *~ 3 | *.o 4 | examples/container 5 | examples/rootcontainer 6 | examples/widgets 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | RM=rm -f 3 | CXXFLAGS=-I./ -I./imgui/ $(shell pkg-config --cflags glfw3 glu gl) -Wall -Wformat -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++11 -w -g 4 | CFLAGS = $(CXXFLAGS) 5 | LDFLAGS=-g $(shell pkg-config --libs glfw3 glu gl) 6 | 7 | SRCS=imgui_dock.cpp imgui_widgets.cpp 8 | OBJS=$(subst .cpp,.o,$(SRCS)) 9 | 10 | all: .depend $(OBJS) 11 | @true 12 | 13 | .depend: $(SRCS) 14 | $(RM) ./.depend 15 | $(CXX) $(CXXFLAGS) -MM $^>>./.depend; 16 | 17 | clean: 18 | $(RM) $(OBJS) 19 | 20 | distclean: clean 21 | $(RM) *~ .depend 22 | 23 | include .depend 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImGui Goodies 2 | 3 | This is a collection of widgets and utilities for the 4 | [immediate mode GUI (imgui)](https://github.com/ocornut/imgui) that I 5 | am developing for the critic2 GUI. Currently, the contents of this 6 | repo are: 7 | 8 | * `imgui_widget.cpp`: a collection of small independent widgets, 9 | including: 10 | 11 | + `SlidingBar`: a bar widget that slides when it is grabbed with the 12 | mouse. 13 | 14 | + `ButtonWithX`: a button with an X at the end. Useful for tabs. 15 | 16 | + `ResizeGripOther`: a resize grip that resizes a window other than 17 | the one on which it is drawn. 18 | 19 | + `LiftGrip`: a grip on the bottom left part of the window rendered in 20 | a different color. Responds to grabbing. 21 | 22 | + `AttachTooltip`: a function that provides delayed 23 | tooltips. Tooltips are shown if: i) the mouse hovers over the 24 | element for longer than a certain time, passed as argument to the 25 | function, ii) the mouse goes from one tooltip element to another, 26 | and the tooltip is being shown, or iii) the mouse hovers a tooltip 27 | element for longer than t seconds and less than t seconds have 28 | elapsed between the time the last tooltip was shown and the current 29 | item started being hovered. See example below. 30 | 31 | * `imgui_dock.cpp`: a window docking system. There are three types of 32 | windows: 33 | 34 | + Docks (`BeginDock()`/`EndDock()`): like normal windows, except 35 | they can be docked to the other two window types. 36 | 37 | + Containers (`Container()`): a window to which docks can be 38 | attached as tabs. It has a behavior similar to a browser window. 39 | 40 | + Root containers (`RootContainer()`): a window to which both 41 | containers and docks can be attached. It splits into regions for 42 | the different attached windows depending on where they are 43 | dropped. 44 | 45 | Most of the functionality of normal ImGui windows is 46 | preserved. `imgui_dock.cpp` uses the widgets in `imgui_widget.cpp` 47 | but is otherwise self-contained. 48 | 49 | Some examples are given in the `examples` subdirectory. Use the 50 | `compile.sh` script to build the whole directory tree. 51 | 52 | ## Examples 53 | 54 | Docks, containers, and root containers: 55 | 56 | ![Example](images/dock_example.gif) 57 | 58 | Delayed tooltips: 59 | 60 | ![Example](images/tooltip_example.gif) 61 | 62 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | RM=rm -f 3 | CXXFLAGS=-I./ -I../ -I../imgui/ $(shell pkg-config --cflags glfw3 glu gl) -Wall -Wformat -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++11 -w -g 4 | CFLAGS = $(CXXFLAGS) 5 | LDFLAGS=-g -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++11 $(shell pkg-config --libs glfw3 glu gl) 6 | 7 | OBJS=../imgui/imgui.o ../imgui/imgui_draw.o ../imgui/imgui_demo.o ../imgui/imgui_impl_glfw.o \ 8 | ../imgui_dock.o ../imgui_widgets.o 9 | 10 | .cpp.o: 11 | $(CXX) $(CXXFLAGS) -c -o $@ $< 12 | 13 | all: widgets container rootcontainer 14 | @true 15 | 16 | widgets: widgets.o $(OBJS) 17 | $(CXX) $(LDFLAGS) -o $@ $(LDFLAGS) $@.o $(OBJS) 18 | 19 | container: container.o $(OBJS) 20 | $(CXX) $(LDFLAGS) -o $@ $(LDFLAGS) $@.o $(OBJS) 21 | 22 | rootcontainer: rootcontainer.o $(OBJS) 23 | $(CXX) $(LDFLAGS) -o $@ $(LDFLAGS) $@.o $(OBJS) 24 | 25 | clean: 26 | $(RM) *.o $(BINS) 27 | 28 | -------------------------------------------------------------------------------- /examples/container.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Alberto Otero de la Roza 3 | . 4 | 5 | imgui-goodies is free software: you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | imgui-goodies is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace std; 31 | using namespace ImGui; 32 | 33 | static void error_callback(int error, const char* description){ 34 | fprintf(stderr, "Error %d: %s\n", error, description); 35 | } 36 | 37 | int main(int argc, char *argv[]){ 38 | // Initialize 39 | glfwSetErrorCallback(error_callback); 40 | if (!glfwInit()) exit(EXIT_FAILURE); 41 | 42 | // Set up window 43 | GLFWwindow* rootwin = glfwCreateWindow(1280, 720, "gcritic2", nullptr, nullptr); 44 | assert(rootwin!=nullptr); 45 | glfwMakeContextCurrent(rootwin); 46 | 47 | // Setup ImGui binding 48 | ImGui_ImplGlfwGL2_Init(rootwin, true); 49 | 50 | // GUI settings 51 | ImGuiIO& io = GetIO(); 52 | io.IniFilename = nullptr; 53 | 54 | // Main loop 55 | while (!glfwWindowShouldClose(rootwin)){ 56 | // New frame 57 | glfwPollEvents(); 58 | ImGui_ImplGlfwGL2_NewFrame(); 59 | ImGuiContext *g = GetCurrentContext(); 60 | static bool first = true; 61 | 62 | // Main menu bar 63 | if (BeginMainMenuBar()){ 64 | if (BeginMenu("File")){ 65 | if (MenuItem("Quit","Ctrl+Q")) 66 | glfwSetWindowShouldClose(rootwin, GLFW_TRUE); 67 | EndMenu(); 68 | } 69 | SameLine(0, GetWindowSize().x-250.); 70 | Text("%.3f ms/frame (%.1f FPS)", 1000.0f / GetIO().Framerate, GetIO().Framerate); 71 | } 72 | EndMainMenuBar(); 73 | 74 | static bool pcont = true; 75 | ImGui::SetNextWindowPos(ImVec2(270,20),ImGuiSetCond_FirstUseEver); 76 | ImGui::SetNextWindowSize(ImVec2(300,300),ImGuiSetCond_FirstUseEver); 77 | Dock *dcont1 = nullptr; 78 | if (pcont) 79 | dcont1 = ImGui::Container("Floating Container",&pcont); 80 | 81 | ImGui::SetNextWindowPos(ImVec2(270,370),ImGuiSetCond_FirstUseEver); 82 | ImGui::SetNextWindowSize(ImVec2(300,300),ImGuiSetCond_FirstUseEver); 83 | Dock *dcont2 = ImGui::Container("Fixed Container",nullptr,ImGuiWindowFlags_NoResize| 84 | ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBringToFrontOnFocus); 85 | 86 | // Some docks unattached on the side 87 | static bool popen[10] = {true,true,true,true,true,true,true,true,true,true}; 88 | for (int i=0;i<10;i++){ 89 | if (popen[i]){ 90 | SetNextWindowPos(ImVec2(20.f,40.f),ImGuiSetCond_FirstUseEver); 91 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 92 | char tmp[16]; 93 | sprintf(tmp,"Dock #%d",i); 94 | if (BeginDock(tmp,&(popen[i]))){ 95 | Text("Hello, world!"); 96 | if (Button("Click me!")){ 97 | printf("Clicked in dock %d\n",i); 98 | } 99 | } 100 | EndDock(); 101 | } 102 | } 103 | 104 | // Some docks attached to a container 105 | static Dock *pwhich[5] = {dcont1,dcont2,dcont1,dcont2,dcont1}; 106 | static bool popen2[3] = {true,true,true}; 107 | for (int i=0;i<3;i++){ 108 | if (popen2[i]){ 109 | char tmp[16]; 110 | sprintf(tmp,"Dck #%d",i); 111 | if (BeginDock(tmp,&(popen2[i]),0,0,pwhich[i])){ 112 | Text("Hello, world!"); 113 | if (Button("Click me!")){ 114 | printf("Clicked in dock %d\n",i); 115 | } 116 | } 117 | Dock *dtmp = GetCurrentDock(); 118 | EndDock(); 119 | if (first) 120 | dtmp->setDetachedDockSize(100.f, 100.f); 121 | } 122 | } 123 | for (int i=3;i<5;i++){ 124 | char tmp[16]; 125 | sprintf(tmp,"Dck #%d",i); 126 | if (BeginDock(tmp,nullptr,0,0,pwhich[i])){ 127 | Text("You can not close me."); 128 | if (Button("Click me!")){ 129 | printf("Clicked in dock %d\n",i); 130 | } 131 | } 132 | Dock *dtmp = GetCurrentDock(); 133 | EndDock(); 134 | if (first) 135 | dtmp->setDetachedDockSize(100.f, 100.f); 136 | } 137 | 138 | // Draw the current scene 139 | int w, h; 140 | glfwGetFramebufferSize(rootwin,&w,&h); 141 | glViewport(0,0,w,h); 142 | glClearColor(1.0f,0.8f,0.5f,0.0f); 143 | glClear(GL_COLOR_BUFFER_BIT); 144 | 145 | // Render and swap 146 | Render(); 147 | glfwSwapBuffers(rootwin); 148 | first = false; 149 | } 150 | 151 | // Cleanup 152 | ShutdownDock(); 153 | ImGui_ImplGlfwGL2_Shutdown(); 154 | glfwTerminate(); 155 | 156 | return 0; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /examples/rootcontainer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Alberto Otero de la Roza 3 | . 4 | 5 | imgui-goodies is free software: you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | imgui-goodies is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace std; 31 | using namespace ImGui; 32 | 33 | static void error_callback(int error, const char* description){ 34 | fprintf(stderr, "Error %d: %s\n", error, description); 35 | } 36 | 37 | int main(int argc, char *argv[]){ 38 | // Initialize 39 | glfwSetErrorCallback(error_callback); 40 | if (!glfwInit()) exit(EXIT_FAILURE); 41 | 42 | // Set up window 43 | GLFWwindow* rootwin = glfwCreateWindow(1280, 720, "gcritic2", nullptr, nullptr); 44 | assert(rootwin!=nullptr); 45 | glfwMakeContextCurrent(rootwin); 46 | 47 | // Setup ImGui binding 48 | ImGui_ImplGlfwGL2_Init(rootwin, true); 49 | 50 | // GUI settings 51 | ImGuiIO& io = GetIO(); 52 | io.IniFilename = nullptr; 53 | 54 | // Main loop 55 | while (!glfwWindowShouldClose(rootwin)){ 56 | // New frame 57 | glfwPollEvents(); 58 | ImGui_ImplGlfwGL2_NewFrame(); 59 | ImGuiContext *g = GetCurrentContext(); 60 | static bool first = true; 61 | 62 | // Main menu bar 63 | if (BeginMainMenuBar()){ 64 | if (BeginMenu("File")){ 65 | if (MenuItem("Quit","Ctrl+Q")) 66 | glfwSetWindowShouldClose(rootwin, GLFW_TRUE); 67 | EndMenu(); 68 | } 69 | SameLine(0, GetWindowSize().x-250.); 70 | Text("%.3f ms/frame (%.1f FPS)", 1000.0f / GetIO().Framerate, GetIO().Framerate); 71 | } 72 | EndMainMenuBar(); 73 | 74 | // Root container 75 | static bool proot = true; 76 | Dock *droot = nullptr; 77 | ImGui::SetNextWindowPos(ImVec2(590,70),ImGuiSetCond_FirstUseEver); 78 | ImGui::SetNextWindowSize(ImVec2(400,400),ImGuiSetCond_FirstUseEver); 79 | if (proot) 80 | droot = RootContainer("Root Container",&proot); 81 | 82 | // Some containers with docks attached 83 | static bool pcont = true; 84 | ImGui::SetNextWindowPos(ImVec2(270,20),ImGuiSetCond_FirstUseEver); 85 | ImGui::SetNextWindowSize(ImVec2(300,300),ImGuiSetCond_FirstUseEver); 86 | Dock *dcont1 = nullptr; 87 | if (pcont) 88 | dcont1 = ImGui::Container("One Container",&pcont); 89 | if (first) 90 | dcont1->setDetachedDockSize(200.f, 200.f); 91 | 92 | ImGui::SetNextWindowPos(ImVec2(270,370),ImGuiSetCond_FirstUseEver); 93 | ImGui::SetNextWindowSize(ImVec2(300,300),ImGuiSetCond_FirstUseEver); 94 | Dock *dcont2 = ImGui::Container("Two Container",nullptr,ImGuiWindowFlags_NoResize| 95 | ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBringToFrontOnFocus); 96 | if (first) 97 | dcont2->setDetachedDockSize(200.f, 200.f); 98 | 99 | // Some containers unattached on the side 100 | static bool popen3[5] = {true,true,true,true,true}; 101 | for (int i=0;i<5;i++){ 102 | if (popen3[i]){ 103 | SetNextWindowPos(ImVec2(20.f,290.f),ImGuiSetCond_FirstUseEver); 104 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 105 | char tmp[16]; 106 | sprintf(tmp,"Container #%d",i); 107 | ImGui::Container(tmp,&(popen3[i])); 108 | } 109 | } 110 | 111 | // Some docks unattached on the side 112 | static bool popen[10] = {true,true,true,true,true,true,true,true,true,true}; 113 | for (int i=0;i<10;i++){ 114 | if (popen[i]){ 115 | SetNextWindowPos(ImVec2(20.f,40.f),ImGuiSetCond_FirstUseEver); 116 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 117 | char tmp[16]; 118 | sprintf(tmp,"Dock #%d",i); 119 | if (BeginDock(tmp,&(popen[i]))){ 120 | Text("Hello, world!"); 121 | if (Button("Click me!")){ 122 | printf("Clicked in dock %d\n",i); 123 | } 124 | } 125 | EndDock(); 126 | } 127 | } 128 | 129 | // Some docks attached to a container 130 | static Dock *pwhich[5] = {dcont1,dcont2,dcont1,dcont2,dcont1}; 131 | static bool popen2[3] = {true,true,true}; 132 | for (int i=0;i<3;i++){ 133 | if (popen2[i]){ 134 | char tmp[16]; 135 | sprintf(tmp,"Dck #%d",i); 136 | if (BeginDock(tmp,&(popen2[i]),0,0,pwhich[i])){ 137 | Text("Hello, world!"); 138 | if (Button("Click me!")){ 139 | printf("Clicked in dock %d\n",i); 140 | } 141 | } 142 | Dock *dtmp = GetCurrentDock(); 143 | EndDock(); 144 | if (first) 145 | dtmp->setDetachedDockSize(100.f, 100.f); 146 | } 147 | } 148 | for (int i=3;i<5;i++){ 149 | char tmp[16]; 150 | sprintf(tmp,"Dck #%d",i); 151 | if (BeginDock(tmp,nullptr,0,0,pwhich[i])){ 152 | Text("You can not close me."); 153 | if (Button("Click me!")){ 154 | printf("Clicked in dock %d\n",i); 155 | } 156 | } 157 | Dock *dtmp = GetCurrentDock(); 158 | EndDock(); 159 | if (first) 160 | dtmp->setDetachedDockSize(100.f, 100.f); 161 | } 162 | 163 | // Some more docks attached to the root container 164 | if (BeginDock("Bleh1",nullptr)){ 165 | Text("Bleh1."); 166 | if (Button("Click me!")){ 167 | printf("Clicked in dock Bleh1\n"); 168 | } 169 | } 170 | Dock *dbleh1 = GetCurrentDock(); 171 | EndDock(); 172 | if (first) 173 | dbleh1->setDetachedDockSize(100.f, 100.f); 174 | if (BeginDock("Bleh2",nullptr,ImGuiWindowFlags_NoResize)){ 175 | Text("Bleh2."); 176 | if (Button("Click me!")){ 177 | printf("Clicked in dock Bleh2\n"); 178 | } 179 | } 180 | Dock *dbleh2 = GetCurrentDock(); 181 | EndDock(); 182 | if (first) 183 | dbleh2->setDetachedDockSize(100.f, 100.f); 184 | 185 | // // Dock some containers and docks in the root container 186 | // if (first){ 187 | // // 1:top, 2:right, 3:bottom, 4:left, 5:replace 188 | // droot->newDockRoot(dcont1,5); // replace the automatic container 189 | 190 | // dcont1->newDockRoot(dcont2,4); // dcont2 on the left of dcont1 191 | // dcont1->setSlidingBarPosition(4,0.3f); // set the bar on the left of dcont1 192 | 193 | // // dock bleh1 below dcont1; save the created automatic container in dtmp 194 | // Dock *dtmp = dcont1->newDockRoot(dbleh1,3); 195 | // dcont1->setSlidingBarPosition(3,0.4f); 196 | 197 | // // dock bleh2 on the right of bleh1 198 | // dtmp->newDockRoot(dbleh2,2); 199 | // dcont1->setSlidingBarPosition(2,0.3f); 200 | // } 201 | 202 | // Draw the current scene 203 | int w, h; 204 | glfwGetFramebufferSize(rootwin,&w,&h); 205 | glViewport(0,0,w,h); 206 | glClearColor(1.0f,0.8f,0.5f,0.0f); 207 | glClear(GL_COLOR_BUFFER_BIT); 208 | 209 | // Render and swap 210 | Render(); 211 | glfwSwapBuffers(rootwin); 212 | first = false; 213 | } 214 | 215 | // Cleanup 216 | ShutdownDock(); 217 | ImGui_ImplGlfwGL2_Shutdown(); 218 | glfwTerminate(); 219 | 220 | return 0; 221 | } 222 | 223 | -------------------------------------------------------------------------------- /examples/widgets.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Alberto Otero de la Roza 3 | . 4 | 5 | imgui-goodies is free software: you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License as 7 | published by the Free Software Foundation, either version 3 of the 8 | License, or (at your option) any later version. 9 | 10 | imgui-goodies is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace std; 31 | using namespace ImGui; 32 | 33 | static void error_callback(int error, const char* description){ 34 | fprintf(stderr, "Error %d: %s\n", error, description); 35 | } 36 | 37 | int main(int argc, char *argv[]){ 38 | // Initialize 39 | glfwSetErrorCallback(error_callback); 40 | if (!glfwInit()) exit(EXIT_FAILURE); 41 | 42 | // Set up window 43 | GLFWwindow* rootwin = glfwCreateWindow(1280, 720, "gcritic2", nullptr, nullptr); 44 | assert(rootwin!=nullptr); 45 | glfwMakeContextCurrent(rootwin); 46 | 47 | // Setup ImGui binding 48 | ImGui_ImplGlfwGL2_Init(rootwin, true); 49 | 50 | // GUI settings 51 | ImGuiIO& io = GetIO(); 52 | io.IniFilename = nullptr; 53 | 54 | // Main loop 55 | while (!glfwWindowShouldClose(rootwin)){ 56 | // New frame 57 | glfwPollEvents(); 58 | ImGui_ImplGlfwGL2_NewFrame(); 59 | ImGuiContext *g = GetCurrentContext(); 60 | 61 | // Main menu bar 62 | if (BeginMainMenuBar()){ 63 | if (BeginMenu("File")){ 64 | if (MenuItem("Quit","Ctrl+Q")) 65 | glfwSetWindowShouldClose(rootwin, GLFW_TRUE); 66 | EndMenu(); 67 | } 68 | SameLine(0, GetWindowSize().x-250.); 69 | Text("%.3f ms/frame (%.1f FPS)", 1000.0f / GetIO().Framerate, GetIO().Framerate); 70 | } 71 | EndMainMenuBar(); 72 | 73 | // Sliding bar example 74 | SetNextWindowPos(ImVec2(20.f,40.f),ImGuiSetCond_FirstUseEver); 75 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 76 | if (Begin("Sliding bar example")){ 77 | ImGuiWindow* win = GetCurrentWindow(); 78 | static float xbar = 0.5f; 79 | ImVec2 pos, size; 80 | pos = win->Pos; 81 | size = win->Size; 82 | size.x = 8.f; 83 | pos.y += win->TitleBarHeight(); 84 | size.y -= win->TitleBarHeight(); 85 | 86 | float x0 = win->Pos.x; 87 | float x1 = win->Pos.x+win->Size.x-size.x; 88 | pos.x = x0 + xbar * (x1-x0); 89 | 90 | SlidingBar("slider", win, &pos, size, x0, x1, 1); 91 | xbar = (pos.x - x0 ) / (x1-x0); 92 | } 93 | End(); 94 | 95 | // ButtonWithX example 96 | SetNextWindowPos(ImVec2(270.f,40.f),ImGuiSetCond_FirstUseEver); 97 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 98 | if (Begin("Button with X example")){ 99 | bool p_open = true, dragged, dclicked, closeclicked; 100 | bool clicked = ButtonWithX("Click me!", ImVec2(80.f,20.f), false, &p_open, &dragged, &dclicked); 101 | if (clicked) 102 | printf("Clicked!\n"); 103 | if (!p_open) 104 | printf(">X< clicked!\n"); 105 | if (dragged) 106 | Text("Dragged!\n"); 107 | if (dclicked) 108 | printf("Double clicked!\n"); 109 | } 110 | End(); 111 | 112 | // ButtonWithX example 113 | SetNextWindowPos(ImVec2(520.f,40.f),ImGuiSetCond_FirstUseEver); 114 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 115 | if (Begin("Lift grip example")){ 116 | ImGuiWindow* win = GetCurrentWindow(); 117 | bool lifted = LiftGrip("Liftgrip", win); 118 | if (lifted) 119 | Text("Lifted!\n"); 120 | } 121 | End(); 122 | 123 | // ResizeGripOther example. 124 | SetNextWindowPos(ImVec2(20.f,290.f),ImGuiSetCond_FirstUseEver); 125 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 126 | if (Begin("Controlled window",nullptr,ImGuiWindowFlags_NoResize)) 127 | Text("Resize me!\n"); 128 | TextWrapped("Use this other window's grip ----->\n"); 129 | ImGuiWindow* win1 = GetCurrentWindow(); 130 | End(); 131 | 132 | // ResizeGripOther example. 133 | SetNextWindowPos(ImVec2(270.f,290.f),ImGuiSetCond_FirstUseEver); 134 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 135 | if (Begin("Parent window",nullptr,ImGuiWindowFlags_NoResize)){ 136 | ImGuiWindow* win2 = GetCurrentWindow(); 137 | TextWrapped("Use this grip to resize the left window.\n"); 138 | ResizeGripOther("resizegrip", win2, win1); 139 | } 140 | End(); 141 | 142 | // ToolTip example. 143 | SetNextWindowPos(ImVec2(520.f,290.f),ImGuiSetCond_FirstUseEver); 144 | SetNextWindowSize(ImVec2(200.f,200.f),ImGuiSetCond_FirstUseEver); 145 | if (Begin("Delayed tooltips example")){ 146 | const float delay = 1.5f; 147 | const float maxwidth = 450.f; 148 | 149 | ImGuiWindow* win2 = GetCurrentWindow(); 150 | PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f,0.f)); 151 | PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 0.5f, 0.f, 1.f)); 152 | PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.f, 1.f, 1.f)); 153 | Button("A",ImVec2(20.f,20.f)); SameLine(); 154 | AttachTooltip("A is for Apple",delay,maxwidth,io.FontDefault); 155 | Button("B",ImVec2(20.f,20.f)); SameLine(); 156 | AttachTooltip("B is for Ball",delay,maxwidth,io.FontDefault); 157 | Button("C",ImVec2(20.f,20.f)); 158 | AttachTooltip("C is for Cat",delay,maxwidth,io.FontDefault); 159 | Button("D",ImVec2(20.f,20.f)); SameLine(); 160 | AttachTooltip("D is for Dog",delay,maxwidth,io.FontDefault); 161 | Button("E",ImVec2(20.f,20.f)); SameLine(); 162 | AttachTooltip("E is for Elephant",delay,maxwidth,io.FontDefault); 163 | Button("F",ImVec2(20.f,20.f)); 164 | AttachTooltip("F is for Fish",delay,maxwidth,io.FontDefault); 165 | PopStyleColor(2); 166 | PopStyleVar(); 167 | } 168 | End(); 169 | 170 | // Draw the current scene 171 | int w, h; 172 | glfwGetFramebufferSize(rootwin,&w,&h); 173 | glViewport(0,0,w,h); 174 | glClearColor(1.0f,1.0f,1.0f,0.0f); 175 | glClear(GL_COLOR_BUFFER_BIT); 176 | 177 | // Render and swap 178 | Render(); 179 | glfwSwapBuffers(rootwin); 180 | } 181 | 182 | // Cleanup 183 | ShutdownDock(); 184 | ImGui_ImplGlfwGL2_Shutdown(); 185 | glfwTerminate(); 186 | 187 | return 0; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /images/dock_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aoterodelaroza/imgui-goodies/5d998850931a4bd2b312445e5eb4173de6d0b651/images/dock_example.gif -------------------------------------------------------------------------------- /images/ogv2gif.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Dawid Drozd 3 | # https://unix.stackexchange.com/questions/35282/convert-ogv-video-to-gif-animation 4 | 5 | inputFile=$1 6 | 7 | FPS=15 8 | WIDTH=320 9 | 10 | #Generate palette for better quality 11 | ffmpeg -i $inputFile -vf fps=$FPS,scale=$WIDTH:-1:flags=lanczos,palettegen tmp_palette.png 12 | 13 | #Generate gif using palette 14 | ffmpeg -i $inputFile -i tmp_palette.png -loop 0 -filter_complex "fps=$FPS,scale=$WIDTH:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gif 15 | 16 | rm tmp_palette.png 17 | -------------------------------------------------------------------------------- /images/tooltip_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aoterodelaroza/imgui-goodies/5d998850931a4bd2b312445e5eb4173de6d0b651/images/tooltip_example.gif -------------------------------------------------------------------------------- /imgui/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | RM=rm -f 3 | CPPFLAGS=-I./ $(shell pkg-config --cflags glfw3 glu gl) -Wall -Wformat -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++11 -w -g 4 | LDFLAGS=-g $(shell pkg-config --libs glfw3 glu gl) 5 | 6 | SRCS=imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_impl_glfw.cpp 7 | OBJS=$(subst .cpp,.o,$(SRCS)) 8 | 9 | all: .depend $(OBJS) 10 | @true 11 | 12 | .depend: $(SRCS) 13 | $(RM) ./.depend 14 | $(CXX) $(CPPFLAGS) -MM $^>>./.depend; 15 | 16 | clean: 17 | $(RM) $(OBJS) 18 | 19 | distclean: clean 20 | $(RM) *~ .depend 21 | 22 | include .depend 23 | -------------------------------------------------------------------------------- /imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define assertion handler. Defaults to calling assert(). 10 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 11 | 12 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 13 | //#define IMGUI_API __declspec( dllexport ) 14 | //#define IMGUI_API __declspec( dllimport ) 15 | 16 | //---- Include imgui_user.h at the end of imgui.h 17 | //#define IMGUI_INCLUDE_IMGUI_USER_H 18 | 19 | //---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 20 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS 21 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS 22 | 23 | //---- Don't implement test window functionality (ShowTestWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) 24 | //---- It is very strongly recommended to NOT disable the test windows. Please read the comment at the top of imgui_demo.cpp to learn why. 25 | //#define IMGUI_DISABLE_TEST_WINDOWS 26 | 27 | //---- Don't define obsolete functions names 28 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 29 | 30 | //---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) 31 | //#define IMGUI_USE_BGRA_PACKED_COLOR 32 | 33 | //---- Implement STB libraries in a namespace to avoid conflicts 34 | //#define IMGUI_STB_NAMESPACE ImGuiStb 35 | 36 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 37 | /* 38 | #define IM_VEC2_CLASS_EXTRA \ 39 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 40 | operator MyVec2() const { return MyVec2(x,y); } 41 | 42 | #define IM_VEC4_CLASS_EXTRA \ 43 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 44 | operator MyVec4() const { return MyVec4(x,y,z,w); } 45 | */ 46 | 47 | //---- Use 32-bit vertex indices (instead of default: 16-bit) to allow meshes with more than 64K vertices 48 | //#define ImDrawIdx unsigned int 49 | 50 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 51 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. 52 | /* 53 | namespace ImGui 54 | { 55 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); 56 | } 57 | */ 58 | 59 | -------------------------------------------------------------------------------- /imgui/imgui_impl_glfw.cpp: -------------------------------------------------------------------------------- 1 | // ImGui GLFW binding with OpenGL (legacy, fixed pipeline) 2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** 6 | // **Prefer using the code in the opengl3_example/ folder** 7 | // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. 8 | // If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more 9 | // complicated, will require your code to reset every single OpenGL attributes to their initial state, and might 10 | // confuse your GPU driver. 11 | // The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. 12 | 13 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 14 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 15 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 16 | // https://github.com/ocornut/imgui 17 | 18 | #include 19 | #include "imgui_impl_glfw.h" 20 | 21 | // GLFW 22 | #include 23 | #ifdef _WIN32 24 | #undef APIENTRY 25 | #define GLFW_EXPOSE_NATIVE_WIN32 26 | #define GLFW_EXPOSE_NATIVE_WGL 27 | #include 28 | #endif 29 | 30 | // Data 31 | static GLFWwindow* g_Window = NULL; 32 | static double g_Time = 0.0f; 33 | static bool g_MouseJustPressed[3] = { false, false, false }; 34 | static float g_MouseWheel = 0.0f; 35 | static GLuint g_FontTexture = 0; 36 | 37 | // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) 38 | void ImGui_ImplGlfwGL2_RenderDrawLists(ImDrawData* draw_data) 39 | // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. 40 | // If text or lines are blurry when integrating ImGui in your engine: in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) 41 | { 42 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 43 | ImGuiIO& io = ImGui::GetIO(); 44 | int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); 45 | int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); 46 | if (fb_width == 0 || fb_height == 0) 47 | return; 48 | draw_data->ScaleClipRects(io.DisplayFramebufferScale); 49 | 50 | // We are using the OpenGL fixed pipeline to make the example code simpler to read! 51 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. 52 | GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 53 | GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); 54 | GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); 55 | GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); 56 | glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); 57 | glEnable(GL_BLEND); 58 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 59 | glDisable(GL_CULL_FACE); 60 | glDisable(GL_DEPTH_TEST); 61 | glEnable(GL_SCISSOR_TEST); 62 | glEnableClientState(GL_VERTEX_ARRAY); 63 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 64 | glEnableClientState(GL_COLOR_ARRAY); 65 | glEnable(GL_TEXTURE_2D); 66 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 67 | //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound 68 | 69 | // Setup viewport, orthographic projection matrix 70 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); 71 | glMatrixMode(GL_PROJECTION); 72 | glPushMatrix(); 73 | glLoadIdentity(); 74 | glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); 75 | glMatrixMode(GL_MODELVIEW); 76 | glPushMatrix(); 77 | glLoadIdentity(); 78 | 79 | // Render command lists 80 | #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) 81 | for (int n = 0; n < draw_data->CmdListsCount; n++) 82 | { 83 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 84 | const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; 85 | const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; 86 | glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, pos))); 87 | glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, uv))); 88 | glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, col))); 89 | 90 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 91 | { 92 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 93 | if (pcmd->UserCallback) 94 | { 95 | pcmd->UserCallback(cmd_list, pcmd); 96 | } 97 | else 98 | { 99 | glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); 100 | glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); 101 | glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); 102 | } 103 | idx_buffer += pcmd->ElemCount; 104 | } 105 | } 106 | #undef OFFSETOF 107 | 108 | // Restore modified state 109 | glDisableClientState(GL_COLOR_ARRAY); 110 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 111 | glDisableClientState(GL_VERTEX_ARRAY); 112 | glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); 113 | glMatrixMode(GL_MODELVIEW); 114 | glPopMatrix(); 115 | glMatrixMode(GL_PROJECTION); 116 | glPopMatrix(); 117 | glPopAttrib(); 118 | glPolygonMode(GL_FRONT, last_polygon_mode[0]); glPolygonMode(GL_BACK, last_polygon_mode[1]); 119 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); 120 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); 121 | } 122 | 123 | static const char* ImGui_ImplGlfwGL2_GetClipboardText(void* user_data) 124 | { 125 | return glfwGetClipboardString((GLFWwindow*)user_data); 126 | } 127 | 128 | static void ImGui_ImplGlfwGL2_SetClipboardText(void* user_data, const char* text) 129 | { 130 | glfwSetClipboardString((GLFWwindow*)user_data, text); 131 | } 132 | 133 | void ImGui_ImplGlfwGL2_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) 134 | { 135 | if (action == GLFW_PRESS && button >= 0 && button < 3) 136 | g_MouseJustPressed[button] = true; 137 | } 138 | 139 | void ImGui_ImplGlfwGL2_ScrollCallback(GLFWwindow*, double /*xoffset*/, double yoffset) 140 | { 141 | g_MouseWheel += (float)yoffset; // Use fractional mouse wheel. 142 | } 143 | 144 | void ImGui_ImplGlfwGL2_KeyCallback(GLFWwindow*, int key, int, int action, int mods) 145 | { 146 | ImGuiIO& io = ImGui::GetIO(); 147 | if (action == GLFW_PRESS) 148 | io.KeysDown[key] = true; 149 | if (action == GLFW_RELEASE) 150 | io.KeysDown[key] = false; 151 | 152 | (void)mods; // Modifiers are not reliable across systems 153 | io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; 154 | io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; 155 | io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; 156 | io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; 157 | } 158 | 159 | void ImGui_ImplGlfwGL2_CharCallback(GLFWwindow*, unsigned int c) 160 | { 161 | ImGuiIO& io = ImGui::GetIO(); 162 | if (c > 0 && c < 0x10000) 163 | io.AddInputCharacter((unsigned short)c); 164 | } 165 | 166 | bool ImGui_ImplGlfwGL2_CreateDeviceObjects() 167 | { 168 | // Build texture atlas 169 | ImGuiIO& io = ImGui::GetIO(); 170 | unsigned char* pixels; 171 | int width, height; 172 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. 173 | 174 | // Upload texture to graphics system 175 | GLint last_texture; 176 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 177 | glGenTextures(1, &g_FontTexture); 178 | glBindTexture(GL_TEXTURE_2D, g_FontTexture); 179 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 180 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 181 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 182 | 183 | // Store our identifier 184 | io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; 185 | 186 | // Restore state 187 | glBindTexture(GL_TEXTURE_2D, last_texture); 188 | 189 | return true; 190 | } 191 | 192 | void ImGui_ImplGlfwGL2_InvalidateDeviceObjects() 193 | { 194 | if (g_FontTexture) 195 | { 196 | glDeleteTextures(1, &g_FontTexture); 197 | ImGui::GetIO().Fonts->TexID = 0; 198 | g_FontTexture = 0; 199 | } 200 | } 201 | 202 | bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks) 203 | { 204 | g_Window = window; 205 | 206 | ImGuiIO& io = ImGui::GetIO(); 207 | io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. 208 | io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; 209 | io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; 210 | io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; 211 | io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; 212 | io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; 213 | io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; 214 | io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; 215 | io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; 216 | io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; 217 | io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; 218 | io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; 219 | io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; 220 | io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; 221 | io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; 222 | io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; 223 | io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; 224 | io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; 225 | io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; 226 | 227 | io.RenderDrawListsFn = ImGui_ImplGlfwGL2_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. 228 | io.SetClipboardTextFn = ImGui_ImplGlfwGL2_SetClipboardText; 229 | io.GetClipboardTextFn = ImGui_ImplGlfwGL2_GetClipboardText; 230 | io.ClipboardUserData = g_Window; 231 | #ifdef _WIN32 232 | io.ImeWindowHandle = glfwGetWin32Window(g_Window); 233 | #endif 234 | 235 | if (install_callbacks) 236 | { 237 | glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL2_MouseButtonCallback); 238 | glfwSetScrollCallback(window, ImGui_ImplGlfwGL2_ScrollCallback); 239 | glfwSetKeyCallback(window, ImGui_ImplGlfwGL2_KeyCallback); 240 | glfwSetCharCallback(window, ImGui_ImplGlfwGL2_CharCallback); 241 | } 242 | 243 | return true; 244 | } 245 | 246 | void ImGui_ImplGlfwGL2_Shutdown() 247 | { 248 | ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); 249 | ImGui::Shutdown(); 250 | } 251 | 252 | void ImGui_ImplGlfwGL2_NewFrame() 253 | { 254 | if (!g_FontTexture) 255 | ImGui_ImplGlfwGL2_CreateDeviceObjects(); 256 | 257 | ImGuiIO& io = ImGui::GetIO(); 258 | 259 | // Setup display size (every frame to accommodate for window resizing) 260 | int w, h; 261 | int display_w, display_h; 262 | glfwGetWindowSize(g_Window, &w, &h); 263 | glfwGetFramebufferSize(g_Window, &display_w, &display_h); 264 | io.DisplaySize = ImVec2((float)w, (float)h); 265 | io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); 266 | 267 | // Setup time step 268 | double current_time = glfwGetTime(); 269 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); 270 | g_Time = current_time; 271 | 272 | // Setup inputs 273 | // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) 274 | if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) 275 | { 276 | if (io.WantMoveMouse) 277 | { 278 | glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) 279 | } 280 | else 281 | { 282 | double mouse_x, mouse_y; 283 | glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); 284 | io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Get mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) 285 | } 286 | } 287 | else 288 | { 289 | io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); 290 | } 291 | 292 | for (int i = 0; i < 3; i++) 293 | { 294 | // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. 295 | io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; 296 | g_MouseJustPressed[i] = false; 297 | } 298 | 299 | io.MouseWheel = g_MouseWheel; 300 | g_MouseWheel = 0.0f; 301 | 302 | // Hide OS mouse cursor if ImGui is drawing it 303 | glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); 304 | 305 | // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. 306 | ImGui::NewFrame(); 307 | } 308 | -------------------------------------------------------------------------------- /imgui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // ImGui GLFW binding with OpenGL (legacy, fixed pipeline) 2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | // (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** 6 | // **Prefer using the code in the opengl3_example/ folder** 7 | // See imgui_impl_glfw.cpp for details. 8 | 9 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 10 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 11 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 12 | // https://github.com/ocornut/imgui 13 | 14 | struct GLFWwindow; 15 | 16 | IMGUI_API bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks); 17 | IMGUI_API void ImGui_ImplGlfwGL2_Shutdown(); 18 | IMGUI_API void ImGui_ImplGlfwGL2_NewFrame(); 19 | 20 | // Use if you want to reset your rendering device without losing ImGui state. 21 | IMGUI_API void ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); 22 | IMGUI_API bool ImGui_ImplGlfwGL2_CreateDeviceObjects(); 23 | 24 | // GLFW callbacks (registered by default to GLFW if you enable 'install_callbacks' during initialization) 25 | // Provided here if you want to chain callbacks yourself. You may also handle inputs yourself and use those as a reference. 26 | IMGUI_API void ImGui_ImplGlfwGL2_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 27 | IMGUI_API void ImGui_ImplGlfwGL2_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 28 | IMGUI_API void ImGui_ImplGlfwGL2_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 29 | IMGUI_API void ImGui_ImplGlfwGL2_CharCallback(GLFWwindow* window, unsigned int c); 30 | -------------------------------------------------------------------------------- /imgui/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.10 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 36 | // 0.09 (2016-08-27) fix compiler warnings 37 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 38 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 39 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 40 | // 0.05: added STBRP_ASSERT to allow replacing assert 41 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 42 | // 0.01: initial release 43 | // 44 | // LICENSE 45 | // 46 | // This software is dual-licensed to the public domain and under the following 47 | // license: you are granted a perpetual, irrevocable license to copy, modify, 48 | // publish, and distribute this file as you see fit. 49 | 50 | ////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // INCLUDE SECTION 53 | // 54 | 55 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 56 | #define STB_INCLUDE_STB_RECT_PACK_H 57 | 58 | #define STB_RECT_PACK_VERSION 1 59 | 60 | #ifdef STBRP_STATIC 61 | #define STBRP_DEF static 62 | #else 63 | #define STBRP_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct stbrp_context stbrp_context; 71 | typedef struct stbrp_node stbrp_node; 72 | typedef struct stbrp_rect stbrp_rect; 73 | 74 | #ifdef STBRP_LARGE_RECTS 75 | typedef int stbrp_coord; 76 | #else 77 | typedef unsigned short stbrp_coord; 78 | #endif 79 | 80 | STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 81 | // Assign packed locations to rectangles. The rectangles are of type 82 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 83 | // are 'num_rects' many of them. 84 | // 85 | // Rectangles which are successfully packed have the 'was_packed' flag 86 | // set to a non-zero value and 'x' and 'y' store the minimum location 87 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 88 | // if you imagine y increasing downwards). Rectangles which do not fit 89 | // have the 'was_packed' flag set to 0. 90 | // 91 | // You should not try to access the 'rects' array from another thread 92 | // while this function is running, as the function temporarily reorders 93 | // the array while it executes. 94 | // 95 | // To pack into another rectangle, you need to call stbrp_init_target 96 | // again. To continue packing into the same rectangle, you can call 97 | // this function again. Calling this multiple times with multiple rect 98 | // arrays will probably produce worse packing results than calling it 99 | // a single time with the full rectangle array, but the option is 100 | // available. 101 | 102 | struct stbrp_rect 103 | { 104 | // reserved for your use: 105 | int id; 106 | 107 | // input: 108 | stbrp_coord w, h; 109 | 110 | // output: 111 | stbrp_coord x, y; 112 | int was_packed; // non-zero if valid packing 113 | 114 | }; // 16 bytes, nominally 115 | 116 | 117 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 118 | // Initialize a rectangle packer to: 119 | // pack a rectangle that is 'width' by 'height' in dimensions 120 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 121 | // 122 | // You must call this function every time you start packing into a new target. 123 | // 124 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 125 | // the following stbrp_pack_rects() call (or calls), but can be freed after 126 | // the call (or calls) finish. 127 | // 128 | // Note: to guarantee best results, either: 129 | // 1. make sure 'num_nodes' >= 'width' 130 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 131 | // 132 | // If you don't do either of the above things, widths will be quantized to multiples 133 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 134 | // 135 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 136 | // may run out of temporary storage and be unable to pack some rectangles. 137 | 138 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 139 | // Optionally call this function after init but before doing any packing to 140 | // change the handling of the out-of-temp-memory scenario, described above. 141 | // If you call init again, this will be reset to the default (false). 142 | 143 | 144 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 145 | // Optionally select which packing heuristic the library should use. Different 146 | // heuristics will produce better/worse results for different data sets. 147 | // If you call init again, this will be reset to the default. 148 | 149 | enum 150 | { 151 | STBRP_HEURISTIC_Skyline_default=0, 152 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 153 | STBRP_HEURISTIC_Skyline_BF_sortHeight 154 | }; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // the details of the following structures don't matter to you, but they must 160 | // be visible so you can handle the memory allocations for them 161 | 162 | struct stbrp_node 163 | { 164 | stbrp_coord x,y; 165 | stbrp_node *next; 166 | }; 167 | 168 | struct stbrp_context 169 | { 170 | int width; 171 | int height; 172 | int align; 173 | int init_mode; 174 | int heuristic; 175 | int num_nodes; 176 | stbrp_node *active_head; 177 | stbrp_node *free_head; 178 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 179 | }; 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | ////////////////////////////////////////////////////////////////////////////// 188 | // 189 | // IMPLEMENTATION SECTION 190 | // 191 | 192 | #ifdef STB_RECT_PACK_IMPLEMENTATION 193 | #ifndef STBRP_SORT 194 | #include 195 | #define STBRP_SORT qsort 196 | #endif 197 | 198 | #ifndef STBRP_ASSERT 199 | #include 200 | #define STBRP_ASSERT assert 201 | #endif 202 | 203 | #ifdef _MSC_VER 204 | #define STBRP__NOTUSED(v) (void)(v) 205 | #else 206 | #define STBRP__NOTUSED(v) (void)sizeof(v) 207 | #endif 208 | 209 | enum 210 | { 211 | STBRP__INIT_skyline = 1 212 | }; 213 | 214 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 215 | { 216 | switch (context->init_mode) { 217 | case STBRP__INIT_skyline: 218 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 219 | context->heuristic = heuristic; 220 | break; 221 | default: 222 | STBRP_ASSERT(0); 223 | } 224 | } 225 | 226 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 227 | { 228 | if (allow_out_of_mem) 229 | // if it's ok to run out of memory, then don't bother aligning them; 230 | // this gives better packing, but may fail due to OOM (even though 231 | // the rectangles easily fit). @TODO a smarter approach would be to only 232 | // quantize once we've hit OOM, then we could get rid of this parameter. 233 | context->align = 1; 234 | else { 235 | // if it's not ok to run out of memory, then quantize the widths 236 | // so that num_nodes is always enough nodes. 237 | // 238 | // I.e. num_nodes * align >= width 239 | // align >= width / num_nodes 240 | // align = ceil(width/num_nodes) 241 | 242 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 243 | } 244 | } 245 | 246 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 247 | { 248 | int i; 249 | #ifndef STBRP_LARGE_RECTS 250 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 251 | #endif 252 | 253 | for (i=0; i < num_nodes-1; ++i) 254 | nodes[i].next = &nodes[i+1]; 255 | nodes[i].next = NULL; 256 | context->init_mode = STBRP__INIT_skyline; 257 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 258 | context->free_head = &nodes[0]; 259 | context->active_head = &context->extra[0]; 260 | context->width = width; 261 | context->height = height; 262 | context->num_nodes = num_nodes; 263 | stbrp_setup_allow_out_of_mem(context, 0); 264 | 265 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 266 | context->extra[0].x = 0; 267 | context->extra[0].y = 0; 268 | context->extra[0].next = &context->extra[1]; 269 | context->extra[1].x = (stbrp_coord) width; 270 | #ifdef STBRP_LARGE_RECTS 271 | context->extra[1].y = (1<<30); 272 | #else 273 | context->extra[1].y = 65535; 274 | #endif 275 | context->extra[1].next = NULL; 276 | } 277 | 278 | // find minimum y position if it starts at x1 279 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 280 | { 281 | stbrp_node *node = first; 282 | int x1 = x0 + width; 283 | int min_y, visited_width, waste_area; 284 | 285 | STBRP__NOTUSED(c); 286 | 287 | STBRP_ASSERT(first->x <= x0); 288 | 289 | #if 0 290 | // skip in case we're past the node 291 | while (node->next->x <= x0) 292 | ++node; 293 | #else 294 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 295 | #endif 296 | 297 | STBRP_ASSERT(node->x <= x0); 298 | 299 | min_y = 0; 300 | waste_area = 0; 301 | visited_width = 0; 302 | while (node->x < x1) { 303 | if (node->y > min_y) { 304 | // raise min_y higher. 305 | // we've accounted for all waste up to min_y, 306 | // but we'll now add more waste for everything we've visted 307 | waste_area += visited_width * (node->y - min_y); 308 | min_y = node->y; 309 | // the first time through, visited_width might be reduced 310 | if (node->x < x0) 311 | visited_width += node->next->x - x0; 312 | else 313 | visited_width += node->next->x - node->x; 314 | } else { 315 | // add waste area 316 | int under_width = node->next->x - node->x; 317 | if (under_width + visited_width > width) 318 | under_width = width - visited_width; 319 | waste_area += under_width * (min_y - node->y); 320 | visited_width += under_width; 321 | } 322 | node = node->next; 323 | } 324 | 325 | *pwaste = waste_area; 326 | return min_y; 327 | } 328 | 329 | typedef struct 330 | { 331 | int x,y; 332 | stbrp_node **prev_link; 333 | } stbrp__findresult; 334 | 335 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 336 | { 337 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 338 | stbrp__findresult fr; 339 | stbrp_node **prev, *node, *tail, **best = NULL; 340 | 341 | // align to multiple of c->align 342 | width = (width + c->align - 1); 343 | width -= width % c->align; 344 | STBRP_ASSERT(width % c->align == 0); 345 | 346 | node = c->active_head; 347 | prev = &c->active_head; 348 | while (node->x + width <= c->width) { 349 | int y,waste; 350 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 351 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 352 | // bottom left 353 | if (y < best_y) { 354 | best_y = y; 355 | best = prev; 356 | } 357 | } else { 358 | // best-fit 359 | if (y + height <= c->height) { 360 | // can only use it if it first vertically 361 | if (y < best_y || (y == best_y && waste < best_waste)) { 362 | best_y = y; 363 | best_waste = waste; 364 | best = prev; 365 | } 366 | } 367 | } 368 | prev = &node->next; 369 | node = node->next; 370 | } 371 | 372 | best_x = (best == NULL) ? 0 : (*best)->x; 373 | 374 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 375 | // 376 | // e.g, if fitting 377 | // 378 | // ____________________ 379 | // |____________________| 380 | // 381 | // into 382 | // 383 | // | | 384 | // | ____________| 385 | // |____________| 386 | // 387 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 388 | // 389 | // This makes BF take about 2x the time 390 | 391 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 392 | tail = c->active_head; 393 | node = c->active_head; 394 | prev = &c->active_head; 395 | // find first node that's admissible 396 | while (tail->x < width) 397 | tail = tail->next; 398 | while (tail) { 399 | int xpos = tail->x - width; 400 | int y,waste; 401 | STBRP_ASSERT(xpos >= 0); 402 | // find the left position that matches this 403 | while (node->next->x <= xpos) { 404 | prev = &node->next; 405 | node = node->next; 406 | } 407 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 408 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 409 | if (y + height < c->height) { 410 | if (y <= best_y) { 411 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 412 | best_x = xpos; 413 | STBRP_ASSERT(y <= best_y); 414 | best_y = y; 415 | best_waste = waste; 416 | best = prev; 417 | } 418 | } 419 | } 420 | tail = tail->next; 421 | } 422 | } 423 | 424 | fr.prev_link = best; 425 | fr.x = best_x; 426 | fr.y = best_y; 427 | return fr; 428 | } 429 | 430 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 431 | { 432 | // find best position according to heuristic 433 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 434 | stbrp_node *node, *cur; 435 | 436 | // bail if: 437 | // 1. it failed 438 | // 2. the best node doesn't fit (we don't always check this) 439 | // 3. we're out of memory 440 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 441 | res.prev_link = NULL; 442 | return res; 443 | } 444 | 445 | // on success, create new node 446 | node = context->free_head; 447 | node->x = (stbrp_coord) res.x; 448 | node->y = (stbrp_coord) (res.y + height); 449 | 450 | context->free_head = node->next; 451 | 452 | // insert the new node into the right starting point, and 453 | // let 'cur' point to the remaining nodes needing to be 454 | // stiched back in 455 | 456 | cur = *res.prev_link; 457 | if (cur->x < res.x) { 458 | // preserve the existing one, so start testing with the next one 459 | stbrp_node *next = cur->next; 460 | cur->next = node; 461 | cur = next; 462 | } else { 463 | *res.prev_link = node; 464 | } 465 | 466 | // from here, traverse cur and free the nodes, until we get to one 467 | // that shouldn't be freed 468 | while (cur->next && cur->next->x <= res.x + width) { 469 | stbrp_node *next = cur->next; 470 | // move the current node to the free list 471 | cur->next = context->free_head; 472 | context->free_head = cur; 473 | cur = next; 474 | } 475 | 476 | // stitch the list back in 477 | node->next = cur; 478 | 479 | if (cur->x < res.x + width) 480 | cur->x = (stbrp_coord) (res.x + width); 481 | 482 | #ifdef _DEBUG 483 | cur = context->active_head; 484 | while (cur->x < context->width) { 485 | STBRP_ASSERT(cur->x < cur->next->x); 486 | cur = cur->next; 487 | } 488 | STBRP_ASSERT(cur->next == NULL); 489 | 490 | { 491 | stbrp_node *L1 = NULL, *L2 = NULL; 492 | int count=0; 493 | cur = context->active_head; 494 | while (cur) { 495 | L1 = cur; 496 | cur = cur->next; 497 | ++count; 498 | } 499 | cur = context->free_head; 500 | while (cur) { 501 | L2 = cur; 502 | cur = cur->next; 503 | ++count; 504 | } 505 | STBRP_ASSERT(count == context->num_nodes+2); 506 | } 507 | #endif 508 | 509 | return res; 510 | } 511 | 512 | static int rect_height_compare(const void *a, const void *b) 513 | { 514 | const stbrp_rect *p = (const stbrp_rect *) a; 515 | const stbrp_rect *q = (const stbrp_rect *) b; 516 | if (p->h > q->h) 517 | return -1; 518 | if (p->h < q->h) 519 | return 1; 520 | return (p->w > q->w) ? -1 : (p->w < q->w); 521 | } 522 | 523 | static int rect_width_compare(const void *a, const void *b) 524 | { 525 | const stbrp_rect *p = (const stbrp_rect *) a; 526 | const stbrp_rect *q = (const stbrp_rect *) b; 527 | if (p->w > q->w) 528 | return -1; 529 | if (p->w < q->w) 530 | return 1; 531 | return (p->h > q->h) ? -1 : (p->h < q->h); 532 | } 533 | 534 | static int rect_original_order(const void *a, const void *b) 535 | { 536 | const stbrp_rect *p = (const stbrp_rect *) a; 537 | const stbrp_rect *q = (const stbrp_rect *) b; 538 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 539 | } 540 | 541 | #ifdef STBRP_LARGE_RECTS 542 | #define STBRP__MAXVAL 0xffffffff 543 | #else 544 | #define STBRP__MAXVAL 0xffff 545 | #endif 546 | 547 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 548 | { 549 | int i; 550 | 551 | // we use the 'was_packed' field internally to allow sorting/unsorting 552 | for (i=0; i < num_rects; ++i) { 553 | rects[i].was_packed = i; 554 | #ifndef STBRP_LARGE_RECTS 555 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 556 | #endif 557 | } 558 | 559 | // sort according to heuristic 560 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 561 | 562 | for (i=0; i < num_rects; ++i) { 563 | if (rects[i].w == 0 || rects[i].h == 0) { 564 | rects[i].x = rects[i].y = 0; // empty rect needs no space 565 | } else { 566 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 567 | if (fr.prev_link) { 568 | rects[i].x = (stbrp_coord) fr.x; 569 | rects[i].y = (stbrp_coord) fr.y; 570 | } else { 571 | rects[i].x = rects[i].y = STBRP__MAXVAL; 572 | } 573 | } 574 | } 575 | 576 | // unsort 577 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 578 | 579 | // set was_packed flags 580 | for (i=0; i < num_rects; ++i) 581 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 582 | } 583 | #endif 584 | -------------------------------------------------------------------------------- /imgui/stb_textedit.h: -------------------------------------------------------------------------------- 1 | // [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb 2 | // [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815) 3 | // [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715) 4 | // [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) 5 | // [ImGui] - fixed some minor warnings 6 | 7 | // stb_textedit.h - v1.9 - public domain - Sean Barrett 8 | // Development of this library was sponsored by RAD Game Tools 9 | // 10 | // This C header file implements the guts of a multi-line text-editing 11 | // widget; you implement display, word-wrapping, and low-level string 12 | // insertion/deletion, and stb_textedit will map user inputs into 13 | // insertions & deletions, plus updates to the cursor position, 14 | // selection state, and undo state. 15 | // 16 | // It is intended for use in games and other systems that need to build 17 | // their own custom widgets and which do not have heavy text-editing 18 | // requirements (this library is not recommended for use for editing large 19 | // texts, as its performance does not scale and it has limited undo). 20 | // 21 | // Non-trivial behaviors are modelled after Windows text controls. 22 | // 23 | // 24 | // LICENSE 25 | // 26 | // This software is dual-licensed to the public domain and under the following 27 | // license: you are granted a perpetual, irrevocable license to copy, modify, 28 | // publish, and distribute this file as you see fit. 29 | // 30 | // 31 | // DEPENDENCIES 32 | // 33 | // Uses the C runtime function 'memmove', which you can override 34 | // by defining STB_TEXTEDIT_memmove before the implementation. 35 | // Uses no other functions. Performs no runtime allocations. 36 | // 37 | // 38 | // VERSION HISTORY 39 | // 40 | // 1.9 (2016-08-27) customizable move-by-word 41 | // 1.8 (2016-04-02) better keyboard handling when mouse button is down 42 | // 1.7 (2015-09-13) change y range handling in case baseline is non-0 43 | // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 44 | // 1.5 (2014-09-10) add support for secondary keys for OS X 45 | // 1.4 (2014-08-17) fix signed/unsigned warnings 46 | // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 47 | // 1.2 (2014-05-27) fix some RAD types that had crept into the new code 48 | // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 49 | // 1.0 (2012-07-26) improve documentation, initial public release 50 | // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 51 | // 0.2 (2011-11-28) fixes to undo/redo 52 | // 0.1 (2010-07-08) initial version 53 | // 54 | // ADDITIONAL CONTRIBUTORS 55 | // 56 | // Ulf Winklemann: move-by-word in 1.1 57 | // Fabian Giesen: secondary key inputs in 1.5 58 | // Martins Mozeiko: STB_TEXTEDIT_memmove 59 | // 60 | // Bugfixes: 61 | // Scott Graham 62 | // Daniel Keller 63 | // Omar Cornut 64 | // 65 | // USAGE 66 | // 67 | // This file behaves differently depending on what symbols you define 68 | // before including it. 69 | // 70 | // 71 | // Header-file mode: 72 | // 73 | // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 74 | // it will operate in "header file" mode. In this mode, it declares a 75 | // single public symbol, STB_TexteditState, which encapsulates the current 76 | // state of a text widget (except for the string, which you will store 77 | // separately). 78 | // 79 | // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 80 | // primitive type that defines a single character (e.g. char, wchar_t, etc). 81 | // 82 | // To save space or increase undo-ability, you can optionally define the 83 | // following things that are used by the undo system: 84 | // 85 | // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 86 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 87 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 88 | // 89 | // If you don't define these, they are set to permissive types and 90 | // moderate sizes. The undo system does no memory allocations, so 91 | // it grows STB_TexteditState by the worst-case storage which is (in bytes): 92 | // 93 | // [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT 94 | // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT 95 | // 96 | // 97 | // Implementation mode: 98 | // 99 | // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 100 | // will compile the implementation of the text edit widget, depending 101 | // on a large number of symbols which must be defined before the include. 102 | // 103 | // The implementation is defined only as static functions. You will then 104 | // need to provide your own APIs in the same file which will access the 105 | // static functions. 106 | // 107 | // The basic concept is that you provide a "string" object which 108 | // behaves like an array of characters. stb_textedit uses indices to 109 | // refer to positions in the string, implicitly representing positions 110 | // in the displayed textedit. This is true for both plain text and 111 | // rich text; even with rich text stb_truetype interacts with your 112 | // code as if there was an array of all the displayed characters. 113 | // 114 | // Symbols that must be the same in header-file and implementation mode: 115 | // 116 | // STB_TEXTEDIT_CHARTYPE the character type 117 | // STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position 118 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 119 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 120 | // 121 | // Symbols you must define for implementation mode: 122 | // 123 | // STB_TEXTEDIT_STRING the type of object representing a string being edited, 124 | // typically this is a wrapper object with other data you need 125 | // 126 | // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 127 | // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 128 | // starting from character #n (see discussion below) 129 | // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 130 | // to the xpos of the i+1'th char for a line of characters 131 | // starting at character #n (i.e. accounts for kerning 132 | // with previous char) 133 | // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 134 | // (return type is int, -1 means not valid to insert) 135 | // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 136 | // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 137 | // as manually wordwrapping for end-of-line positioning 138 | // 139 | // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 140 | // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 141 | // 142 | // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 143 | // 144 | // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 145 | // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 146 | // STB_TEXTEDIT_K_UP keyboard input to move cursor up 147 | // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 148 | // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 149 | // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 150 | // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 151 | // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 152 | // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 153 | // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 154 | // STB_TEXTEDIT_K_UNDO keyboard input to perform undo 155 | // STB_TEXTEDIT_K_REDO keyboard input to perform redo 156 | // 157 | // Optional: 158 | // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 159 | // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 160 | // required for default WORDLEFT/WORDRIGHT handlers 161 | // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 162 | // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 163 | // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 164 | // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 165 | // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 166 | // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 167 | // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 168 | // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 169 | // 170 | // Todo: 171 | // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 172 | // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 173 | // 174 | // Keyboard input must be encoded as a single integer value; e.g. a character code 175 | // and some bitflags that represent shift states. to simplify the interface, SHIFT must 176 | // be a bitflag, so we can test the shifted state of cursor movements to allow selection, 177 | // i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 178 | // 179 | // You can encode other things, such as CONTROL or ALT, in additional bits, and 180 | // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 181 | // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 182 | // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 183 | // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 184 | // API below. The control keys will only match WM_KEYDOWN events because of the 185 | // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 186 | // bit so it only decodes WM_CHAR events. 187 | // 188 | // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 189 | // row of characters assuming they start on the i'th character--the width and 190 | // the height and the number of characters consumed. This allows this library 191 | // to traverse the entire layout incrementally. You need to compute word-wrapping 192 | // here. 193 | // 194 | // Each textfield keeps its own insert mode state, which is not how normal 195 | // applications work. To keep an app-wide insert mode, update/copy the 196 | // "insert_mode" field of STB_TexteditState before/after calling API functions. 197 | // 198 | // API 199 | // 200 | // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 201 | // 202 | // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 203 | // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 204 | // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 205 | // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 206 | // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 207 | // 208 | // Each of these functions potentially updates the string and updates the 209 | // state. 210 | // 211 | // initialize_state: 212 | // set the textedit state to a known good default state when initially 213 | // constructing the textedit. 214 | // 215 | // click: 216 | // call this with the mouse x,y on a mouse down; it will update the cursor 217 | // and reset the selection start/end to the cursor point. the x,y must 218 | // be relative to the text widget, with (0,0) being the top left. 219 | // 220 | // drag: 221 | // call this with the mouse x,y on a mouse drag/up; it will update the 222 | // cursor and the selection end point 223 | // 224 | // cut: 225 | // call this to delete the current selection; returns true if there was 226 | // one. you should FIRST copy the current selection to the system paste buffer. 227 | // (To copy, just copy the current selection out of the string yourself.) 228 | // 229 | // paste: 230 | // call this to paste text at the current cursor point or over the current 231 | // selection if there is one. 232 | // 233 | // key: 234 | // call this for keyboard inputs sent to the textfield. you can use it 235 | // for "key down" events or for "translated" key events. if you need to 236 | // do both (as in Win32), or distinguish Unicode characters from control 237 | // inputs, set a high bit to distinguish the two; then you can define the 238 | // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 239 | // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 240 | // clear. 241 | // 242 | // When rendering, you can read the cursor position and selection state from 243 | // the STB_TexteditState. 244 | // 245 | // 246 | // Notes: 247 | // 248 | // This is designed to be usable in IMGUI, so it allows for the possibility of 249 | // running in an IMGUI that has NOT cached the multi-line layout. For this 250 | // reason, it provides an interface that is compatible with computing the 251 | // layout incrementally--we try to make sure we make as few passes through 252 | // as possible. (For example, to locate the mouse pointer in the text, we 253 | // could define functions that return the X and Y positions of characters 254 | // and binary search Y and then X, but if we're doing dynamic layout this 255 | // will run the layout algorithm many times, so instead we manually search 256 | // forward in one pass. Similar logic applies to e.g. up-arrow and 257 | // down-arrow movement.) 258 | // 259 | // If it's run in a widget that *has* cached the layout, then this is less 260 | // efficient, but it's not horrible on modern computers. But you wouldn't 261 | // want to edit million-line files with it. 262 | 263 | 264 | //////////////////////////////////////////////////////////////////////////// 265 | //////////////////////////////////////////////////////////////////////////// 266 | //// 267 | //// Header-file mode 268 | //// 269 | //// 270 | 271 | #ifndef INCLUDE_STB_TEXTEDIT_H 272 | #define INCLUDE_STB_TEXTEDIT_H 273 | 274 | //////////////////////////////////////////////////////////////////////// 275 | // 276 | // STB_TexteditState 277 | // 278 | // Definition of STB_TexteditState which you should store 279 | // per-textfield; it includes cursor position, selection state, 280 | // and undo state. 281 | // 282 | 283 | #ifndef STB_TEXTEDIT_UNDOSTATECOUNT 284 | #define STB_TEXTEDIT_UNDOSTATECOUNT 99 285 | #endif 286 | #ifndef STB_TEXTEDIT_UNDOCHARCOUNT 287 | #define STB_TEXTEDIT_UNDOCHARCOUNT 999 288 | #endif 289 | #ifndef STB_TEXTEDIT_CHARTYPE 290 | #define STB_TEXTEDIT_CHARTYPE int 291 | #endif 292 | #ifndef STB_TEXTEDIT_POSITIONTYPE 293 | #define STB_TEXTEDIT_POSITIONTYPE int 294 | #endif 295 | 296 | typedef struct 297 | { 298 | // private data 299 | STB_TEXTEDIT_POSITIONTYPE where; 300 | short insert_length; 301 | short delete_length; 302 | short char_storage; 303 | } StbUndoRecord; 304 | 305 | typedef struct 306 | { 307 | // private data 308 | StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; 309 | STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; 310 | short undo_point, redo_point; 311 | short undo_char_point, redo_char_point; 312 | } StbUndoState; 313 | 314 | typedef struct 315 | { 316 | ///////////////////// 317 | // 318 | // public data 319 | // 320 | 321 | int cursor; 322 | // position of the text cursor within the string 323 | 324 | int select_start; // selection start point 325 | int select_end; 326 | // selection start and end point in characters; if equal, no selection. 327 | // note that start may be less than or greater than end (e.g. when 328 | // dragging the mouse, start is where the initial click was, and you 329 | // can drag in either direction) 330 | 331 | unsigned char insert_mode; 332 | // each textfield keeps its own insert mode state. to keep an app-wide 333 | // insert mode, copy this value in/out of the app state 334 | 335 | ///////////////////// 336 | // 337 | // private data 338 | // 339 | unsigned char cursor_at_end_of_line; // not implemented yet 340 | unsigned char initialized; 341 | unsigned char has_preferred_x; 342 | unsigned char single_line; 343 | unsigned char padding1, padding2, padding3; 344 | float preferred_x; // this determines where the cursor up/down tries to seek to along x 345 | StbUndoState undostate; 346 | } STB_TexteditState; 347 | 348 | 349 | //////////////////////////////////////////////////////////////////////// 350 | // 351 | // StbTexteditRow 352 | // 353 | // Result of layout query, used by stb_textedit to determine where 354 | // the text in each row is. 355 | 356 | // result of layout query 357 | typedef struct 358 | { 359 | float x0,x1; // starting x location, end x location (allows for align=right, etc) 360 | float baseline_y_delta; // position of baseline relative to previous row's baseline 361 | float ymin,ymax; // height of row above and below baseline 362 | int num_chars; 363 | } StbTexteditRow; 364 | #endif //INCLUDE_STB_TEXTEDIT_H 365 | 366 | 367 | //////////////////////////////////////////////////////////////////////////// 368 | //////////////////////////////////////////////////////////////////////////// 369 | //// 370 | //// Implementation mode 371 | //// 372 | //// 373 | 374 | 375 | // implementation isn't include-guarded, since it might have indirectly 376 | // included just the "header" portion 377 | #ifdef STB_TEXTEDIT_IMPLEMENTATION 378 | 379 | #ifndef STB_TEXTEDIT_memmove 380 | #include 381 | #define STB_TEXTEDIT_memmove memmove 382 | #endif 383 | 384 | 385 | ///////////////////////////////////////////////////////////////////////////// 386 | // 387 | // Mouse input handling 388 | // 389 | 390 | // traverse the layout to locate the nearest character to a display position 391 | static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) 392 | { 393 | StbTexteditRow r; 394 | int n = STB_TEXTEDIT_STRINGLEN(str); 395 | float base_y = 0, prev_x; 396 | int i=0, k; 397 | 398 | r.x0 = r.x1 = 0; 399 | r.ymin = r.ymax = 0; 400 | r.num_chars = 0; 401 | 402 | // search rows to find one that straddles 'y' 403 | while (i < n) { 404 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 405 | if (r.num_chars <= 0) 406 | return n; 407 | 408 | if (i==0 && y < base_y + r.ymin) 409 | return 0; 410 | 411 | if (y < base_y + r.ymax) 412 | break; 413 | 414 | i += r.num_chars; 415 | base_y += r.baseline_y_delta; 416 | } 417 | 418 | // below all text, return 'after' last character 419 | if (i >= n) 420 | return n; 421 | 422 | // check if it's before the beginning of the line 423 | if (x < r.x0) 424 | return i; 425 | 426 | // check if it's before the end of the line 427 | if (x < r.x1) { 428 | // search characters in row for one that straddles 'x' 429 | prev_x = r.x0; 430 | for (k=0; k < r.num_chars; ++k) { 431 | float w = STB_TEXTEDIT_GETWIDTH(str, i, k); 432 | if (x < prev_x+w) { 433 | if (x < prev_x+w/2) 434 | return k+i; 435 | else 436 | return k+i+1; 437 | } 438 | prev_x += w; 439 | } 440 | // shouldn't happen, but if it does, fall through to end-of-line case 441 | } 442 | 443 | // if the last character is a newline, return that. otherwise return 'after' the last character 444 | if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) 445 | return i+r.num_chars-1; 446 | else 447 | return i+r.num_chars; 448 | } 449 | 450 | // API click: on mouse down, move the cursor to the clicked location, and reset the selection 451 | static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 452 | { 453 | state->cursor = stb_text_locate_coord(str, x, y); 454 | state->select_start = state->cursor; 455 | state->select_end = state->cursor; 456 | state->has_preferred_x = 0; 457 | } 458 | 459 | // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 460 | static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 461 | { 462 | int p = stb_text_locate_coord(str, x, y); 463 | if (state->select_start == state->select_end) 464 | state->select_start = state->cursor; 465 | state->cursor = state->select_end = p; 466 | } 467 | 468 | ///////////////////////////////////////////////////////////////////////////// 469 | // 470 | // Keyboard input handling 471 | // 472 | 473 | // forward declarations 474 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 475 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 476 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 477 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 478 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 479 | 480 | typedef struct 481 | { 482 | float x,y; // position of n'th character 483 | float height; // height of line 484 | int first_char, length; // first char of row, and length 485 | int prev_first; // first char of previous row 486 | } StbFindState; 487 | 488 | // find the x/y location of a character, and remember info about the previous row in 489 | // case we get a move-up event (for page up, we'll have to rescan) 490 | static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) 491 | { 492 | StbTexteditRow r; 493 | int prev_start = 0; 494 | int z = STB_TEXTEDIT_STRINGLEN(str); 495 | int i=0, first; 496 | 497 | if (n == z) { 498 | // if it's at the end, then find the last line -- simpler than trying to 499 | // explicitly handle this case in the regular code 500 | if (single_line) { 501 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 502 | find->y = 0; 503 | find->first_char = 0; 504 | find->length = z; 505 | find->height = r.ymax - r.ymin; 506 | find->x = r.x1; 507 | } else { 508 | find->y = 0; 509 | find->x = 0; 510 | find->height = 1; 511 | while (i < z) { 512 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 513 | prev_start = i; 514 | i += r.num_chars; 515 | } 516 | find->first_char = i; 517 | find->length = 0; 518 | find->prev_first = prev_start; 519 | } 520 | return; 521 | } 522 | 523 | // search rows to find the one that straddles character n 524 | find->y = 0; 525 | 526 | for(;;) { 527 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 528 | if (n < i + r.num_chars) 529 | break; 530 | prev_start = i; 531 | i += r.num_chars; 532 | find->y += r.baseline_y_delta; 533 | } 534 | 535 | find->first_char = first = i; 536 | find->length = r.num_chars; 537 | find->height = r.ymax - r.ymin; 538 | find->prev_first = prev_start; 539 | 540 | // now scan to find xpos 541 | find->x = r.x0; 542 | i = 0; 543 | for (i=0; first+i < n; ++i) 544 | find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 545 | } 546 | 547 | #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 548 | 549 | // make the selection/cursor state valid if client altered the string 550 | static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 551 | { 552 | int n = STB_TEXTEDIT_STRINGLEN(str); 553 | if (STB_TEXT_HAS_SELECTION(state)) { 554 | if (state->select_start > n) state->select_start = n; 555 | if (state->select_end > n) state->select_end = n; 556 | // if clamping forced them to be equal, move the cursor to match 557 | if (state->select_start == state->select_end) 558 | state->cursor = state->select_start; 559 | } 560 | if (state->cursor > n) state->cursor = n; 561 | } 562 | 563 | // delete characters while updating undo 564 | static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 565 | { 566 | stb_text_makeundo_delete(str, state, where, len); 567 | STB_TEXTEDIT_DELETECHARS(str, where, len); 568 | state->has_preferred_x = 0; 569 | } 570 | 571 | // delete the section 572 | static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 573 | { 574 | stb_textedit_clamp(str, state); 575 | if (STB_TEXT_HAS_SELECTION(state)) { 576 | if (state->select_start < state->select_end) { 577 | stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 578 | state->select_end = state->cursor = state->select_start; 579 | } else { 580 | stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 581 | state->select_start = state->cursor = state->select_end; 582 | } 583 | state->has_preferred_x = 0; 584 | } 585 | } 586 | 587 | // canoncialize the selection so start <= end 588 | static void stb_textedit_sortselection(STB_TexteditState *state) 589 | { 590 | if (state->select_end < state->select_start) { 591 | int temp = state->select_end; 592 | state->select_end = state->select_start; 593 | state->select_start = temp; 594 | } 595 | } 596 | 597 | // move cursor to first character of selection 598 | static void stb_textedit_move_to_first(STB_TexteditState *state) 599 | { 600 | if (STB_TEXT_HAS_SELECTION(state)) { 601 | stb_textedit_sortselection(state); 602 | state->cursor = state->select_start; 603 | state->select_end = state->select_start; 604 | state->has_preferred_x = 0; 605 | } 606 | } 607 | 608 | // move cursor to last character of selection 609 | static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 610 | { 611 | if (STB_TEXT_HAS_SELECTION(state)) { 612 | stb_textedit_sortselection(state); 613 | stb_textedit_clamp(str, state); 614 | state->cursor = state->select_end; 615 | state->select_start = state->select_end; 616 | state->has_preferred_x = 0; 617 | } 618 | } 619 | 620 | #ifdef STB_TEXTEDIT_IS_SPACE 621 | static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) 622 | { 623 | return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; 624 | } 625 | 626 | #ifndef STB_TEXTEDIT_MOVEWORDLEFT 627 | static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) 628 | { 629 | --c; // always move at least one character 630 | while( c >= 0 && !is_word_boundary( str, c ) ) 631 | --c; 632 | 633 | if( c < 0 ) 634 | c = 0; 635 | 636 | return c; 637 | } 638 | #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 639 | #endif 640 | 641 | #ifndef STB_TEXTEDIT_MOVEWORDRIGHT 642 | static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) 643 | { 644 | const int len = STB_TEXTEDIT_STRINGLEN(str); 645 | ++c; // always move at least one character 646 | while( c < len && !is_word_boundary( str, c ) ) 647 | ++c; 648 | 649 | if( c > len ) 650 | c = len; 651 | 652 | return c; 653 | } 654 | #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 655 | #endif 656 | 657 | #endif 658 | 659 | // update selection and cursor to match each other 660 | static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 661 | { 662 | if (!STB_TEXT_HAS_SELECTION(state)) 663 | state->select_start = state->select_end = state->cursor; 664 | else 665 | state->cursor = state->select_end; 666 | } 667 | 668 | // API cut: delete selection 669 | static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 670 | { 671 | if (STB_TEXT_HAS_SELECTION(state)) { 672 | stb_textedit_delete_selection(str,state); // implicity clamps 673 | state->has_preferred_x = 0; 674 | return 1; 675 | } 676 | return 0; 677 | } 678 | 679 | // API paste: replace existing selection with passed-in text 680 | static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) 681 | { 682 | STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; 683 | // if there's a selection, the paste should delete it 684 | stb_textedit_clamp(str, state); 685 | stb_textedit_delete_selection(str,state); 686 | // try to insert the characters 687 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 688 | stb_text_makeundo_insert(state, state->cursor, len); 689 | state->cursor += len; 690 | state->has_preferred_x = 0; 691 | return 1; 692 | } 693 | // remove the undo since we didn't actually insert the characters 694 | if (state->undostate.undo_point) 695 | --state->undostate.undo_point; 696 | return 0; 697 | } 698 | 699 | // API key: process a keyboard input 700 | static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 701 | { 702 | retry: 703 | switch (key) { 704 | default: { 705 | int c = STB_TEXTEDIT_KEYTOTEXT(key); 706 | if (c > 0) { 707 | STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; 708 | 709 | // can't add newline in single-line mode 710 | if (c == '\n' && state->single_line) 711 | break; 712 | 713 | if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 714 | stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 715 | STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 716 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 717 | ++state->cursor; 718 | state->has_preferred_x = 0; 719 | } 720 | } else { 721 | stb_textedit_delete_selection(str,state); // implicity clamps 722 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 723 | stb_text_makeundo_insert(state, state->cursor, 1); 724 | ++state->cursor; 725 | state->has_preferred_x = 0; 726 | } 727 | } 728 | } 729 | break; 730 | } 731 | 732 | #ifdef STB_TEXTEDIT_K_INSERT 733 | case STB_TEXTEDIT_K_INSERT: 734 | state->insert_mode = !state->insert_mode; 735 | break; 736 | #endif 737 | 738 | case STB_TEXTEDIT_K_UNDO: 739 | stb_text_undo(str, state); 740 | state->has_preferred_x = 0; 741 | break; 742 | 743 | case STB_TEXTEDIT_K_REDO: 744 | stb_text_redo(str, state); 745 | state->has_preferred_x = 0; 746 | break; 747 | 748 | case STB_TEXTEDIT_K_LEFT: 749 | // if currently there's a selection, move cursor to start of selection 750 | if (STB_TEXT_HAS_SELECTION(state)) 751 | stb_textedit_move_to_first(state); 752 | else 753 | if (state->cursor > 0) 754 | --state->cursor; 755 | state->has_preferred_x = 0; 756 | break; 757 | 758 | case STB_TEXTEDIT_K_RIGHT: 759 | // if currently there's a selection, move cursor to end of selection 760 | if (STB_TEXT_HAS_SELECTION(state)) 761 | stb_textedit_move_to_last(str, state); 762 | else 763 | ++state->cursor; 764 | stb_textedit_clamp(str, state); 765 | state->has_preferred_x = 0; 766 | break; 767 | 768 | case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 769 | stb_textedit_clamp(str, state); 770 | stb_textedit_prep_selection_at_cursor(state); 771 | // move selection left 772 | if (state->select_end > 0) 773 | --state->select_end; 774 | state->cursor = state->select_end; 775 | state->has_preferred_x = 0; 776 | break; 777 | 778 | #ifdef STB_TEXTEDIT_MOVEWORDLEFT 779 | case STB_TEXTEDIT_K_WORDLEFT: 780 | if (STB_TEXT_HAS_SELECTION(state)) 781 | stb_textedit_move_to_first(state); 782 | else { 783 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 784 | stb_textedit_clamp( str, state ); 785 | } 786 | break; 787 | 788 | case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 789 | if( !STB_TEXT_HAS_SELECTION( state ) ) 790 | stb_textedit_prep_selection_at_cursor(state); 791 | 792 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 793 | state->select_end = state->cursor; 794 | 795 | stb_textedit_clamp( str, state ); 796 | break; 797 | #endif 798 | 799 | #ifdef STB_TEXTEDIT_MOVEWORDRIGHT 800 | case STB_TEXTEDIT_K_WORDRIGHT: 801 | if (STB_TEXT_HAS_SELECTION(state)) 802 | stb_textedit_move_to_last(str, state); 803 | else { 804 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 805 | stb_textedit_clamp( str, state ); 806 | } 807 | break; 808 | 809 | case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 810 | if( !STB_TEXT_HAS_SELECTION( state ) ) 811 | stb_textedit_prep_selection_at_cursor(state); 812 | 813 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 814 | state->select_end = state->cursor; 815 | 816 | stb_textedit_clamp( str, state ); 817 | break; 818 | #endif 819 | 820 | case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 821 | stb_textedit_prep_selection_at_cursor(state); 822 | // move selection right 823 | ++state->select_end; 824 | stb_textedit_clamp(str, state); 825 | state->cursor = state->select_end; 826 | state->has_preferred_x = 0; 827 | break; 828 | 829 | case STB_TEXTEDIT_K_DOWN: 830 | case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { 831 | StbFindState find; 832 | StbTexteditRow row; 833 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 834 | 835 | if (state->single_line) { 836 | // on windows, up&down in single-line behave like left&right 837 | key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 838 | goto retry; 839 | } 840 | 841 | if (sel) 842 | stb_textedit_prep_selection_at_cursor(state); 843 | else if (STB_TEXT_HAS_SELECTION(state)) 844 | stb_textedit_move_to_last(str,state); 845 | 846 | // compute current position of cursor point 847 | stb_textedit_clamp(str, state); 848 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 849 | 850 | // now find character position down a row 851 | if (find.length) { 852 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 853 | float x; 854 | int start = find.first_char + find.length; 855 | state->cursor = start; 856 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 857 | x = row.x0; 858 | for (i=0; i < row.num_chars; ++i) { 859 | float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 860 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 861 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 862 | break; 863 | #endif 864 | x += dx; 865 | if (x > goal_x) 866 | break; 867 | ++state->cursor; 868 | } 869 | stb_textedit_clamp(str, state); 870 | 871 | state->has_preferred_x = 1; 872 | state->preferred_x = goal_x; 873 | 874 | if (sel) 875 | state->select_end = state->cursor; 876 | } 877 | break; 878 | } 879 | 880 | case STB_TEXTEDIT_K_UP: 881 | case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { 882 | StbFindState find; 883 | StbTexteditRow row; 884 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 885 | 886 | if (state->single_line) { 887 | // on windows, up&down become left&right 888 | key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 889 | goto retry; 890 | } 891 | 892 | if (sel) 893 | stb_textedit_prep_selection_at_cursor(state); 894 | else if (STB_TEXT_HAS_SELECTION(state)) 895 | stb_textedit_move_to_first(state); 896 | 897 | // compute current position of cursor point 898 | stb_textedit_clamp(str, state); 899 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 900 | 901 | // can only go up if there's a previous row 902 | if (find.prev_first != find.first_char) { 903 | // now find character position up a row 904 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 905 | float x; 906 | state->cursor = find.prev_first; 907 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 908 | x = row.x0; 909 | for (i=0; i < row.num_chars; ++i) { 910 | float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 911 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 912 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 913 | break; 914 | #endif 915 | x += dx; 916 | if (x > goal_x) 917 | break; 918 | ++state->cursor; 919 | } 920 | stb_textedit_clamp(str, state); 921 | 922 | state->has_preferred_x = 1; 923 | state->preferred_x = goal_x; 924 | 925 | if (sel) 926 | state->select_end = state->cursor; 927 | } 928 | break; 929 | } 930 | 931 | case STB_TEXTEDIT_K_DELETE: 932 | case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 933 | if (STB_TEXT_HAS_SELECTION(state)) 934 | stb_textedit_delete_selection(str, state); 935 | else { 936 | int n = STB_TEXTEDIT_STRINGLEN(str); 937 | if (state->cursor < n) 938 | stb_textedit_delete(str, state, state->cursor, 1); 939 | } 940 | state->has_preferred_x = 0; 941 | break; 942 | 943 | case STB_TEXTEDIT_K_BACKSPACE: 944 | case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 945 | if (STB_TEXT_HAS_SELECTION(state)) 946 | stb_textedit_delete_selection(str, state); 947 | else { 948 | stb_textedit_clamp(str, state); 949 | if (state->cursor > 0) { 950 | stb_textedit_delete(str, state, state->cursor-1, 1); 951 | --state->cursor; 952 | } 953 | } 954 | state->has_preferred_x = 0; 955 | break; 956 | 957 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 958 | case STB_TEXTEDIT_K_TEXTSTART2: 959 | #endif 960 | case STB_TEXTEDIT_K_TEXTSTART: 961 | state->cursor = state->select_start = state->select_end = 0; 962 | state->has_preferred_x = 0; 963 | break; 964 | 965 | #ifdef STB_TEXTEDIT_K_TEXTEND2 966 | case STB_TEXTEDIT_K_TEXTEND2: 967 | #endif 968 | case STB_TEXTEDIT_K_TEXTEND: 969 | state->cursor = STB_TEXTEDIT_STRINGLEN(str); 970 | state->select_start = state->select_end = 0; 971 | state->has_preferred_x = 0; 972 | break; 973 | 974 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 975 | case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 976 | #endif 977 | case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 978 | stb_textedit_prep_selection_at_cursor(state); 979 | state->cursor = state->select_end = 0; 980 | state->has_preferred_x = 0; 981 | break; 982 | 983 | #ifdef STB_TEXTEDIT_K_TEXTEND2 984 | case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 985 | #endif 986 | case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 987 | stb_textedit_prep_selection_at_cursor(state); 988 | state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 989 | state->has_preferred_x = 0; 990 | break; 991 | 992 | 993 | #ifdef STB_TEXTEDIT_K_LINESTART2 994 | case STB_TEXTEDIT_K_LINESTART2: 995 | #endif 996 | case STB_TEXTEDIT_K_LINESTART: 997 | stb_textedit_clamp(str, state); 998 | stb_textedit_move_to_first(state); 999 | if (state->single_line) 1000 | state->cursor = 0; 1001 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1002 | --state->cursor; 1003 | state->has_preferred_x = 0; 1004 | break; 1005 | 1006 | #ifdef STB_TEXTEDIT_K_LINEEND2 1007 | case STB_TEXTEDIT_K_LINEEND2: 1008 | #endif 1009 | case STB_TEXTEDIT_K_LINEEND: { 1010 | int n = STB_TEXTEDIT_STRINGLEN(str); 1011 | stb_textedit_clamp(str, state); 1012 | stb_textedit_move_to_first(state); 1013 | if (state->single_line) 1014 | state->cursor = n; 1015 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1016 | ++state->cursor; 1017 | state->has_preferred_x = 0; 1018 | break; 1019 | } 1020 | 1021 | #ifdef STB_TEXTEDIT_K_LINESTART2 1022 | case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1023 | #endif 1024 | case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: 1025 | stb_textedit_clamp(str, state); 1026 | stb_textedit_prep_selection_at_cursor(state); 1027 | if (state->single_line) 1028 | state->cursor = 0; 1029 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1030 | --state->cursor; 1031 | state->select_end = state->cursor; 1032 | state->has_preferred_x = 0; 1033 | break; 1034 | 1035 | #ifdef STB_TEXTEDIT_K_LINEEND2 1036 | case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1037 | #endif 1038 | case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1039 | int n = STB_TEXTEDIT_STRINGLEN(str); 1040 | stb_textedit_clamp(str, state); 1041 | stb_textedit_prep_selection_at_cursor(state); 1042 | if (state->single_line) 1043 | state->cursor = n; 1044 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1045 | ++state->cursor; 1046 | state->select_end = state->cursor; 1047 | state->has_preferred_x = 0; 1048 | break; 1049 | } 1050 | 1051 | // @TODO: 1052 | // STB_TEXTEDIT_K_PGUP - move cursor up a page 1053 | // STB_TEXTEDIT_K_PGDOWN - move cursor down a page 1054 | } 1055 | } 1056 | 1057 | ///////////////////////////////////////////////////////////////////////////// 1058 | // 1059 | // Undo processing 1060 | // 1061 | // @OPTIMIZE: the undo/redo buffer should be circular 1062 | 1063 | static void stb_textedit_flush_redo(StbUndoState *state) 1064 | { 1065 | state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1066 | state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1067 | } 1068 | 1069 | // discard the oldest entry in the undo list 1070 | static void stb_textedit_discard_undo(StbUndoState *state) 1071 | { 1072 | if (state->undo_point > 0) { 1073 | // if the 0th undo state has characters, clean those up 1074 | if (state->undo_rec[0].char_storage >= 0) { 1075 | int n = state->undo_rec[0].insert_length, i; 1076 | // delete n characters from all other records 1077 | state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 1078 | STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); 1079 | for (i=0; i < state->undo_point; ++i) 1080 | if (state->undo_rec[i].char_storage >= 0) 1081 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it 1082 | } 1083 | --state->undo_point; 1084 | STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); 1085 | } 1086 | } 1087 | 1088 | // discard the oldest entry in the redo list--it's bad if this 1089 | // ever happens, but because undo & redo have to store the actual 1090 | // characters in different cases, the redo character buffer can 1091 | // fill up even though the undo buffer didn't 1092 | static void stb_textedit_discard_redo(StbUndoState *state) 1093 | { 1094 | int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; 1095 | 1096 | if (state->redo_point <= k) { 1097 | // if the k'th undo state has characters, clean those up 1098 | if (state->undo_rec[k].char_storage >= 0) { 1099 | int n = state->undo_rec[k].insert_length, i; 1100 | // delete n characters from all other records 1101 | state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 1102 | STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); 1103 | for (i=state->redo_point; i < k; ++i) 1104 | if (state->undo_rec[i].char_storage >= 0) 1105 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 1106 | } 1107 | STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point-1, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); 1108 | ++state->redo_point; 1109 | } 1110 | } 1111 | 1112 | static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1113 | { 1114 | // any time we create a new undo record, we discard redo 1115 | stb_textedit_flush_redo(state); 1116 | 1117 | // if we have no free records, we have to make room, by sliding the 1118 | // existing records down 1119 | if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1120 | stb_textedit_discard_undo(state); 1121 | 1122 | // if the characters to store won't possibly fit in the buffer, we can't undo 1123 | if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { 1124 | state->undo_point = 0; 1125 | state->undo_char_point = 0; 1126 | return NULL; 1127 | } 1128 | 1129 | // if we don't have enough free characters in the buffer, we have to make room 1130 | while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) 1131 | stb_textedit_discard_undo(state); 1132 | 1133 | return &state->undo_rec[state->undo_point++]; 1134 | } 1135 | 1136 | static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1137 | { 1138 | StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1139 | if (r == NULL) 1140 | return NULL; 1141 | 1142 | r->where = pos; 1143 | r->insert_length = (short) insert_len; 1144 | r->delete_length = (short) delete_len; 1145 | 1146 | if (insert_len == 0) { 1147 | r->char_storage = -1; 1148 | return NULL; 1149 | } else { 1150 | r->char_storage = state->undo_char_point; 1151 | state->undo_char_point = state->undo_char_point + (short) insert_len; 1152 | return &state->undo_char[r->char_storage]; 1153 | } 1154 | } 1155 | 1156 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1157 | { 1158 | StbUndoState *s = &state->undostate; 1159 | StbUndoRecord u, *r; 1160 | if (s->undo_point == 0) 1161 | return; 1162 | 1163 | // we need to do two things: apply the undo record, and create a redo record 1164 | u = s->undo_rec[s->undo_point-1]; 1165 | r = &s->undo_rec[s->redo_point-1]; 1166 | r->char_storage = -1; 1167 | 1168 | r->insert_length = u.delete_length; 1169 | r->delete_length = u.insert_length; 1170 | r->where = u.where; 1171 | 1172 | if (u.delete_length) { 1173 | // if the undo record says to delete characters, then the redo record will 1174 | // need to re-insert the characters that get deleted, so we need to store 1175 | // them. 1176 | 1177 | // there are three cases: 1178 | // there's enough room to store the characters 1179 | // characters stored for *redoing* don't leave room for redo 1180 | // characters stored for *undoing* don't leave room for redo 1181 | // if the last is true, we have to bail 1182 | 1183 | if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { 1184 | // the undo records take up too much character space; there's no space to store the redo characters 1185 | r->insert_length = 0; 1186 | } else { 1187 | int i; 1188 | 1189 | // there's definitely room to store the characters eventually 1190 | while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1191 | // there's currently not enough room, so discard a redo record 1192 | stb_textedit_discard_redo(s); 1193 | // should never happen: 1194 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1195 | return; 1196 | } 1197 | r = &s->undo_rec[s->redo_point-1]; 1198 | 1199 | r->char_storage = s->redo_char_point - u.delete_length; 1200 | s->redo_char_point = s->redo_char_point - (short) u.delete_length; 1201 | 1202 | // now save the characters 1203 | for (i=0; i < u.delete_length; ++i) 1204 | s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1205 | } 1206 | 1207 | // now we can carry out the deletion 1208 | STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1209 | } 1210 | 1211 | // check type of recorded action: 1212 | if (u.insert_length) { 1213 | // easy case: was a deletion, so we need to insert n characters 1214 | STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1215 | s->undo_char_point -= u.insert_length; 1216 | } 1217 | 1218 | state->cursor = u.where + u.insert_length; 1219 | 1220 | s->undo_point--; 1221 | s->redo_point--; 1222 | } 1223 | 1224 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1225 | { 1226 | StbUndoState *s = &state->undostate; 1227 | StbUndoRecord *u, r; 1228 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1229 | return; 1230 | 1231 | // we need to do two things: apply the redo record, and create an undo record 1232 | u = &s->undo_rec[s->undo_point]; 1233 | r = s->undo_rec[s->redo_point]; 1234 | 1235 | // we KNOW there must be room for the undo record, because the redo record 1236 | // was derived from an undo record 1237 | 1238 | u->delete_length = r.insert_length; 1239 | u->insert_length = r.delete_length; 1240 | u->where = r.where; 1241 | u->char_storage = -1; 1242 | 1243 | if (r.delete_length) { 1244 | // the redo record requires us to delete characters, so the undo record 1245 | // needs to store the characters 1246 | 1247 | if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1248 | u->insert_length = 0; 1249 | u->delete_length = 0; 1250 | } else { 1251 | int i; 1252 | u->char_storage = s->undo_char_point; 1253 | s->undo_char_point = s->undo_char_point + u->insert_length; 1254 | 1255 | // now save the characters 1256 | for (i=0; i < u->insert_length; ++i) 1257 | s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1258 | } 1259 | 1260 | STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1261 | } 1262 | 1263 | if (r.insert_length) { 1264 | // easy case: need to insert n characters 1265 | STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1266 | s->redo_char_point += r.insert_length; 1267 | } 1268 | 1269 | state->cursor = r.where + r.insert_length; 1270 | 1271 | s->undo_point++; 1272 | s->redo_point++; 1273 | } 1274 | 1275 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1276 | { 1277 | stb_text_createundo(&state->undostate, where, 0, length); 1278 | } 1279 | 1280 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1281 | { 1282 | int i; 1283 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1284 | if (p) { 1285 | for (i=0; i < length; ++i) 1286 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1287 | } 1288 | } 1289 | 1290 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1291 | { 1292 | int i; 1293 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1294 | if (p) { 1295 | for (i=0; i < old_length; ++i) 1296 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1297 | } 1298 | } 1299 | 1300 | // reset the state to default 1301 | static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1302 | { 1303 | state->undostate.undo_point = 0; 1304 | state->undostate.undo_char_point = 0; 1305 | state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1306 | state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1307 | state->select_end = state->select_start = 0; 1308 | state->cursor = 0; 1309 | state->has_preferred_x = 0; 1310 | state->preferred_x = 0; 1311 | state->cursor_at_end_of_line = 0; 1312 | state->initialized = 1; 1313 | state->single_line = (unsigned char) is_single_line; 1314 | state->insert_mode = 0; 1315 | } 1316 | 1317 | // API initialize 1318 | static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1319 | { 1320 | stb_textedit_clear_state(state, is_single_line); 1321 | } 1322 | #endif//STB_TEXTEDIT_IMPLEMENTATION 1323 | -------------------------------------------------------------------------------- /imgui_dock.h: -------------------------------------------------------------------------------- 1 | // -*-c++-*- 2 | /* 3 | Copyright (c) 2017 Alberto Otero de la Roza 4 | , Robin Myhr , Isaac 5 | Visintainer , Richard Greaves , Ángel 6 | Martín Pendás and Víctor Luaña 7 | . 8 | 9 | critic2 is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or (at 12 | your option) any later version. 13 | 14 | critic2 is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | */ 22 | // Rewritten from: git@github.com:vassvik/imgui_docking_minimal.git 23 | // Original code by vassvik (?) released as public domain. 24 | 25 | // This code provides a collection of docking windows and containers 26 | // for the immediate-mode graphical user interface (ImGui) library 27 | // (https://github.com/ocornut/imgui) by Omar Cornut and 28 | // collaborators. Three components are implemented: 29 | // 30 | // 1. Docks - Behave like normal windows, but can attach themselves to 31 | // containers and root containers. 32 | // 33 | // 2. Containers - Windows that display the contents of docks attached 34 | // to them. Containers have a tab bar at the top that shows all 35 | // containers attached. At any time, one tab is active and the rest 36 | // are hidden. Very similar to how a browser window (e.g. chrome, 37 | // firefox) works. 38 | // 39 | // 3. Root containers - docks and containers can be attached to root 40 | // container windows. The window space of a root container is split 41 | // when a new window is added to it, depending on the position at 42 | // which it is dropped. Containers inside root containers keep track 43 | // of attached docks, and have an additional "lift grip" by which they 44 | // can be pulled out of the root container. 45 | // 46 | // Besides the attaching/detaching, all three components behave like 47 | // normal windows - they can be resized, auto-resized, moved, 48 | // collapsed, and closed. The usual flags (NoResize, 49 | // NoBringToFrontOnFocus,...) and placement commands 50 | // (SetNextWindowPos,...) work as well. 51 | // 52 | // The public interface contains the following: 53 | // 54 | // - The ImGui::Dock structure: docks, containers, and root containers 55 | // are instances of this class. 56 | // 57 | // - ImGui::RootContainer: create a root container and return a 58 | // pointer to the new Dock object. 59 | // 60 | // - ImGui::Container: create a container and return a pointer to the 61 | // new Dock object. Neither root containers nor containers need to 62 | // call an End() function (because no items are allowed inside them). 63 | // 64 | // - ImGui::BeginDock and ImGui::EndDock: the equivalent for docks to what 65 | // ImGui::Begin() and ImGui::End() are for windows. They open and 66 | // close a dock. If BeginDock returns true, interactive items can be 67 | // added to the dock. In addition, the argument oncedock allows 68 | // docking the dock to a container in the first pass. More convoluted 69 | // ways to attach docks and containers on initialization are possible 70 | // using the newDock and newDockRoot functions - an example will be 71 | // provided elsewhere. 72 | // 73 | // - ImGui::GetCurrentDock: when used beteween BeginDock and EndDock, 74 | // returns a pointer to the currently open dock. Otherwise, returns 75 | // null. 76 | // 77 | // - ImGui::ShutdownDock: deallocates memory for the dock hash 78 | // table. Should be run once docks are no longer needed, or at the end 79 | // of the program. 80 | // 81 | // Some notes: 82 | // 83 | // 1. Call RootContainer before any Container attached to it, and 84 | // Container before any BeginDock/EndDock attached to it. Doing it the 85 | // other way around works, but since root containers set the position 86 | // of containers, and containers of docks, it will result in a lag of 87 | // one or two frames when moving windows about. 88 | // 89 | // 2. Some widgets from imgui_widgets are used, so you will need that 90 | // file and its header to use docks. 91 | // 92 | // Have fun! -- Alberto 93 | 94 | #ifndef IMGUI_DOCK_H 95 | #define IMGUI_DOCK_H 96 | 97 | #include "imgui.h" 98 | #include "imgui_internal.h" 99 | #include 100 | #include 101 | 102 | namespace ImGui{ 103 | 104 | typedef int DockFlags; 105 | 106 | struct Dock{ 107 | enum Drop_ {Drop_None, Drop_Top, Drop_Right, Drop_Bottom, Drop_Left, Drop_Tab}; 108 | 109 | // Enum for the type of docking windows 110 | enum Type_{Type_None,Type_Root,Type_Container,Type_Dock,Type_Horizontal, 111 | Type_Vertical}; 112 | // Status of a single dock 113 | enum Status_{Status_None,Status_Open,Status_Collapsed,Status_Closed, 114 | Status_Dragged,Status_Docked}; 115 | // Dock flags 116 | enum DockFlags_{ 117 | DockFlags_NoLiftContainer = 1 << 0, // A container is not allowed to be lifted 118 | DockFlags_Transparent = 1 << 1, // This window is transparent (but still handles inputs) 119 | }; 120 | 121 | char* label = nullptr; // dock and window label 122 | ImGuiWindow* window = nullptr; // associated window 123 | Type_ type = Type_None; // type of docking window 124 | Status_ status = Status_None; // status of the docking window 125 | ImVec2 pos = {}; // position of the window 126 | ImVec2 pos_saved = {}; // position of the window (before docking) 127 | ImVec2 size = {-1.f,-1.f}; // size of the window 128 | ImVec2 size_saved = {}; // saved size (before docking for dockable window) 129 | ImGuiWindowFlags flags = 0; // flags for the window 130 | ImGuiWindowFlags flags_saved = 0; // flags for the window (before docking) 131 | DockFlags dockflags = 0; // flags for this dock 132 | bool collapsed = false; // whether a docked window is collapsed 133 | bool collapsed_saved = false; // saved collapsed (before docking) 134 | ImRect tabbarrect = {}; // rectangle for the container tab bar 135 | float tabdz = 0.f; // z position for the end of the tab bar (container) 136 | ImGuiWindow* tabwin = nullptr; // pointer to the tab window (for cleaning up the window stack) 137 | ImVector tabsx = {}; // tab positions for container; sliders for h/v-container 138 | int splithint = 0; // hint to tell which bar should be removed when lifted (0 = any, +1 right/up, -1 left/down) 139 | ImVec2 splitweight = {1.f,1.f}; // relative weight of this dock - used to set the initial position of the bar in a h-v split 140 | bool hidden = false; // whether a docked window is hidden 141 | bool noborder = false; // flag if we pushed to have no border 142 | bool showingdrops = false; // true if we are showing the drop targets for this dock 143 | bool hoverable = true; // whether a window responds to being hovered 144 | std::list stack = {}; // stack of docks at this level 145 | Dock *currenttab = nullptr; // currently selected tab (container) 146 | Dock *parent = nullptr; // immediate dock to which this is dock 147 | Dock *root = nullptr; // root container to which this is docked 148 | bool *p_open = nullptr; // the calling routine open window bool 149 | bool control_window_this_frame = false; // the pos, size, etc. change window's attributes this frame 150 | int nchild_ = 0; // number of children (to generate labels in rootcontainer) 151 | int nchild = 0; // number of children (to count for the last dock in rootcontainer) 152 | bool automatic = false; // whether this dock was automatically generated in a rootcontainer 153 | 154 | Dock(){}; 155 | ~Dock(){ MemFree(label);} 156 | 157 | // Is the mouse hovering the tab bar of this dock? (no rectangle clipping) 158 | bool IsMouseHoveringTabBar(); 159 | // Is the mouse hovering the drop edges of a container? (no rectangle clipping) 160 | // Returns the edge id. 161 | Drop_ IsMouseHoveringEdge(); 162 | // Returns true if the mouse is hovering the drop target when the container 163 | // is empty. (no rectangle clipping) 164 | bool IsMouseHoveringFull(); 165 | // Get the nearest tab border in the tab when hovering a 166 | // container. Returns the tab number or -1 if the tab bar is not 167 | // hovered or there are no tabs. 168 | int getNearestTabBorder(); 169 | 170 | // Show a drop targets on this window that covers the whole window. 171 | void showDropTargetFull(); 172 | // Show the drop targets on this window's tab bar 173 | void showDropTargetOnTabBar(); 174 | // Show the drop targets on the edge of the container. edge is the 175 | // id for the edge (1:top, 2:right, 3:bottom, 4:left). 176 | void showDropTargetEdge(Drop_ edge, bool active); 177 | 178 | // Find the integer index of dock dthis in the stack of this 179 | // container or h/v-container. 180 | int OpStack_Find(Dock *dthis); 181 | // Insert the dock dnew in the stack of this at position ithis (or 182 | // at the back if ithis == -1. 183 | void OpStack_Insert(Dock *dnew, int ithis=-1); 184 | // Replace the dock replaced with replacement in this dock's 185 | // stack. If erase, kill the replaced dock. 186 | void OpStack_Replace(Dock *replaced, Dock *replacement, bool erase); 187 | // Remove the dock dd from the stack of this container. If erase, 188 | // kill the dock as well. 189 | void OpStack_Remove(Dock *dd, bool erase); 190 | 191 | // Replace this dock (part of a root container tree) with a 192 | // horizontal (type==Type_Horizontal) or vertical 193 | // (type==Type_Vertical) container. The new container has the 194 | // current dock plus container dcont (if null, a new dcont is 195 | // allocated). The new container is placed before (before==true) 196 | // or after (false) the old one and the split uses weight for the 197 | // splitweight of the new container if dcont=null. Returns the new 198 | // container. 199 | Dock *OpRoot_ReplaceHV(Type_ type,bool before,Dock *dcont=nullptr,ImVec2 weight={1.f,1.f}); 200 | // Add a new container (dcont) to the horizontal/vertical parent 201 | // of this dock. If !dcont, a new container is allocated. The new 202 | // container is placed before (before==true) or after (false) the 203 | // old one, and is returned by this function. The split uses 204 | // weight for the splitweight of the new container if dcont=null. 205 | Dock *OpRoot_AddToHV(bool before,Dock *dcont=nullptr,ImVec2 weight={1.f,1.f}); 206 | // Fill an empty root container with at least one empty automatic 207 | // container. 208 | void OpRoot_FillEmpty(); 209 | 210 | // Raise this dock's window to the top of the window stack. 211 | void raiseDock(); 212 | // Raise this dock's window to the top of the window 213 | // stack. Or sink it if it is NoBringToFrontOnFocus. 214 | void raiseOrSinkDock(); 215 | // Focus a container and its child and parent. Sets the move ID 216 | // and the active ID. 217 | void focusContainer(); 218 | // Lift this container from its rootcontainer 219 | void liftContainer(); 220 | 221 | // Add a new dock (dnew) to a container (this) at position ithis 222 | // in the tab bar, and make it the current tab. If ithis == -1, 223 | // add it to the end of the tab bar. 224 | void newDock(Dock *dnew, int ithis = -1); 225 | // Add a new dock to a root container, or a container docked to a 226 | // root container. If this is a root or h-v container, the new 227 | // dock is added to the last container in the tree (bottom 228 | // right). If the added dock is a container, just add it - if it 229 | // is a normal dock, create an automatic container for it. The 230 | // new dock is added at edge ithis of the container (1:top, 231 | // 2:right, 3:bottom, 4:left). edge = 5 is used to add to or 232 | // replace an automatic container in an empty root container. 233 | // Returns the new container. 234 | Dock *newDockRoot(Dock *dnew, Drop_ iedge); 235 | 236 | // Undock a container, restore its position, size, etc. flags, and 237 | // place it at the top or the bottom of the window stack. The 238 | // stack of the parent container is not modified. 239 | void unDock(); 240 | // Clear all docked windows from a container 241 | void clearContainer(); 242 | // Clear all docked windows from a root container 243 | void clearRootContainer(); 244 | 245 | // Kill this automatic container if it is empty. If this 246 | // h/v-container is empty, convert it to a container. If it has 247 | // one window, kill it and connect its child to its parent. 248 | void killContainerMaybe(); 249 | 250 | // Draw the tab bar of a container. On output, erased is true if a 251 | // tab in this container was closed. 252 | void drawTabBar(Dock **erased=nullptr); 253 | // Hide this dock docked to a container on an inactive tab. 254 | void hideTabWindow(); 255 | // Show this dock docked to a container (dcont) on an active 256 | // tab. If noresize, do not show the resize grip. 257 | void showTabWindow(Dock *dcont, bool noresize); 258 | // Draw the contents of a container. If noresize, do not show the 259 | // resize grip. On output, erased is true if a tab in this 260 | // container was closed. 261 | void drawContainer(bool noresize, Dock **erased=nullptr); 262 | 263 | // Traverse the tree of this root container and return its minimum 264 | // size based on its contents. Recursive. 265 | void getMinSize(ImVec2 *minsize, ImVec2 *autosize); 266 | // Center all sliding bars in this root container. 267 | void resetRootContainerBars(); 268 | // Sets the position of a sliding bar. The sliding bar is on edge 269 | // iedge (1:top, 2:right, 3:bottom, 4:left) of this container, 270 | // which is docked to a root container. xpos is the position of 271 | // the bar given as a fraction of the window size (between 0 and 272 | // 1). 273 | void setSlidingBarPosition(Drop_ iedge, float xpos); 274 | // Traverse the tree of this root container and draw all sliding 275 | // bars in it. Sets the tabsx vector containing the positions of 276 | // the bars. root is a pointer to the root container. Recursive. 277 | void drawRootContainerBars(Dock* root); 278 | // Traverse the tree of this root container and draw all 279 | // containers in it. Must be called after drawRootContainerBars to 280 | // have correct sliding bar positions. root is a pointer to the 281 | // root container. On output, lift contains a pointer to a 282 | // container to be lifted or null. erased points to the container 283 | // where one of the tabs was closed. count keeps count of the 284 | // number of docked windows in the root container. Recursive. 285 | void drawRootContainer(Dock* root, Dock **lift, Dock **erased, int *count = nullptr); 286 | 287 | // Sets the position of this dock/container in its detached 288 | // state. Useful when a dock/container is immediately attached in 289 | // the first pass and does not have the chance to save this 290 | // variable from the created window. 291 | void setDetachedDockPosition(float x, float y); 292 | 293 | // Sets the size of this dock/container in its detached 294 | // state. Useful when a dock/container is immediately attached in 295 | // the first pass and does not have the chance to save this 296 | // variable from the created window. 297 | void setDetachedDockSize(float x, float y); 298 | 299 | // Set the h-v split weight for this dock. 300 | void setSplitWeight(float wx,float wy); 301 | 302 | // Close a dock window. This function is used to kill a dock 303 | // externally. Unlike normal ImGui windows, making p_open false is 304 | // not enough if the window is docked, because the container has to 305 | // be modified, too. Therefore, CloseDock() should always be used 306 | // when setting p_open = false. 307 | void closeDock(); 308 | }; // struct Dock 309 | 310 | // Create a root container with the given label. If p_open, with a 311 | // close button (close status as *p_open). Extra window flags are 312 | // passed to the container window. Dock flags can also be passed 313 | // (see above). Returns a pointer to the root container dock 314 | // object. Root containers can hold containers and docks. 315 | Dock *RootContainer(const char* label, bool* p_open=nullptr, ImGuiWindowFlags extra_flags=0, DockFlags dock_flags=0); 316 | 317 | // Create a container with the given label. If p_open, with a close 318 | // button (close status as *p_open). Extra window flags are passed 319 | // to the container window. Dock flags can also be passed (see 320 | // above). Returns a pointer to the container dock 321 | // object. Containers contain docks and can be docked to root 322 | // containers. 323 | Dock *Container(const char* label, bool* p_open=nullptr, ImGuiWindowFlags extra_flags=0, DockFlags dock_flags=0); 324 | 325 | // Create/end a dock window. If p_open, with a close button. If 326 | // p_open, with a close button (close status as *p_open). Extra 327 | // window flags are passed to the window. Dock flags can also be 328 | // passed (see above). If a pointer to a container is passed in 329 | // oncedock, dock to that container in the first pass. Returns true 330 | // if the window is open and accepts items (same as ImGui's 331 | // Begin). The EndDock() function needs to be used after all items 332 | // have been added. See closeDock() note above for how to set 333 | // p_open externally to close a dock. 334 | bool BeginDock(const char* label, bool* p_open=nullptr, ImGuiWindowFlags extra_flags=0, 335 | DockFlags dock_flags=0, Dock *oncedock=nullptr); 336 | void EndDock(); 337 | 338 | // GetCurrentDock() gives the pointer to the current open dock (same 339 | // as GetCurrentWindow(), but for docks). Must appear between 340 | // BeginDock and EndDock. Returns null if no dock is open. 341 | Dock *GetCurrentDock(); 342 | 343 | // Free the memory occupied dock hash tables. 344 | void ShutdownDock(); 345 | 346 | // Print information about the current known docks. For debug purposes. 347 | void PrintDock__(); 348 | 349 | } // namespace ImGui 350 | #endif 351 | -------------------------------------------------------------------------------- /imgui_widgets.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 Alberto Otero de la Roza 3 | , Robin Myhr , Isaac 4 | Visintainer , Richard Greaves , Ángel 5 | Martín Pendás and Víctor Luaña 6 | . 7 | 8 | critic2 is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or (at 11 | your option) any later version. 12 | 13 | critic2 is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #include "imgui_widgets.h" 23 | #include "imgui.h" 24 | #include "imgui_internal.h" 25 | 26 | #include 27 | #include 28 | 29 | using namespace ImGui; 30 | 31 | // Tooltip variables and behavior // 32 | // Variables: 33 | // - tooltip_shownid: ID for the tooltip element shown. 34 | // - tooltip_hoveredid: ID for the tooltip element hovered last frame. 35 | // - tooltip_thisframe: saves the current frame to detect when 36 | // AttachTooltip is called for the first time. 37 | // - tooltip_washovered: set to true if any AttachTooltip element 38 | // has been hovered in the last frame. 39 | // - tooltip_time: the last time when no item was hovered. 40 | // - tooltip_lastactive - the last time when a tooltip was shown. 41 | // How it works: 42 | // The tooltip is shown if: 43 | // - The mouse hovers over a single tooltip element for longer than 44 | // delay, which is passed as a function argument. 45 | // - The mouse hovers over a tooltip element and it was already 46 | // showing a tooltip from a different element. In this case, 47 | // there is no delay. 48 | // - The mouse hovers over a tooltip element, then hovers none for a 49 | // period of time less than delay, then overs over a new tooltip 50 | // element. In this case, the delay is calculated as the difference 51 | // between the time the mouse hovered over the new tooltip element 52 | // (tooltip_time) and the time the mouse left the previous tooltip 53 | // element (tooltip_lastactive), with a maximum equal to 54 | // delay. Thus, the delay is equal to the time the mouse spent 55 | // outside any tooltip elment. 56 | static ImGuiID tooltip_shownid = 0; 57 | static ImGuiID tooltip_hoveredid = 0; 58 | static int tooltip_thisframe = -1; 59 | static bool tooltip_washovered = false; 60 | static float tooltip_time = 0.f; 61 | static float tooltip_lastactive = 0.f; 62 | 63 | // Variable for the widget styles 64 | ImGuiStyleWidgets_ ImGuiStyleWidgets; 65 | 66 | // Function definitions // 67 | 68 | bool ImGui::IsMouseHoveringConvexPoly(const ImVec2* points, const int num_points){ 69 | ImGuiContext& g = *GImGui; 70 | 71 | float p0[num_points][2]; 72 | 73 | // differences 74 | for (int i = 0; i < num_points; i++){ 75 | p0[i][0] = points[i].x - g.IO.MousePos.x; 76 | p0[i][1] = points[i].y - g.IO.MousePos.y; 77 | } 78 | 79 | // first sign 80 | float a0 = p0[0][0] * p0[num_points-1][1] - p0[num_points-1][0] * p0[0][1]; 81 | 82 | // compare to all other signs 83 | for (int i = 0; i < num_points-1; i++){ 84 | float a = p0[i+1][0] * p0[i][1] - p0[i][0] * p0[i+1][1]; 85 | if (a * a0 < 0.f) 86 | return false; 87 | } 88 | 89 | return true; 90 | } 91 | 92 | void ImGui::SlidingBar(const char *label, ImGuiWindow* window, ImVec2 *pos, 93 | ImVec2 size, float minx, float maxx, int direction){ 94 | ImDrawList* dl = window->DrawList; 95 | ImGuiContext *g = GetCurrentContext(); 96 | bool hovered, held; 97 | const ImU32 color = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_Slidingbar]); 98 | const ImU32 coloractive = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_SlidingbarActive]); 99 | const ImU32 colorhovered = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_SlidingbarHovered]); 100 | 101 | const ImRect slidingrect(*pos,*pos+size); 102 | const ImGuiID slidingid = window->GetID(label); 103 | ButtonBehavior(slidingrect, slidingid, &hovered, &held); 104 | 105 | if (hovered || held){ 106 | if (direction == 1) 107 | SetMouseCursor(ImGuiMouseCursor_ResizeEW); 108 | else 109 | SetMouseCursor(ImGuiMouseCursor_ResizeNS); 110 | } 111 | 112 | if (held){ 113 | if (direction == 1) 114 | pos->x = max(min(g->IO.MousePos.x - 0.5f * size.x,maxx),minx); 115 | else 116 | pos->y = max(min(g->IO.MousePos.y - 0.5f * size.y,maxx),minx); 117 | } 118 | 119 | // draw the rectangle 120 | dl->PushClipRectFullScreen(); 121 | dl->AddRectFilled(slidingrect.GetTL(),slidingrect.GetBR(), 122 | held?coloractive:(hovered?colorhovered:color), 123 | g->Style.ScrollbarRounding); 124 | dl->PopClipRect(); 125 | } 126 | 127 | bool ImGui::ButtonWithX(const char* label, const ImVec2& size, bool activetab, 128 | bool *p_open, bool *dragged, bool *dclicked, float alphamul /*=1.f*/){ 129 | ImGuiContext *g = GetCurrentContext(); 130 | const float crossz = round(0.3 * g->FontSize); 131 | const float crosswidth = 3.5f * crossz + 6; 132 | const float mintabwidth = 2 * crosswidth + 1; 133 | 134 | const ImU32 colorxfg = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXFg]); 135 | const ImU32 colorxfg_hovered = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXFgHovered]); 136 | const ImU32 colorxfg_pressed = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXFgActive]); 137 | const ImU32 colorxbg = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXBg]); 138 | const ImU32 colorxbg_hovered = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXBgHovered]); 139 | const ImU32 colorxbg_pressed = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabXBgActive]); 140 | ImU32 color = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_Tab]); 141 | ImU32 color_active = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabActive]); 142 | ImU32 color_pressed = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabPressed]); 143 | ImU32 color_hovered = GetColorU32(ImGuiStyleWidgets.Colors[ImGuiColWidgets_TabHovered]); 144 | 145 | // size of the main button 146 | ImVec2 mainsize = size; 147 | if (p_open && size.x >= mintabwidth) 148 | mainsize.x -= crosswidth; 149 | 150 | // main button 151 | bool clicked = InvisibleButton(label, mainsize); 152 | 153 | // some positions and other variables 154 | bool hovered = IsItemHovered(); 155 | ImVec2 pos0 = GetItemRectMin(); 156 | ImVec2 pos1s = GetItemRectMax(); 157 | 158 | // set the output flags for the main button 159 | bool pressed = IsItemActive() && IsMouseDown(0); 160 | *dragged = IsItemActive() && IsMouseDragging(); 161 | *dclicked = IsItemActive() && IsMouseDoubleClicked(0); 162 | 163 | // draw the close button, if this window can be closed 164 | ImVec2 center; 165 | bool xhovered = false, xpressed = false; 166 | if (p_open && size.x >= mintabwidth){ 167 | // draw the close button itself 168 | SameLine(); 169 | char tmp2[strlen(label)+6]; 170 | ImFormatString(tmp2,IM_ARRAYSIZE(tmp2),"%s__x__",label); 171 | ImVec2 smallsize(crosswidth, size.y); 172 | 173 | // cross button 174 | *p_open = !(InvisibleButton(tmp2, smallsize)); 175 | 176 | // update output flags and variables for drawing 177 | *dragged = *dragged | (p_open && IsItemActive() && IsMouseDragging()); 178 | xhovered = IsItemHovered(); 179 | hovered |= xhovered; 180 | xpressed = IsItemActive() && IsMouseDown(0); 181 | center = ((GetItemRectMin() + GetItemRectMax()) * 0.5f); 182 | } 183 | ImVec2 pos1 = GetItemRectMax(); 184 | 185 | // rectangle and text 186 | ImDrawList* drawl = GetWindowDrawList(); 187 | const char* text_end = FindRenderedTextEnd(label); 188 | ImVec2 text_size = CalcTextSize(label,text_end,true,false); 189 | ImRect clip_rect = ImRect(pos0,pos1s); 190 | drawl->AddRectFilled(pos0,pos1,activetab? color_active: 191 | pressed? color_pressed: 192 | hovered? color_hovered: 193 | color,ImGuiStyleWidgets.TabRounding,ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_TopRight); 194 | 195 | if (ImGuiStyleWidgets.TabBorderSize > 0.0f) 196 | drawl->AddRect(pos0,pos1,GetColorU32(g->Style.Colors[ImGuiColWidgets_TabBorder]), 197 | ImGuiStyleWidgets.TabRounding,ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_TopRight,1.0f); 198 | RenderTextClipped(pos0,pos1s,label,text_end,&text_size, ImVec2(0.5f,0.5f), &clip_rect); 199 | 200 | // draw the "x" 201 | if (p_open && size.x >= mintabwidth){ 202 | drawl->AddCircleFilled(center,crossz * sqrt(2.f) * 1.25f, 203 | xpressed?colorxbg_pressed: 204 | xhovered?colorxbg_hovered: 205 | colorxbg,36); 206 | drawl->AddLine(center+ImVec2(-crossz,-crossz), center+ImVec2(crossz,crossz), 207 | xpressed?colorxfg_pressed: 208 | xhovered?colorxfg_hovered: 209 | colorxfg); 210 | drawl->AddLine(center+ImVec2( crossz,-crossz), center+ImVec2(-crossz,crossz), 211 | xpressed?colorxfg_pressed: 212 | xhovered?colorxfg_hovered: 213 | colorxfg); 214 | } 215 | 216 | return clicked; 217 | } 218 | 219 | void ImGui::ResizeGripOther(const char *label, ImGuiWindow* window, ImGuiWindow* cwindow, bool *dclicked/*=nullptr*/){ 220 | static bool first = true; 221 | if (dclicked) *dclicked = false; 222 | 223 | static ImVec2 pos_orig = {}; 224 | static ImVec2 size_orig = {}; 225 | static ImVec2 csize_orig = {}; 226 | ImGuiContext *g = GetCurrentContext(); 227 | const ImVec2 br = window->Rect().GetBR(); 228 | ImDrawList* dl = window->DrawList; 229 | const float resize_corner_size = ImMax(g->FontSize * 1.35f, g->Style.WindowRounding + 1.0f + g->FontSize * 0.2f); 230 | const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); 231 | char tmp[strlen(label)+15]; 232 | ImFormatString(tmp,IM_ARRAYSIZE(tmp),"%s__resize__",label); 233 | const ImGuiID resize_id = window->GetID(tmp); 234 | 235 | // no clipping; save previous clipping 236 | ImRect saverect = window->ClipRect; 237 | window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); 238 | dl->PushClipRectFullScreen(); 239 | 240 | // button behavior 241 | bool hovered, held; 242 | ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); 243 | 244 | // update the static flags 245 | if (held){ 246 | if (first) { 247 | pos_orig = window->Pos; 248 | size_orig = window->SizeFull; 249 | csize_orig = cwindow->SizeFull; 250 | } 251 | first = false; 252 | } else { 253 | first = true; 254 | } 255 | 256 | // mouse cursor 257 | if (hovered || held) 258 | g->MouseCursor = ImGuiMouseCursor_ResizeNWSE; 259 | 260 | // apply the size change 261 | if (g->HoveredWindow == window && held && g->IO.MouseDoubleClicked[0]){ 262 | if (dclicked) *dclicked = true; 263 | ImVec2 size_auto_fit = ImClamp(cwindow->SizeContents + cwindow->WindowPadding, g->Style.WindowMinSize, 264 | ImMax(g->Style.WindowMinSize, g->IO.DisplaySize - g->Style.DisplaySafeAreaPadding)); 265 | 266 | cwindow->SizeFull = size_auto_fit; 267 | ClearActiveID(); 268 | } else if (held) 269 | cwindow->SizeFull = csize_orig + (g->IO.MousePos - g->ActiveIdClickOffset + resize_rect.GetSize() - pos_orig) - size_orig; 270 | cwindow->Size = cwindow->SizeFull; 271 | 272 | // resize grip (from imgui.cpp) 273 | ImU32 resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); 274 | dl->PathLineTo(br + ImVec2(-resize_corner_size, -window->WindowBorderSize)); 275 | dl->PathLineTo(br + ImVec2(-window->WindowBorderSize, -resize_corner_size)); 276 | dl->PathArcToFast(ImVec2(br.x - g->Style.WindowRounding - window->WindowBorderSize, br.y - g->Style.WindowRounding - window->WindowBorderSize), g->Style.WindowRounding, 0, 3); 277 | dl->PathFillConvex(resize_col); 278 | dl->PopClipRect(); 279 | window->ClipRect = saverect; 280 | } 281 | 282 | bool ImGui::LiftGrip(const char *label, ImGuiWindow* window){ 283 | ImGuiContext *g = GetCurrentContext(); 284 | const ImVec2 bl = window->Rect().GetBL(); 285 | ImDrawList* dl = window->DrawList; 286 | const float lift_corner_size = ImMax(g->FontSize * 1.35f, g->Style.WindowRounding + 1.0f + g->FontSize * 0.2f); 287 | const ImRect lift_rect(bl - ImVec2(0.f, lift_corner_size * 0.75f), bl + ImVec2(lift_corner_size * 0.75f, 0.f)); 288 | char tmp[strlen(label)+15]; 289 | ImFormatString(tmp,IM_ARRAYSIZE(tmp),"%s__lift__",label); 290 | const ImGuiID lift_id = window->GetID(tmp); 291 | 292 | // no clipping; save previous clipping 293 | ImRect saverect = window->ClipRect; 294 | window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); 295 | dl->PushClipRectFullScreen(); 296 | 297 | // button behavior 298 | bool hovered, held; 299 | ButtonBehavior(lift_rect, lift_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); 300 | 301 | // lift grip (from imgui.cpp's resize grip) 302 | ImU32 lift_col = GetColorU32(held?ImGuiStyleWidgets.Colors[ImGuiColWidgets_LiftGripActive]: 303 | hovered?ImGuiStyleWidgets.Colors[ImGuiColWidgets_LiftGripHovered]: 304 | ImGuiStyleWidgets.Colors[ImGuiColWidgets_LiftGrip]); 305 | dl->PathLineTo(bl + ImVec2(window->WindowBorderSize, -lift_corner_size)); 306 | dl->PathLineTo(bl + ImVec2(lift_corner_size, -window->WindowBorderSize)); 307 | dl->PathArcToFast(ImVec2(bl.x + g->Style.WindowRounding + window->WindowBorderSize, bl.y - g->Style.WindowRounding - window->WindowBorderSize), g->Style.WindowRounding, 3, 6); 308 | dl->PathFillConvex(lift_col); 309 | dl->PopClipRect(); 310 | window->ClipRect = saverect; 311 | 312 | return held && IsMouseDragging(); 313 | } 314 | 315 | bool ImGui::ImageInteractive(ImTextureID texture, float a, bool *hover, ImRect *vrect){ 316 | ImGuiWindow *win = GetCurrentWindow(); 317 | if (win->SkipItems) 318 | return false; 319 | ImGuiContext *g = GetCurrentContext(); 320 | 321 | PushID((void *)texture); 322 | const ImGuiID id = win->GetID("#imageinteractive"); 323 | PopID(); 324 | 325 | vrect->Min = win->DC.CursorPos; 326 | vrect->Max = win->DC.CursorPos + win->ContentsRegionRect.Max - (win->DC.CursorPos - win->Pos + ImVec2(1.f,1.f)); 327 | 328 | if (!ItemAdd(*vrect, id)) 329 | return false; 330 | 331 | float x = vrect->Max.x - vrect->Min.x; 332 | float y = vrect->Max.y - vrect->Min.y; 333 | float xratio = x/fmax(x,y); 334 | float yratio = y/fmax(x,y); 335 | float rx = 0.5f * (1.f - xratio) * a; 336 | float ry = 0.5f * (1.f - yratio) * a; 337 | 338 | bool held; 339 | bool pressed = ButtonBehavior(*vrect, id, hover, &held); 340 | win->DrawList->AddImage(texture,vrect->Min,vrect->Max,ImVec2(rx, a - ry),ImVec2(a - rx, ry)); 341 | return true; 342 | } 343 | 344 | bool ImGui::InvisibleButtonEx(const char* str_id, const ImVec2& size_arg, bool* hovered, bool *held){ 345 | ImGuiWindow* window = GetCurrentWindow(); 346 | if (window->SkipItems) 347 | return false; 348 | 349 | const ImGuiID id = window->GetID(str_id); 350 | ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); 351 | const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 352 | ItemSize(bb); 353 | if (!ItemAdd(bb, id)) 354 | return false; 355 | 356 | bool pressed = ButtonBehavior(bb, id, hovered, held); 357 | 358 | return pressed; 359 | } 360 | 361 | void ImGui::AttachTooltip(const char* desc, float delay, float maxwidth, ImFont* font){ 362 | ImGuiContext *g = GetCurrentContext(); 363 | ImGuiID id = g->CurrentWindow->DC.LastItemId; 364 | float time = GetTime(); 365 | int thisframe = GetFrameCount(); 366 | 367 | if (thisframe != tooltip_thisframe){ 368 | // run once every frame, in the first call 369 | if (!tooltip_washovered){ 370 | tooltip_time = time; 371 | tooltip_shownid = 0; 372 | tooltip_hoveredid = 0; 373 | } 374 | tooltip_thisframe = thisframe; 375 | tooltip_washovered = false; 376 | } 377 | 378 | if (g->HoveredId == id){ 379 | // If no tooltip is being shown and the mouse moves from one tooltip element 380 | // to another, this is the same as if it moved from a zone without any 381 | // tooltip elements. 382 | if (id != tooltip_hoveredid && tooltip_shownid == 0) 383 | tooltip_time = time; 384 | 385 | if (tooltip_lastactive != 0.f) 386 | delay = fmin(delay,fmax(tooltip_time - tooltip_lastactive,0.f)); 387 | 388 | tooltip_washovered = true; 389 | tooltip_hoveredid = id; 390 | 391 | if (time - tooltip_time > delay){ 392 | tooltip_shownid = id; 393 | tooltip_lastactive = time; 394 | BeginTooltip(); 395 | PushTextWrapPos(maxwidth); 396 | PushFont(font); 397 | TextUnformatted(desc); 398 | PopFont(); 399 | PopTextWrapPos(); 400 | EndTooltip(); 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /imgui_widgets.h: -------------------------------------------------------------------------------- 1 | // -*-c++-*- 2 | /* 3 | Copyright (c) 2017 Alberto Otero de la Roza 4 | , Robin Myhr , Isaac 5 | Visintainer , Richard Greaves , Ángel 6 | Martín Pendás and Víctor Luaña 7 | . 8 | 9 | critic2 is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or (at 12 | your option) any later version. 13 | 14 | critic2 is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | // Several widgets independent from each other are provided by this 24 | // file. See below for a description. 25 | 26 | #ifndef IMGUI_WIDGETS_H 27 | #define IMGUI_WIDGETS_H 28 | 29 | #include "imgui_widgets.h" 30 | #include "imgui.h" 31 | #include "imgui_internal.h" 32 | 33 | using namespace std; 34 | 35 | // Helper functions 36 | static inline ImVec2 operator+(ImVec2 lhs, ImVec2 rhs) { 37 | return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); 38 | } 39 | static inline ImVec2 operator-(ImVec2 lhs, ImVec2 rhs) { 40 | return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); 41 | } 42 | static inline ImVec2 operator*(ImVec2 lhs, float rhs) { 43 | return ImVec2(lhs.x*rhs, lhs.y*rhs); 44 | } 45 | static inline ImVec4 OpaqueColor(ImGuiCol_ color, float newalpha){ 46 | ImGuiContext *g = ImGui::GetCurrentContext(); 47 | ImVec4 col = g->Style.Colors[color]; 48 | col.w = newalpha; 49 | return col; 50 | } 51 | static inline ImVec4 TransparentColor(ImGuiCol_ color){ 52 | const float small_alpha = 1e-15; 53 | ImGuiContext *g = ImGui::GetCurrentContext(); 54 | ImVec4 col = g->Style.Colors[color]; 55 | col.w = small_alpha; 56 | return col; 57 | } 58 | 59 | // Colors for the widgets 60 | enum ImGuiColWidgets_ { 61 | ImGuiColWidgets_Slidingbar, 62 | ImGuiColWidgets_SlidingbarHovered, 63 | ImGuiColWidgets_SlidingbarActive, 64 | ImGuiColWidgets_Tab, 65 | ImGuiColWidgets_TabHovered, 66 | ImGuiColWidgets_TabPressed, 67 | ImGuiColWidgets_TabActive, 68 | ImGuiColWidgets_TabXFg, 69 | ImGuiColWidgets_TabXFgHovered, 70 | ImGuiColWidgets_TabXFgActive, 71 | ImGuiColWidgets_TabXBg, 72 | ImGuiColWidgets_TabXBgHovered, 73 | ImGuiColWidgets_TabXBgActive, 74 | ImGuiColWidgets_TabBorder, 75 | ImGuiColWidgets_LiftGrip, 76 | ImGuiColWidgets_LiftGripHovered, 77 | ImGuiColWidgets_LiftGripActive, 78 | ImGuiColWidgets_DropTarget, 79 | ImGuiColWidgets_DropTargetActive, 80 | ImGuiColWidgets_COUNT, 81 | }; 82 | 83 | // Style for the widgets 84 | struct ImGuiStyleWidgets_ { 85 | ImVec4 Colors[ImGuiColWidgets_COUNT]; 86 | float TabRounding; 87 | float TabBorderSize; 88 | float DropTargetLooseness; 89 | float DropTargetMinsizeEdge; 90 | float DropTargetMaxsizeEdge; 91 | float DropTargetEdgeFraction; 92 | float DropTargetFullFraction; 93 | float TabHeight; 94 | float TabMaxWidth; 95 | float CascadeIncrement; 96 | float SlidingBarWidth; 97 | 98 | void DefaultStyle(){ 99 | TabRounding = 7.0f; 100 | TabBorderSize = 0.0f; 101 | DropTargetLooseness = 4.0f; 102 | DropTargetMinsizeEdge = 20.f; 103 | DropTargetMaxsizeEdge = 20.f; 104 | DropTargetEdgeFraction = 0.1f; 105 | DropTargetFullFraction = 0.4f; 106 | TabHeight = 19.0f; 107 | TabMaxWidth = 100.f; 108 | CascadeIncrement = 25.f; 109 | SlidingBarWidth = 4.f; 110 | } 111 | 112 | void DefaultColors(){ 113 | Colors[ImGuiColWidgets_Slidingbar] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); 114 | Colors[ImGuiColWidgets_SlidingbarHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f); 115 | Colors[ImGuiColWidgets_SlidingbarActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f); 116 | Colors[ImGuiColWidgets_Tab] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); 117 | Colors[ImGuiColWidgets_TabHovered] = ImVec4(0.45f, 0.45f, 0.90f, 1.00f); 118 | Colors[ImGuiColWidgets_TabPressed] = ImVec4(0.46f, 0.54f, 0.80f, 1.00f); 119 | Colors[ImGuiColWidgets_TabActive] = ImVec4(0.53f, 0.53f, 0.87f, 1.00f); 120 | Colors[ImGuiColWidgets_TabXFg] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 121 | Colors[ImGuiColWidgets_TabXFgHovered] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 122 | Colors[ImGuiColWidgets_TabXFgActive] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); 123 | Colors[ImGuiColWidgets_TabXBg] = ImVec4(0.80f, 0.20f, 0.00f, 0.00f); 124 | Colors[ImGuiColWidgets_TabXBgHovered] = ImVec4(0.80f, 0.20f, 0.00f, 1.00f); 125 | Colors[ImGuiColWidgets_TabXBgActive] = ImVec4(0.60f, 0.20f, 0.00f, 1.00f); 126 | Colors[ImGuiColWidgets_TabBorder] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); 127 | Colors[ImGuiColWidgets_LiftGrip] = ImVec4(0.60f, 0.20f, 0.00f, 1.00f); 128 | Colors[ImGuiColWidgets_LiftGripHovered] = ImVec4(0.80f, 0.40f, 0.20f, 1.00f); 129 | Colors[ImGuiColWidgets_LiftGripActive] = ImVec4(1.00f, 0.40f, 0.20f, 1.00f); 130 | Colors[ImGuiColWidgets_DropTarget] = ImVec4(0.43f, 0.43f, 0.43f, 0.43f); 131 | Colors[ImGuiColWidgets_DropTargetActive] = ImVec4(0.80f, 0.80f, 0.80f, 0.80f); 132 | } 133 | 134 | // gettabheight gettabwidth getslidingbarwidth ... 135 | ImGuiStyleWidgets_(){ 136 | DefaultStyle(); 137 | DefaultColors(); 138 | }; 139 | }; 140 | extern ImGuiStyleWidgets_ ImGuiStyleWidgets; 141 | 142 | // Widgets added to ImGui 143 | namespace ImGui{ 144 | // Returns true if mouse is hovering the inside of a convex 145 | // polygon. 146 | bool IsMouseHoveringConvexPoly(const ImVec2* points, const int num_points); 147 | 148 | // Sliding bar for splits. label: used to calculate the ID. window: 149 | // window containing the bar. pos: position of the top left of the bar on 150 | // input and output. size: size of the bar. minx and maxx: minimum and maximum 151 | // positions in direction direction (1=x, 2=y). 152 | void SlidingBar(const char* label, ImGuiWindow* window, ImVec2 *pos, ImVec2 size, 153 | float minx, float maxx, int direction); 154 | 155 | // Button with a clickable "X" at the end. label: label for the 156 | // button (and generates the ID of the main button). size: size of 157 | // the button. activetab: whether the button uses the "active" or 158 | // "inactive" color. scrollbarcol: if true, use scroll bar grab 159 | // colors (otherwise, use framebg colors). p_open whether the "x" is 160 | // shown and close status. dragged: on output, true if the button is 161 | // being dragged. dclicked: on output, true if the button was double 162 | // clicked. closeclicked: on output, true if the X has been clicked. 163 | // alphamul: alpha multiplier for all colors. Returns true if the 164 | // main part of the button (not the x) has been clicked. 165 | bool ButtonWithX(const char* label, const ImVec2& size, bool activetab, 166 | bool *p_open, bool *dragged, bool *dclicked, float alpha = 1.f); 167 | 168 | // A resize grip drawn on window that controls the size of cwindow. 169 | // On output, dclicked is true if double-click (auto-resize) 170 | // happened. 171 | void ResizeGripOther(const char *label, ImGuiWindow* window, ImGuiWindow* cwindow, bool *dclicked=nullptr); 172 | 173 | // Lift grip. A grip the with button colors drawn on the bottom left 174 | // corner of the window. True if the grip is clicked. 175 | bool LiftGrip(const char *label, ImGuiWindow* window); 176 | 177 | // xxxx // 178 | bool ImageInteractive(ImTextureID texture, float a, bool *hover, ImRect *vrect); 179 | 180 | // xxxx // 181 | bool InvisibleButtonEx(const char* str_id, const ImVec2& size_arg, bool* hovered, bool *held); 182 | 183 | // xxxx // 184 | void AttachTooltip(const char* desc, float delay, float maxwidth, ImFont* font); 185 | 186 | } // namespace ImGui 187 | 188 | #endif 189 | 190 | --------------------------------------------------------------------------------