diff options
| author | Steven Van Dorp <steven@vandorp.lu> | 2026-02-06 10:00:08 +0100 |
|---|---|---|
| committer | Steven Van Dorp <steven@vandorp.lu> | 2026-02-06 10:00:08 +0100 |
| commit | 0d8aeb6464103ec8e35c10c448bf08b0b9edc156 (patch) | |
| tree | 80ab47bf3e0f137856d8494753e774c8456c85c8 /README.md | |
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..988a52e --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# minimgui + +Experimental immediate-mode GUI without IDs. + +# Libraries + +- [stb_image](https://github.com/nothings/stb/blob/master/stb_image.h) +- [stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) + +# Scope And Purpose + +This is mostly just a proof-of-concept. It's not intended to be used for serious work yet. +There's currently just a software backend, so no accelerated graphics yet. +Currently, only Linux + X11 is supported. + +IMGUIs typically come in two forms: + - Dear ImGui-like: functions either explicitly accept an ID argument or use + static arguments (like labels) to generate an ID. The obvious problems + are that the user has to manually make sure the IDs are unique, since + using the same ID multiple times will cause issues. Serious + implementations use an ID stack to mitigate this, however, it can still + cause issues: + [Dear ImGui FAQ](https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system) + - Unity-like: Unity's IMGUI implicitly assigns incremental IDs that reset every frame. + This fixes the problem of always needing a unique value and simplifies the API. + However, IDs are still used, it's just largely opaque to the user. + Because of the per-frame incremental nature of IDs, aliasing can happen dynamically + depending on which code path is executed, which is very annoying to debug and fix, + since you have to increase the ID counter by the exact amount necessary + to make up for the omitted GUI calls, which isn't always 1:1, since more complex calls + might use multiple IDs. + +The reason conventional IMGUIs use IDs is that they need to 1. remember +state across frames and 2. detect when one UI element hides another. The first +problem is trivially solved by pushing the responsibility of storing state onto +the user, so IDs aren't strictly necessary here. This might seem like more +work for the user, but in practice, anywhere there's state, the user's already +storing some data. For example, text-fields require the user to store the +string anyway, so using a state object as opposed to a string doesn't add +complexity for the user. The only difference is that he now needs to access the +field of a struct (or call a toString() method) to access the string, instead +of storing the string directly. The second problem is more subtle. Take the +following pseudocode: +``` +rect = getSomeRect(); +if (button("Button 1", rect)) + print("Button 1 pressed"); +if (button("Button 2", rect)) + print("Button 2 pressed"); +``` +Both Button 1 and 2 are in the same position on the screen, but with back-to-front +rendering, Button 2 is drawn on top. However, the code for Button 1 executes first, +so the implementation can't just return true if the cursor is inside Button 1's +bounds. In practice, IMGUIs have some sort of `clicked_id` and `propagated_click_id`. +When the user clicks, the code for Button 1 runs, which sets `propagated_click_id` +to "Button 1", then the code for Button 2 runs, which sets `propagated_click_id` +to "Button 2". At the end of the frame, `clicked_id` is set to the last value of +`propagated_click_id` (in this case "Button 2"), and then, on the next frame, +`button("Button 2", rect)` will return true. + +There is a related, more complex, aliasing/desync bug that mostly occurs when dealing +with text input fields/areas in Unity-like (incremental) IMGUIs: +Even when the user stores the states of input elements, +some level of arbitration between the inputs is necessary to avoid situations like +multiple input fields being focused simultaneously. So the library internally stores +the ID of the currently focused element, which leads to the aforementioned problems +with IDs changing depending on which code paths are taken, leading to focus changing +seemingly at random. + +Minimgui completely eliminates IDs. +This is done by rendering front-to-back, instead of the usual back-to-front. +If we simply declare that elements will be rendered front-to-back, "Button 1" +can just return true (and set some internal `click_consumed` bool to inhibit +following elements from handling clicks), since it's the top-most element. +This has two benefits: Firstly and most obviously, we no longer need IDs. Secondly, +we no longer have a 1-frame delay, since we immediately know whether an element +was clicked. Of course, conventional IMGUIs can mitigate this in all +sorts of ways: Unity does multiple GUI passes, which also allows automatically managing +more complex layouts (though in practice this is difficult to work with). Some implementations +do a hit test on click-start and return the hit result on click-end, though of course +this complicates internals and reduces flexibility. + +While front-to-back might seem awkward, at the end of the day it's just a different +convention, and all the weirdness from having to dispatch rendering commands +backwards is limited to low-level internal functions that most users will never +interact with. So the only thing that changes from an API point-of-view is that +the user no longer needs to worry about IDs or aliasing, and the mental model +of drawing one element on top of another is replaced by drawing one element +behind another. + +# Roadmap + +- [x] Figure out a way to have an imgui without IDs +- [x] Implement MVP + - [x] Rectangle + - [x] Image + - [x] Click + - [x] Drag and Drop + - [x] Rudimentary Text +- [ ] Port to MS Windows +- [ ] Add OpenGL backend +- [ ] Clean up project + - [ ] Separate input handling library from project + - [ ] Separate window handling library from project + - [ ] Isolate Backends + - [ ] Remove example/testing app + +# Build + +Requires zig 0.15 + +## As Executable (and run) + +``` +git clone git.vandorp.lu/minimgui +cd minimgui +zig build -Demit-exe +zig-out/bin/minimgui +``` + +## As Library (C-ABI) + +``` +git clone git.vandorp.lu/minimgui +cd minimgui +zig build -Demit-lib +``` + +# Examples + +See src/app/app.zig, bindings/main.c, and bindings/main.py + +# Contributing + +If you have any ideas or issues, send them to steven@vandorp.lu. + +If you want to modify code, make your changes in a separate branch, +generate the patch(es) (`git format-patch master` from inside your branch) +and send the patch file(s) to steven@vandorp.lu. + +# License + +MIT License + +Copyright (c) 2026 Steven Van Dorp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + |
