git repos / blockattack-game

blame: source/code/Libs/imgui/imgui.cpp

normal view · raw

1193b4be Poul Sander 2025-02-03 21:28 1
// dear imgui, v1.91.4
1193b4be Poul Sander 2025-02-03 21:28 2
// (main code and documentation)
1193b4be Poul Sander 2025-02-03 21:28 3
1193b4be Poul Sander 2025-02-03 21:28 4
// Help:
1193b4be Poul Sander 2025-02-03 21:28 5
// - See links below.
1193b4be Poul Sander 2025-02-03 21:28 6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
1193b4be Poul Sander 2025-02-03 21:28 7
// - Read top of imgui.cpp for more details, links and comments.
1193b4be Poul Sander 2025-02-03 21:28 8
1193b4be Poul Sander 2025-02-03 21:28 9
// Resources:
1193b4be Poul Sander 2025-02-03 21:28 10
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
1193b4be Poul Sander 2025-02-03 21:28 11
// - Homepage ................... https://github.com/ocornut/imgui
1193b4be Poul Sander 2025-02-03 21:28 12
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
1193b4be Poul Sander 2025-02-03 21:28 13
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
1193b4be Poul Sander 2025-02-03 21:28 14
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
1193b4be Poul Sander 2025-02-03 21:28 15
//   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
1193b4be Poul Sander 2025-02-03 21:28 16
//   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
1193b4be Poul Sander 2025-02-03 21:28 17
//   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
1193b4be Poul Sander 2025-02-03 21:28 18
//   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary
1193b4be Poul Sander 2025-02-03 21:28 19
//   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools
1193b4be Poul Sander 2025-02-03 21:28 20
//   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
1193b4be Poul Sander 2025-02-03 21:28 21
// - Issues & support ........... https://github.com/ocornut/imgui/issues
1193b4be Poul Sander 2025-02-03 21:28 22
// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
1193b4be Poul Sander 2025-02-03 21:28 23
1193b4be Poul Sander 2025-02-03 21:28 24
// For first-time users having issues compiling/linking/running/loading fonts:
1193b4be Poul Sander 2025-02-03 21:28 25
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
1193b4be Poul Sander 2025-02-03 21:28 26
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
1193b4be Poul Sander 2025-02-03 21:28 27
1193b4be Poul Sander 2025-02-03 21:28 28
// Copyright (c) 2014-2024 Omar Cornut
1193b4be Poul Sander 2025-02-03 21:28 29
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
1193b4be Poul Sander 2025-02-03 21:28 30
// See LICENSE.txt for copyright and licensing details (standard MIT License).
1193b4be Poul Sander 2025-02-03 21:28 31
// This library is free but needs your support to sustain development and maintenance.
1193b4be Poul Sander 2025-02-03 21:28 32
// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
1193b4be Poul Sander 2025-02-03 21:28 33
// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
1193b4be Poul Sander 2025-02-03 21:28 34
// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1193b4be Poul Sander 2025-02-03 21:28 35
1193b4be Poul Sander 2025-02-03 21:28 36
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
1193b4be Poul Sander 2025-02-03 21:28 37
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
1193b4be Poul Sander 2025-02-03 21:28 38
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
1193b4be Poul Sander 2025-02-03 21:28 39
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
1193b4be Poul Sander 2025-02-03 21:28 40
// to a better solution or official support for them.
1193b4be Poul Sander 2025-02-03 21:28 41
1193b4be Poul Sander 2025-02-03 21:28 42
/*
1193b4be Poul Sander 2025-02-03 21:28 43
1193b4be Poul Sander 2025-02-03 21:28 44
Index of this file:
1193b4be Poul Sander 2025-02-03 21:28 45
1193b4be Poul Sander 2025-02-03 21:28 46
DOCUMENTATION
1193b4be Poul Sander 2025-02-03 21:28 47
1193b4be Poul Sander 2025-02-03 21:28 48
- MISSION STATEMENT
1193b4be Poul Sander 2025-02-03 21:28 49
- CONTROLS GUIDE
1193b4be Poul Sander 2025-02-03 21:28 50
- PROGRAMMER GUIDE
1193b4be Poul Sander 2025-02-03 21:28 51
  - READ FIRST
1193b4be Poul Sander 2025-02-03 21:28 52
  - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
1193b4be Poul Sander 2025-02-03 21:28 53
  - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
1193b4be Poul Sander 2025-02-03 21:28 54
  - HOW A SIMPLE APPLICATION MAY LOOK LIKE
1193b4be Poul Sander 2025-02-03 21:28 55
  - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
1193b4be Poul Sander 2025-02-03 21:28 56
- API BREAKING CHANGES (read me when you update!)
1193b4be Poul Sander 2025-02-03 21:28 57
- FREQUENTLY ASKED QUESTIONS (FAQ)
1193b4be Poul Sander 2025-02-03 21:28 58
  - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
1193b4be Poul Sander 2025-02-03 21:28 59
1193b4be Poul Sander 2025-02-03 21:28 60
CODE
1193b4be Poul Sander 2025-02-03 21:28 61
(search for "[SECTION]" in the code to find them)
1193b4be Poul Sander 2025-02-03 21:28 62
1193b4be Poul Sander 2025-02-03 21:28 63
// [SECTION] INCLUDES
1193b4be Poul Sander 2025-02-03 21:28 64
// [SECTION] FORWARD DECLARATIONS
1193b4be Poul Sander 2025-02-03 21:28 65
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1193b4be Poul Sander 2025-02-03 21:28 66
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1193b4be Poul Sander 2025-02-03 21:28 67
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1193b4be Poul Sander 2025-02-03 21:28 68
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1193b4be Poul Sander 2025-02-03 21:28 69
// [SECTION] MISC HELPERS/UTILITIES (File functions)
1193b4be Poul Sander 2025-02-03 21:28 70
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1193b4be Poul Sander 2025-02-03 21:28 71
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
1193b4be Poul Sander 2025-02-03 21:28 72
// [SECTION] ImGuiStorage
1193b4be Poul Sander 2025-02-03 21:28 73
// [SECTION] ImGuiTextFilter
1193b4be Poul Sander 2025-02-03 21:28 74
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
1193b4be Poul Sander 2025-02-03 21:28 75
// [SECTION] ImGuiListClipper
1193b4be Poul Sander 2025-02-03 21:28 76
// [SECTION] STYLING
1193b4be Poul Sander 2025-02-03 21:28 77
// [SECTION] RENDER HELPERS
1193b4be Poul Sander 2025-02-03 21:28 78
// [SECTION] INITIALIZATION, SHUTDOWN
1193b4be Poul Sander 2025-02-03 21:28 79
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
1193b4be Poul Sander 2025-02-03 21:28 80
// [SECTION] ID STACK
1193b4be Poul Sander 2025-02-03 21:28 81
// [SECTION] INPUTS
1193b4be Poul Sander 2025-02-03 21:28 82
// [SECTION] ERROR CHECKING, STATE RECOVERY
1193b4be Poul Sander 2025-02-03 21:28 83
// [SECTION] ITEM SUBMISSION
1193b4be Poul Sander 2025-02-03 21:28 84
// [SECTION] LAYOUT
1193b4be Poul Sander 2025-02-03 21:28 85
// [SECTION] SCROLLING
1193b4be Poul Sander 2025-02-03 21:28 86
// [SECTION] TOOLTIPS
1193b4be Poul Sander 2025-02-03 21:28 87
// [SECTION] POPUPS
1193b4be Poul Sander 2025-02-03 21:28 88
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
1193b4be Poul Sander 2025-02-03 21:28 89
// [SECTION] DRAG AND DROP
1193b4be Poul Sander 2025-02-03 21:28 90
// [SECTION] LOGGING/CAPTURING
1193b4be Poul Sander 2025-02-03 21:28 91
// [SECTION] SETTINGS
1193b4be Poul Sander 2025-02-03 21:28 92
// [SECTION] LOCALIZATION
1193b4be Poul Sander 2025-02-03 21:28 93
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
1193b4be Poul Sander 2025-02-03 21:28 94
// [SECTION] DOCKING
1193b4be Poul Sander 2025-02-03 21:28 95
// [SECTION] PLATFORM DEPENDENT HELPERS
1193b4be Poul Sander 2025-02-03 21:28 96
// [SECTION] METRICS/DEBUGGER WINDOW
1193b4be Poul Sander 2025-02-03 21:28 97
// [SECTION] DEBUG LOG WINDOW
1193b4be Poul Sander 2025-02-03 21:28 98
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
1193b4be Poul Sander 2025-02-03 21:28 99
1193b4be Poul Sander 2025-02-03 21:28 100
*/
1193b4be Poul Sander 2025-02-03 21:28 101
1193b4be Poul Sander 2025-02-03 21:28 102
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 103
// DOCUMENTATION
1193b4be Poul Sander 2025-02-03 21:28 104
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 105
1193b4be Poul Sander 2025-02-03 21:28 106
/*
1193b4be Poul Sander 2025-02-03 21:28 107
1193b4be Poul Sander 2025-02-03 21:28 108
 MISSION STATEMENT
1193b4be Poul Sander 2025-02-03 21:28 109
 =================
1193b4be Poul Sander 2025-02-03 21:28 110
1193b4be Poul Sander 2025-02-03 21:28 111
 - Easy to use to create code-driven and data-driven tools.
1193b4be Poul Sander 2025-02-03 21:28 112
 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
1193b4be Poul Sander 2025-02-03 21:28 113
 - Easy to hack and improve.
1193b4be Poul Sander 2025-02-03 21:28 114
 - Minimize setup and maintenance.
1193b4be Poul Sander 2025-02-03 21:28 115
 - Minimize state storage on user side.
1193b4be Poul Sander 2025-02-03 21:28 116
 - Minimize state synchronization.
1193b4be Poul Sander 2025-02-03 21:28 117
 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
1193b4be Poul Sander 2025-02-03 21:28 118
 - Efficient runtime and memory consumption.
1193b4be Poul Sander 2025-02-03 21:28 119
1193b4be Poul Sander 2025-02-03 21:28 120
 Designed primarily for developers and content-creators, not the typical end-user!
1193b4be Poul Sander 2025-02-03 21:28 121
 Some of the current weaknesses (which we aim to address in the future) includes:
1193b4be Poul Sander 2025-02-03 21:28 122
1193b4be Poul Sander 2025-02-03 21:28 123
 - Doesn't look fancy.
1193b4be Poul Sander 2025-02-03 21:28 124
 - Limited layout features, intricate layouts are typically crafted in code.
1193b4be Poul Sander 2025-02-03 21:28 125
1193b4be Poul Sander 2025-02-03 21:28 126
1193b4be Poul Sander 2025-02-03 21:28 127
 CONTROLS GUIDE
1193b4be Poul Sander 2025-02-03 21:28 128
 ==============
1193b4be Poul Sander 2025-02-03 21:28 129
1193b4be Poul Sander 2025-02-03 21:28 130
 - MOUSE CONTROLS
1193b4be Poul Sander 2025-02-03 21:28 131
   - Mouse wheel:                   Scroll vertically.
1193b4be Poul Sander 2025-02-03 21:28 132
   - SHIFT+Mouse wheel:             Scroll horizontally.
1193b4be Poul Sander 2025-02-03 21:28 133
   - Click [X]:                     Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
1193b4be Poul Sander 2025-02-03 21:28 134
   - Click ^, Double-Click title:   Collapse window.
1193b4be Poul Sander 2025-02-03 21:28 135
   - Drag on corner/border:         Resize window (double-click to auto fit window to its contents).
1193b4be Poul Sander 2025-02-03 21:28 136
   - Drag on any empty space:       Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
1193b4be Poul Sander 2025-02-03 21:28 137
   - Left-click outside popup:      Close popup stack (right-click over underlying popup: Partially close popup stack).
1193b4be Poul Sander 2025-02-03 21:28 138
1193b4be Poul Sander 2025-02-03 21:28 139
 - TEXT EDITOR
1193b4be Poul Sander 2025-02-03 21:28 140
   - Hold SHIFT or Drag Mouse:      Select text.
1193b4be Poul Sander 2025-02-03 21:28 141
   - CTRL+Left/Right:               Word jump.
1193b4be Poul Sander 2025-02-03 21:28 142
   - CTRL+Shift+Left/Right:         Select words.
1193b4be Poul Sander 2025-02-03 21:28 143
   - CTRL+A or Double-Click:        Select All.
1193b4be Poul Sander 2025-02-03 21:28 144
   - CTRL+X, CTRL+C, CTRL+V:        Use OS clipboard.
1193b4be Poul Sander 2025-02-03 21:28 145
   - CTRL+Z, CTRL+Y:                Undo, Redo.
1193b4be Poul Sander 2025-02-03 21:28 146
   - ESCAPE:                        Revert text to its original value.
1193b4be Poul Sander 2025-02-03 21:28 147
   - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
1193b4be Poul Sander 2025-02-03 21:28 148
1193b4be Poul Sander 2025-02-03 21:28 149
 - KEYBOARD CONTROLS
1193b4be Poul Sander 2025-02-03 21:28 150
   - Basic:
1193b4be Poul Sander 2025-02-03 21:28 151
     - Tab, SHIFT+Tab               Cycle through text editable fields.
1193b4be Poul Sander 2025-02-03 21:28 152
     - CTRL+Tab, CTRL+Shift+Tab     Cycle through windows.
1193b4be Poul Sander 2025-02-03 21:28 153
     - CTRL+Click                   Input text into a Slider or Drag widget.
1193b4be Poul Sander 2025-02-03 21:28 154
   - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
1193b4be Poul Sander 2025-02-03 21:28 155
     - Tab, SHIFT+Tab:              Cycle through every items.
1193b4be Poul Sander 2025-02-03 21:28 156
     - Arrow keys                   Move through items using directional navigation. Tweak value.
1193b4be Poul Sander 2025-02-03 21:28 157
     - Arrow keys + Alt, Shift      Tweak slower, tweak faster (when using arrow keys).
1193b4be Poul Sander 2025-02-03 21:28 158
     - Enter                        Activate item (prefer text input when possible).
1193b4be Poul Sander 2025-02-03 21:28 159
     - Space                        Activate item (prefer tweaking with arrows when possible).
1193b4be Poul Sander 2025-02-03 21:28 160
     - Escape                       Deactivate item, leave child window, close popup.
1193b4be Poul Sander 2025-02-03 21:28 161
     - Page Up, Page Down           Previous page, next page.
1193b4be Poul Sander 2025-02-03 21:28 162
     - Home, End                    Scroll to top, scroll to bottom.
1193b4be Poul Sander 2025-02-03 21:28 163
     - Alt                          Toggle between scrolling layer and menu layer.
1193b4be Poul Sander 2025-02-03 21:28 164
     - CTRL+Tab then Ctrl+Arrows    Move window. Hold SHIFT to resize instead of moving.
1193b4be Poul Sander 2025-02-03 21:28 165
   - Output when ImGuiConfigFlags_NavEnableKeyboard set,
1193b4be Poul Sander 2025-02-03 21:28 166
     - io.WantCaptureKeyboard flag is set when keyboard is claimed.
1193b4be Poul Sander 2025-02-03 21:28 167
     - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
1193b4be Poul Sander 2025-02-03 21:28 168
     - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
1193b4be Poul Sander 2025-02-03 21:28 169
1193b4be Poul Sander 2025-02-03 21:28 170
 - GAMEPAD CONTROLS
1193b4be Poul Sander 2025-02-03 21:28 171
   - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
1193b4be Poul Sander 2025-02-03 21:28 172
   - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
1193b4be Poul Sander 2025-02-03 21:28 173
   - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
1193b4be Poul Sander 2025-02-03 21:28 174
   - Backend support: backend needs to:
1193b4be Poul Sander 2025-02-03 21:28 175
      - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
1193b4be Poul Sander 2025-02-03 21:28 176
      - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
1193b4be Poul Sander 2025-02-03 21:28 177
        Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
1193b4be Poul Sander 2025-02-03 21:28 178
      - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!
1193b4be Poul Sander 2025-02-03 21:28 179
   - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
1193b4be Poul Sander 2025-02-03 21:28 180
     with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
1193b4be Poul Sander 2025-02-03 21:28 181
1193b4be Poul Sander 2025-02-03 21:28 182
 - REMOTE INPUTS SHARING & MOUSE EMULATION
1193b4be Poul Sander 2025-02-03 21:28 183
   - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
1193b4be Poul Sander 2025-02-03 21:28 184
   - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
1193b4be Poul Sander 2025-02-03 21:28 185
     in order to share your PC mouse/keyboard.
1193b4be Poul Sander 2025-02-03 21:28 186
   - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
1193b4be Poul Sander 2025-02-03 21:28 187
   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
1193b4be Poul Sander 2025-02-03 21:28 188
     Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
1193b4be Poul Sander 2025-02-03 21:28 189
     When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
1193b4be Poul Sander 2025-02-03 21:28 190
     When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
1193b4be Poul Sander 2025-02-03 21:28 191
     (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
1193b4be Poul Sander 2025-02-03 21:28 192
     (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
1193b4be Poul Sander 2025-02-03 21:28 193
     to set a boolean to ignore your other external mouse positions until the external source is moved again.)
1193b4be Poul Sander 2025-02-03 21:28 194
1193b4be Poul Sander 2025-02-03 21:28 195
1193b4be Poul Sander 2025-02-03 21:28 196
 PROGRAMMER GUIDE
1193b4be Poul Sander 2025-02-03 21:28 197
 ================
1193b4be Poul Sander 2025-02-03 21:28 198
1193b4be Poul Sander 2025-02-03 21:28 199
 READ FIRST
1193b4be Poul Sander 2025-02-03 21:28 200
 ----------
1193b4be Poul Sander 2025-02-03 21:28 201
 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
1193b4be Poul Sander 2025-02-03 21:28 202
 - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
1193b4be Poul Sander 2025-02-03 21:28 203
   The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
1193b4be Poul Sander 2025-02-03 21:28 204
   data retention on your side, less state duplication, less state synchronization, fewer bugs.
1193b4be Poul Sander 2025-02-03 21:28 205
 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
1193b4be Poul Sander 2025-02-03 21:28 206
   Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
1193b4be Poul Sander 2025-02-03 21:28 207
 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
1193b4be Poul Sander 2025-02-03 21:28 208
 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
1193b4be Poul Sander 2025-02-03 21:28 209
   You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
1193b4be Poul Sander 2025-02-03 21:28 210
 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
1193b4be Poul Sander 2025-02-03 21:28 211
   For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
1193b4be Poul Sander 2025-02-03 21:28 212
   where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
1193b4be Poul Sander 2025-02-03 21:28 213
 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
1193b4be Poul Sander 2025-02-03 21:28 214
 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
1193b4be Poul Sander 2025-02-03 21:28 215
   If you get an assert, read the messages and comments around the assert.
1193b4be Poul Sander 2025-02-03 21:28 216
 - This codebase aims to be highly optimized:
1193b4be Poul Sander 2025-02-03 21:28 217
   - A typical idle frame should never call malloc/free.
1193b4be Poul Sander 2025-02-03 21:28 218
   - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
1193b4be Poul Sander 2025-02-03 21:28 219
   - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
1193b4be Poul Sander 2025-02-03 21:28 220
     Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
1193b4be Poul Sander 2025-02-03 21:28 221
 - This codebase aims to be both highly opinionated and highly flexible:
1193b4be Poul Sander 2025-02-03 21:28 222
   - This code works because of the things it choose to solve or not solve.
1193b4be Poul Sander 2025-02-03 21:28 223
   - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
1193b4be Poul Sander 2025-02-03 21:28 224
     and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
1193b4be Poul Sander 2025-02-03 21:28 225
     This is to increase compatibility, increase maintainability and facilitate use from other languages.
1193b4be Poul Sander 2025-02-03 21:28 226
   - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
1193b4be Poul Sander 2025-02-03 21:28 227
     See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
1193b4be Poul Sander 2025-02-03 21:28 228
     We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
1193b4be Poul Sander 2025-02-03 21:28 229
   - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
1193b4be Poul Sander 2025-02-03 21:28 230
     (so don't use ImVector in your code or at our own risk!).
1193b4be Poul Sander 2025-02-03 21:28 231
   - Building: We don't use nor mandate a build system for the main library.
1193b4be Poul Sander 2025-02-03 21:28 232
     This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
1193b4be Poul Sander 2025-02-03 21:28 233
     This is also because providing a build system for the main library would be of little-value.
1193b4be Poul Sander 2025-02-03 21:28 234
     The build problems are almost never coming from the main library but from specific backends.
1193b4be Poul Sander 2025-02-03 21:28 235
1193b4be Poul Sander 2025-02-03 21:28 236
1193b4be Poul Sander 2025-02-03 21:28 237
 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
1193b4be Poul Sander 2025-02-03 21:28 238
 ----------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 239
 - Update submodule or copy/overwrite every file.
1193b4be Poul Sander 2025-02-03 21:28 240
 - About imconfig.h:
1193b4be Poul Sander 2025-02-03 21:28 241
   - You may modify your copy of imconfig.h, in this case don't overwrite it.
1193b4be Poul Sander 2025-02-03 21:28 242
   - or you may locally branch to modify imconfig.h and merge/rebase latest.
1193b4be Poul Sander 2025-02-03 21:28 243
   - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
1193b4be Poul Sander 2025-02-03 21:28 244
     specify a custom path for your imconfig.h file and instead not have to modify the default one.
1193b4be Poul Sander 2025-02-03 21:28 245
1193b4be Poul Sander 2025-02-03 21:28 246
 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
1193b4be Poul Sander 2025-02-03 21:28 247
 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
1193b4be Poul Sander 2025-02-03 21:28 248
 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
1193b4be Poul Sander 2025-02-03 21:28 249
 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
1193b4be Poul Sander 2025-02-03 21:28 250
   If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
1193b4be Poul Sander 2025-02-03 21:28 251
   from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
1193b4be Poul Sander 2025-02-03 21:28 252
   likely be a comment about it. Please report any issue to the GitHub page!
1193b4be Poul Sander 2025-02-03 21:28 253
 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
1193b4be Poul Sander 2025-02-03 21:28 254
 - Try to keep your copy of Dear ImGui reasonably up to date!
1193b4be Poul Sander 2025-02-03 21:28 255
1193b4be Poul Sander 2025-02-03 21:28 256
1193b4be Poul Sander 2025-02-03 21:28 257
 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
1193b4be Poul Sander 2025-02-03 21:28 258
 ---------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 259
 - See https://github.com/ocornut/imgui/wiki/Getting-Started.
1193b4be Poul Sander 2025-02-03 21:28 260
 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
1193b4be Poul Sander 2025-02-03 21:28 261
 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
1193b4be Poul Sander 2025-02-03 21:28 262
 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
1193b4be Poul Sander 2025-02-03 21:28 263
   It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
1193b4be Poul Sander 2025-02-03 21:28 264
 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
1193b4be Poul Sander 2025-02-03 21:28 265
 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
1193b4be Poul Sander 2025-02-03 21:28 266
 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
1193b4be Poul Sander 2025-02-03 21:28 267
   Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
1193b4be Poul Sander 2025-02-03 21:28 268
   phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
1193b4be Poul Sander 2025-02-03 21:28 269
 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
1193b4be Poul Sander 2025-02-03 21:28 270
 - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
1193b4be Poul Sander 2025-02-03 21:28 271
1193b4be Poul Sander 2025-02-03 21:28 272
1193b4be Poul Sander 2025-02-03 21:28 273
 HOW A SIMPLE APPLICATION MAY LOOK LIKE
1193b4be Poul Sander 2025-02-03 21:28 274
 --------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 275
 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
1193b4be Poul Sander 2025-02-03 21:28 276
 The sub-folders in examples/ contain examples applications following this structure.
1193b4be Poul Sander 2025-02-03 21:28 277
1193b4be Poul Sander 2025-02-03 21:28 278
     // Application init: create a dear imgui context, setup some options, load fonts
1193b4be Poul Sander 2025-02-03 21:28 279
     ImGui::CreateContext();
1193b4be Poul Sander 2025-02-03 21:28 280
     ImGuiIO& io = ImGui::GetIO();
1193b4be Poul Sander 2025-02-03 21:28 281
     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
1193b4be Poul Sander 2025-02-03 21:28 282
     // TODO: Fill optional fields of the io structure later.
1193b4be Poul Sander 2025-02-03 21:28 283
     // TODO: Load TTF/OTF fonts if you don't want to use the default font.
1193b4be Poul Sander 2025-02-03 21:28 284
1193b4be Poul Sander 2025-02-03 21:28 285
     // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
1193b4be Poul Sander 2025-02-03 21:28 286
     ImGui_ImplWin32_Init(hwnd);
1193b4be Poul Sander 2025-02-03 21:28 287
     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
1193b4be Poul Sander 2025-02-03 21:28 288
1193b4be Poul Sander 2025-02-03 21:28 289
     // Application main loop
1193b4be Poul Sander 2025-02-03 21:28 290
     while (true)
1193b4be Poul Sander 2025-02-03 21:28 291
     {
1193b4be Poul Sander 2025-02-03 21:28 292
         // Feed inputs to dear imgui, start new frame
1193b4be Poul Sander 2025-02-03 21:28 293
         ImGui_ImplDX11_NewFrame();
1193b4be Poul Sander 2025-02-03 21:28 294
         ImGui_ImplWin32_NewFrame();
1193b4be Poul Sander 2025-02-03 21:28 295
         ImGui::NewFrame();
1193b4be Poul Sander 2025-02-03 21:28 296
1193b4be Poul Sander 2025-02-03 21:28 297
         // Any application code here
1193b4be Poul Sander 2025-02-03 21:28 298
         ImGui::Text("Hello, world!");
1193b4be Poul Sander 2025-02-03 21:28 299
1193b4be Poul Sander 2025-02-03 21:28 300
         // Render dear imgui into screen
1193b4be Poul Sander 2025-02-03 21:28 301
         ImGui::Render();
1193b4be Poul Sander 2025-02-03 21:28 302
         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
1193b4be Poul Sander 2025-02-03 21:28 303
         g_pSwapChain->Present(1, 0);
1193b4be Poul Sander 2025-02-03 21:28 304
     }
1193b4be Poul Sander 2025-02-03 21:28 305
1193b4be Poul Sander 2025-02-03 21:28 306
     // Shutdown
1193b4be Poul Sander 2025-02-03 21:28 307
     ImGui_ImplDX11_Shutdown();
1193b4be Poul Sander 2025-02-03 21:28 308
     ImGui_ImplWin32_Shutdown();
1193b4be Poul Sander 2025-02-03 21:28 309
     ImGui::DestroyContext();
1193b4be Poul Sander 2025-02-03 21:28 310
1193b4be Poul Sander 2025-02-03 21:28 311
 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
1193b4be Poul Sander 2025-02-03 21:28 312
1193b4be Poul Sander 2025-02-03 21:28 313
     // Application init: create a dear imgui context, setup some options, load fonts
1193b4be Poul Sander 2025-02-03 21:28 314
     ImGui::CreateContext();
1193b4be Poul Sander 2025-02-03 21:28 315
     ImGuiIO& io = ImGui::GetIO();
1193b4be Poul Sander 2025-02-03 21:28 316
     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
1193b4be Poul Sander 2025-02-03 21:28 317
     // TODO: Fill optional fields of the io structure later.
1193b4be Poul Sander 2025-02-03 21:28 318
     // TODO: Load TTF/OTF fonts if you don't want to use the default font.
1193b4be Poul Sander 2025-02-03 21:28 319
1193b4be Poul Sander 2025-02-03 21:28 320
     // Build and load the texture atlas into a texture
1193b4be Poul Sander 2025-02-03 21:28 321
     // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
1193b4be Poul Sander 2025-02-03 21:28 322
     int width, height;
1193b4be Poul Sander 2025-02-03 21:28 323
     unsigned char* pixels = nullptr;
1193b4be Poul Sander 2025-02-03 21:28 324
     io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
1193b4be Poul Sander 2025-02-03 21:28 325
1193b4be Poul Sander 2025-02-03 21:28 326
     // At this point you've got the texture data and you need to upload that to your graphic system:
1193b4be Poul Sander 2025-02-03 21:28 327
     // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
1193b4be Poul Sander 2025-02-03 21:28 328
     // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
1193b4be Poul Sander 2025-02-03 21:28 329
     MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
1193b4be Poul Sander 2025-02-03 21:28 330
     io.Fonts->SetTexID((void*)texture);
1193b4be Poul Sander 2025-02-03 21:28 331
1193b4be Poul Sander 2025-02-03 21:28 332
     // Application main loop
1193b4be Poul Sander 2025-02-03 21:28 333
     while (true)
1193b4be Poul Sander 2025-02-03 21:28 334
     {
1193b4be Poul Sander 2025-02-03 21:28 335
        // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
1193b4be Poul Sander 2025-02-03 21:28 336
        // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
1193b4be Poul Sander 2025-02-03 21:28 337
        io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
1193b4be Poul Sander 2025-02-03 21:28 338
        io.DisplaySize.x = 1920.0f;             // set the current display width
1193b4be Poul Sander 2025-02-03 21:28 339
        io.DisplaySize.y = 1280.0f;             // set the current display height here
1193b4be Poul Sander 2025-02-03 21:28 340
        io.AddMousePosEvent(mouse_x, mouse_y);  // update mouse position
1193b4be Poul Sander 2025-02-03 21:28 341
        io.AddMouseButtonEvent(0, mouse_b[0]);  // update mouse button states
1193b4be Poul Sander 2025-02-03 21:28 342
        io.AddMouseButtonEvent(1, mouse_b[1]);  // update mouse button states
1193b4be Poul Sander 2025-02-03 21:28 343
1193b4be Poul Sander 2025-02-03 21:28 344
        // Call NewFrame(), after this point you can use ImGui::* functions anytime
1193b4be Poul Sander 2025-02-03 21:28 345
        // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
1193b4be Poul Sander 2025-02-03 21:28 346
        ImGui::NewFrame();
1193b4be Poul Sander 2025-02-03 21:28 347
1193b4be Poul Sander 2025-02-03 21:28 348
        // Most of your application code here
1193b4be Poul Sander 2025-02-03 21:28 349
        ImGui::Text("Hello, world!");
1193b4be Poul Sander 2025-02-03 21:28 350
        MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
1193b4be Poul Sander 2025-02-03 21:28 351
        MyGameRender(); // may use any Dear ImGui functions as well!
1193b4be Poul Sander 2025-02-03 21:28 352
1193b4be Poul Sander 2025-02-03 21:28 353
        // Render dear imgui, swap buffers
1193b4be Poul Sander 2025-02-03 21:28 354
        // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
1193b4be Poul Sander 2025-02-03 21:28 355
        ImGui::EndFrame();
1193b4be Poul Sander 2025-02-03 21:28 356
        ImGui::Render();
1193b4be Poul Sander 2025-02-03 21:28 357
        ImDrawData* draw_data = ImGui::GetDrawData();
1193b4be Poul Sander 2025-02-03 21:28 358
        MyImGuiRenderFunction(draw_data);
1193b4be Poul Sander 2025-02-03 21:28 359
        SwapBuffers();
1193b4be Poul Sander 2025-02-03 21:28 360
     }
1193b4be Poul Sander 2025-02-03 21:28 361
1193b4be Poul Sander 2025-02-03 21:28 362
     // Shutdown
1193b4be Poul Sander 2025-02-03 21:28 363
     ImGui::DestroyContext();
1193b4be Poul Sander 2025-02-03 21:28 364
1193b4be Poul Sander 2025-02-03 21:28 365
 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
1193b4be Poul Sander 2025-02-03 21:28 366
 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1193b4be Poul Sander 2025-02-03 21:28 367
 Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
1193b4be Poul Sander 2025-02-03 21:28 368
1193b4be Poul Sander 2025-02-03 21:28 369
1193b4be Poul Sander 2025-02-03 21:28 370
 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
1193b4be Poul Sander 2025-02-03 21:28 371
 ---------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 372
 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
1193b4be Poul Sander 2025-02-03 21:28 373
1193b4be Poul Sander 2025-02-03 21:28 374
    void MyImGuiRenderFunction(ImDrawData* draw_data)
1193b4be Poul Sander 2025-02-03 21:28 375
    {
1193b4be Poul Sander 2025-02-03 21:28 376
       // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
1193b4be Poul Sander 2025-02-03 21:28 377
       // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
1193b4be Poul Sander 2025-02-03 21:28 378
       // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
1193b4be Poul Sander 2025-02-03 21:28 379
       // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
1193b4be Poul Sander 2025-02-03 21:28 380
       // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
1193b4be Poul Sander 2025-02-03 21:28 381
       ImVec2 clip_off = draw_data->DisplayPos;
1193b4be Poul Sander 2025-02-03 21:28 382
       for (int n = 0; n < draw_data->CmdListsCount; n++)
1193b4be Poul Sander 2025-02-03 21:28 383
       {
1193b4be Poul Sander 2025-02-03 21:28 384
          const ImDrawList* cmd_list = draw_data->CmdLists[n];
1193b4be Poul Sander 2025-02-03 21:28 385
          const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui
1193b4be Poul Sander 2025-02-03 21:28 386
          const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui
1193b4be Poul Sander 2025-02-03 21:28 387
          for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
1193b4be Poul Sander 2025-02-03 21:28 388
          {
1193b4be Poul Sander 2025-02-03 21:28 389
             const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
1193b4be Poul Sander 2025-02-03 21:28 390
             if (pcmd->UserCallback)
1193b4be Poul Sander 2025-02-03 21:28 391
             {
1193b4be Poul Sander 2025-02-03 21:28 392
                 pcmd->UserCallback(cmd_list, pcmd);
1193b4be Poul Sander 2025-02-03 21:28 393
             }
1193b4be Poul Sander 2025-02-03 21:28 394
             else
1193b4be Poul Sander 2025-02-03 21:28 395
             {
1193b4be Poul Sander 2025-02-03 21:28 396
                 // Project scissor/clipping rectangles into framebuffer space
1193b4be Poul Sander 2025-02-03 21:28 397
                 ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
1193b4be Poul Sander 2025-02-03 21:28 398
                 ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
1193b4be Poul Sander 2025-02-03 21:28 399
                 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
1193b4be Poul Sander 2025-02-03 21:28 400
                     continue;
1193b4be Poul Sander 2025-02-03 21:28 401
1193b4be Poul Sander 2025-02-03 21:28 402
                 // We are using scissoring to clip some objects. All low-level graphics API should support it.
1193b4be Poul Sander 2025-02-03 21:28 403
                 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
1193b4be Poul Sander 2025-02-03 21:28 404
                 //   (some elements visible outside their bounds) but you can fix that once everything else works!
1193b4be Poul Sander 2025-02-03 21:28 405
                 // - Clipping coordinates are provided in imgui coordinates space:
1193b4be Poul Sander 2025-02-03 21:28 406
                 //   - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
1193b4be Poul Sander 2025-02-03 21:28 407
                 //   - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
1193b4be Poul Sander 2025-02-03 21:28 408
                 //   - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
1193b4be Poul Sander 2025-02-03 21:28 409
                 //     always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
1193b4be Poul Sander 2025-02-03 21:28 410
                 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
1193b4be Poul Sander 2025-02-03 21:28 411
                 MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);
1193b4be Poul Sander 2025-02-03 21:28 412
1193b4be Poul Sander 2025-02-03 21:28 413
                 // The texture for the draw call is specified by pcmd->GetTexID().
1193b4be Poul Sander 2025-02-03 21:28 414
                 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
1193b4be Poul Sander 2025-02-03 21:28 415
                 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
1193b4be Poul Sander 2025-02-03 21:28 416
1193b4be Poul Sander 2025-02-03 21:28 417
                 // Render 'pcmd->ElemCount/3' indexed triangles.
1193b4be Poul Sander 2025-02-03 21:28 418
                 // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
1193b4be Poul Sander 2025-02-03 21:28 419
                 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);
1193b4be Poul Sander 2025-02-03 21:28 420
             }
1193b4be Poul Sander 2025-02-03 21:28 421
          }
1193b4be Poul Sander 2025-02-03 21:28 422
       }
1193b4be Poul Sander 2025-02-03 21:28 423
    }
1193b4be Poul Sander 2025-02-03 21:28 424
1193b4be Poul Sander 2025-02-03 21:28 425
1193b4be Poul Sander 2025-02-03 21:28 426
 API BREAKING CHANGES
1193b4be Poul Sander 2025-02-03 21:28 427
 ====================
1193b4be Poul Sander 2025-02-03 21:28 428
1193b4be Poul Sander 2025-02-03 21:28 429
 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
1193b4be Poul Sander 2025-02-03 21:28 430
 Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
1193b4be Poul Sander 2025-02-03 21:28 431
 When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
1193b4be Poul Sander 2025-02-03 21:28 432
 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
1193b4be Poul Sander 2025-02-03 21:28 433
1193b4be Poul Sander 2025-02-03 21:28 434
(Docking/Viewport Branch)
1193b4be Poul Sander 2025-02-03 21:28 435
 - 2024/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that:
1193b4be Poul Sander 2025-02-03 21:28 436
                          - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore.
1193b4be Poul Sander 2025-02-03 21:28 437
                            you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos)
1193b4be Poul Sander 2025-02-03 21:28 438
                          - likewise io.MousePos and GetMousePos() will use OS coordinates.
1193b4be Poul Sander 2025-02-03 21:28 439
                            If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
1193b4be Poul Sander 2025-02-03 21:28 440
1193b4be Poul Sander 2025-02-03 21:28 441
 - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 442
 - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
1193b4be Poul Sander 2025-02-03 21:28 443
                         moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
1193b4be Poul Sander 2025-02-03 21:28 444
                         kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
1193b4be Poul Sander 2025-02-03 21:28 445
 - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
1193b4be Poul Sander 2025-02-03 21:28 446
                         this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
1193b4be Poul Sander 2025-02-03 21:28 447
                         you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
1193b4be Poul Sander 2025-02-03 21:28 448
                         in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
1193b4be Poul Sander 2025-02-03 21:28 449
                            - May warn:    ImGui::Image((void*)MyTextureData, ...);
1193b4be Poul Sander 2025-02-03 21:28 450
                            - May warn:    ImGui::Image((void*)(intptr_t)MyTextureData, ...);
1193b4be Poul Sander 2025-02-03 21:28 451
                            - Won't warn:  ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
1193b4be Poul Sander 2025-02-03 21:28 452
  -                      note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
1193b4be Poul Sander 2025-02-03 21:28 453
 - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
1193b4be Poul Sander 2025-02-03 21:28 454
                       - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
1193b4be Poul Sander 2025-02-03 21:28 455
                         although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
1193b4be Poul Sander 2025-02-03 21:28 456
 - 2024/09/10 (1.91.2) - internals: using multiple overlayed ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
1193b4be Poul Sander 2025-02-03 21:28 457
                         it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
1193b4be Poul Sander 2025-02-03 21:28 458
 - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
1193b4be Poul Sander 2025-02-03 21:28 459
 - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
1193b4be Poul Sander 2025-02-03 21:28 460
                            - io.GetClipboardTextFn         -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
1193b4be Poul Sander 2025-02-03 21:28 461
                            - io.SetClipboardTextFn         -> platform_io.Platform_SetClipboardTextFn + same as above line.
1193b4be Poul Sander 2025-02-03 21:28 462
                            - io.PlatformOpenInShellFn      -> platform_io.Platform_OpenInShellFn (#7660)
1193b4be Poul Sander 2025-02-03 21:28 463
                            - io.PlatformSetImeDataFn       -> platform_io.Platform_SetImeDataFn
1193b4be Poul Sander 2025-02-03 21:28 464
                            - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
1193b4be Poul Sander 2025-02-03 21:28 465
                            - access those via GetPlatformIO() instead of GetIO().
1193b4be Poul Sander 2025-02-03 21:28 466
                         some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol.
1193b4be Poul Sander 2025-02-03 21:28 467
                       - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
1193b4be Poul Sander 2025-02-03 21:28 468
                            - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)
1193b4be Poul Sander 2025-02-03 21:28 469
                            - new ImageButton() since 1.89 requires an explicit 'const char* str_id'
1193b4be Poul Sander 2025-02-03 21:28 470
                            - old ImageButton() before 1.89 had frame_padding' override argument.
1193b4be Poul Sander 2025-02-03 21:28 471
                            - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
1193b4be Poul Sander 2025-02-03 21:28 472
 - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
1193b4be Poul Sander 2025-02-03 21:28 473
                         you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
1193b4be Poul Sander 2025-02-03 21:28 474
                            - instead of:  GetWindowContentRegionMax().x - GetCursorPos().x
1193b4be Poul Sander 2025-02-03 21:28 475
                            - you can use: GetContentRegionAvail().x
1193b4be Poul Sander 2025-02-03 21:28 476
                            - instead of:  GetWindowContentRegionMax().x + GetWindowPos().x
1193b4be Poul Sander 2025-02-03 21:28 477
                            - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
1193b4be Poul Sander 2025-02-03 21:28 478
                            - instead of:  GetContentRegionMax()
1193b4be Poul Sander 2025-02-03 21:28 479
                            - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
1193b4be Poul Sander 2025-02-03 21:28 480
                            - instead of:  GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
1193b4be Poul Sander 2025-02-03 21:28 481
                            - you can use: GetContentRegionAvail() // when called from left edge of window
1193b4be Poul Sander 2025-02-03 21:28 482
 - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
1193b4be Poul Sander 2025-02-03 21:28 483
                         (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
1193b4be Poul Sander 2025-02-03 21:28 484
 - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
1193b4be Poul Sander 2025-02-03 21:28 485
 - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
1193b4be Poul Sander 2025-02-03 21:28 486
                       - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
1193b4be Poul Sander 2025-02-03 21:28 487
                            - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
1193b4be Poul Sander 2025-02-03 21:28 488
 - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
1193b4be Poul Sander 2025-02-03 21:28 489
                            - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1193b4be Poul Sander 2025-02-03 21:28 490
                            - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1193b4be Poul Sander 2025-02-03 21:28 491
 - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.
1193b4be Poul Sander 2025-02-03 21:28 492
                            - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
1193b4be Poul Sander 2025-02-03 21:28 493
                            - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
1193b4be Poul Sander 2025-02-03 21:28 494
 - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
1193b4be Poul Sander 2025-02-03 21:28 495
 - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
1193b4be Poul Sander 2025-02-03 21:28 496
 - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
1193b4be Poul Sander 2025-02-03 21:28 497
 - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
1193b4be Poul Sander 2025-02-03 21:28 498
 - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.
1193b4be Poul Sander 2025-02-03 21:28 499
 - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
1193b4be Poul Sander 2025-02-03 21:28 500
 - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
1193b4be Poul Sander 2025-02-03 21:28 501
                            - old: CaptureKeyboardFromApp(bool)
1193b4be Poul Sander 2025-02-03 21:28 502
                            - new: SetNextFrameWantCaptureKeyboard(bool)
1193b4be Poul Sander 2025-02-03 21:28 503
                            - old: CaptureMouseFromApp(bool)
1193b4be Poul Sander 2025-02-03 21:28 504
                            - new: SetNextFrameWantCaptureMouse(bool)
1193b4be Poul Sander 2025-02-03 21:28 505
 - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).
1193b4be Poul Sander 2025-02-03 21:28 506
                       - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
1193b4be Poul Sander 2025-02-03 21:28 507
                       - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
1193b4be Poul Sander 2025-02-03 21:28 508
                            - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
1193b4be Poul Sander 2025-02-03 21:28 509
                            - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
1193b4be Poul Sander 2025-02-03 21:28 510
                       - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
1193b4be Poul Sander 2025-02-03 21:28 511
                            - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
1193b4be Poul Sander 2025-02-03 21:28 512
                            - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
1193b4be Poul Sander 2025-02-03 21:28 513
                            - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
1193b4be Poul Sander 2025-02-03 21:28 514
                            - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
1193b4be Poul Sander 2025-02-03 21:28 515
                         for various reasons those changes makes sense. They are being made because making some of those API public.
1193b4be Poul Sander 2025-02-03 21:28 516
                         only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.
1193b4be Poul Sander 2025-02-03 21:28 517
 - 2024/05/21 (1.90.7) - docking: changed signature of DockSpaceOverViewport() to add explicit dockspace id if desired. pass 0 to use old behavior. (#7611)
1193b4be Poul Sander 2025-02-03 21:28 518
                           - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
1193b4be Poul Sander 2025-02-03 21:28 519
                           - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
1193b4be Poul Sander 2025-02-03 21:28 520
 - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.
1193b4be Poul Sander 2025-02-03 21:28 521
                           - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
1193b4be Poul Sander 2025-02-03 21:28 522
                           - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)
1193b4be Poul Sander 2025-02-03 21:28 523
 - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
1193b4be Poul Sander 2025-02-03 21:28 524
 - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
1193b4be Poul Sander 2025-02-03 21:28 525
                           - old: TreeNode("##Hidden"); SameLine(); Text("Hello");     // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).
1193b4be Poul Sander 2025-02-03 21:28 526
                           - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
1193b4be Poul Sander 2025-02-03 21:28 527
                         with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
1193b4be Poul Sander 2025-02-03 21:28 528
                         You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.
1193b4be Poul Sander 2025-02-03 21:28 529
                         (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
1193b4be Poul Sander 2025-02-03 21:28 530
 - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)
1193b4be Poul Sander 2025-02-03 21:28 531
 - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)
1193b4be Poul Sander 2025-02-03 21:28 532
                           - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
1193b4be Poul Sander 2025-02-03 21:28 533
 - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
1193b4be Poul Sander 2025-02-03 21:28 534
 - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
1193b4be Poul Sander 2025-02-03 21:28 535
 - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
1193b4be Poul Sander 2025-02-03 21:28 536
 - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.
1193b4be Poul Sander 2025-02-03 21:28 537
 - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.
1193b4be Poul Sander 2025-02-03 21:28 538
 - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 539
 - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 540
                         those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.
1193b4be Poul Sander 2025-02-03 21:28 541
 - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.
1193b4be Poul Sander 2025-02-03 21:28 542
                           - old: BeginChild("Name", size, true)
1193b4be Poul Sander 2025-02-03 21:28 543
                           - new: BeginChild("Name", size, ImGuiChildFlags_Border)
1193b4be Poul Sander 2025-02-03 21:28 544
                           - old: BeginChild("Name", size, false)
1193b4be Poul Sander 2025-02-03 21:28 545
                           - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
1193b4be Poul Sander 2025-02-03 21:28 546
                         **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
1193b4be Poul Sander 2025-02-03 21:28 547
 - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.
1193b4be Poul Sander 2025-02-03 21:28 548
                           - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
1193b4be Poul Sander 2025-02-03 21:28 549
                           - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
1193b4be Poul Sander 2025-02-03 21:28 550
 - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).
1193b4be Poul Sander 2025-02-03 21:28 551
 - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631)
1193b4be Poul Sander 2025-02-03 21:28 552
 - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 553
                           - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
1193b4be Poul Sander 2025-02-03 21:28 554
                           - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
1193b4be Poul Sander 2025-02-03 21:28 555
                           - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
1193b4be Poul Sander 2025-02-03 21:28 556
                           - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
1193b4be Poul Sander 2025-02-03 21:28 557
 - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
1193b4be Poul Sander 2025-02-03 21:28 558
                           - GetWindowContentRegionWidth()  -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
1193b4be Poul Sander 2025-02-03 21:28 559
                           - ImDrawCornerFlags_XXX          -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
1193b4be Poul Sander 2025-02-03 21:28 560
                       - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details
1193b4be Poul Sander 2025-02-03 21:28 561
 - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
1193b4be Poul Sander 2025-02-03 21:28 562
 - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)
1193b4be Poul Sander 2025-02-03 21:28 563
 - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).
1193b4be Poul Sander 2025-02-03 21:28 564
 - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 565
 - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.
1193b4be Poul Sander 2025-02-03 21:28 566
 - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)
1193b4be Poul Sander 2025-02-03 21:28 567
 - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
1193b4be Poul Sander 2025-02-03 21:28 568
 - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
1193b4be Poul Sander 2025-02-03 21:28 569
 - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
1193b4be Poul Sander 2025-02-03 21:28 570
                           - ListBoxHeader()  -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
1193b4be Poul Sander 2025-02-03 21:28 571
                           - ListBoxFooter()  -> use EndListBox()
1193b4be Poul Sander 2025-02-03 21:28 572
 - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().
1193b4be Poul Sander 2025-02-03 21:28 573
 - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
1193b4be Poul Sander 2025-02-03 21:28 574
 - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
1193b4be Poul Sander 2025-02-03 21:28 575
                           - ImGuiSliderFlags_ClampOnInput        -> use ImGuiSliderFlags_AlwaysClamp
1193b4be Poul Sander 2025-02-03 21:28 576
                           - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
1193b4be Poul Sander 2025-02-03 21:28 577
                           - ImDrawList::AddBezierCurve()         -> use ImDrawList::AddBezierCubic()
1193b4be Poul Sander 2025-02-03 21:28 578
                           - ImDrawList::PathBezierCurveTo()      -> use ImDrawList::PathBezierCubicCurveTo()
1193b4be Poul Sander 2025-02-03 21:28 579
 - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 580
 - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.
1193b4be Poul Sander 2025-02-03 21:28 581
 - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
1193b4be Poul Sander 2025-02-03 21:28 582
                         Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
1193b4be Poul Sander 2025-02-03 21:28 583
                         it has been frequently requested by people to use our own. We had an opt-in define which was
1193b4be Poul Sander 2025-02-03 21:28 584
                         previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
1193b4be Poul Sander 2025-02-03 21:28 585
                           - OK:     #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
1193b4be Poul Sander 2025-02-03 21:28 586
                           - Error:  #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
1193b4be Poul Sander 2025-02-03 21:28 587
 - 2023/02/07 (1.89.3) - backends: renamed "imgui_impl_sdl.cpp" to "imgui_impl_sdl2.cpp" and "imgui_impl_sdl.h" to "imgui_impl_sdl2.h". (#6146) This is in prevision for the future release of SDL3.
1193b4be Poul Sander 2025-02-03 21:28 588
 - 2022/10/26 (1.89)   - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
1193b4be Poul Sander 2025-02-03 21:28 589
 - 2022/10/12 (1.89)   - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.
1193b4be Poul Sander 2025-02-03 21:28 590
 - 2022/09/26 (1.89)   - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 591
                           - ImGuiKey_ModCtrl  and ImGuiModFlags_Ctrl  -> ImGuiMod_Ctrl
1193b4be Poul Sander 2025-02-03 21:28 592
                           - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
1193b4be Poul Sander 2025-02-03 21:28 593
                           - ImGuiKey_ModAlt   and ImGuiModFlags_Alt   -> ImGuiMod_Alt
1193b4be Poul Sander 2025-02-03 21:28 594
                           - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
1193b4be Poul Sander 2025-02-03 21:28 595
                         the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
1193b4be Poul Sander 2025-02-03 21:28 596
                         the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
1193b4be Poul Sander 2025-02-03 21:28 597
                         exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.
1193b4be Poul Sander 2025-02-03 21:28 598
 - 2022/09/20 (1.89)   - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
1193b4be Poul Sander 2025-02-03 21:28 599
                         this will require uses of legacy backend-dependent indices to be casted, e.g.
1193b4be Poul Sander 2025-02-03 21:28 600
                            - with imgui_impl_glfw:  IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
1193b4be Poul Sander 2025-02-03 21:28 601
                            - with imgui_impl_win32: IsKeyPressed('A')        -> IsKeyPressed((ImGuiKey)'A')
1193b4be Poul Sander 2025-02-03 21:28 602
                            - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
1193b4be Poul Sander 2025-02-03 21:28 603
 - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);
1193b4be Poul Sander 2025-02-03 21:28 604
 - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
1193b4be Poul Sander 2025-02-03 21:28 605
                         - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
1193b4be Poul Sander 2025-02-03 21:28 606
                         - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
1193b4be Poul Sander 2025-02-03 21:28 607
                         - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
1193b4be Poul Sander 2025-02-03 21:28 608
 - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
1193b4be Poul Sander 2025-02-03 21:28 609
                       this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
1193b4be Poul Sander 2025-02-03 21:28 610
                         - previously this would make the window content size ~200x200:
1193b4be Poul Sander 2025-02-03 21:28 611
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
1193b4be Poul Sander 2025-02-03 21:28 612
                         - instead, please submit an item:
1193b4be Poul Sander 2025-02-03 21:28 613
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
1193b4be Poul Sander 2025-02-03 21:28 614
                         - alternative:
1193b4be Poul Sander 2025-02-03 21:28 615
                              Begin(...) + Dummy(ImVec2(200,200)) + End();
1193b4be Poul Sander 2025-02-03 21:28 616
                         - content size is now only extended when submitting an item!
1193b4be Poul Sander 2025-02-03 21:28 617
                         - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
1193b4be Poul Sander 2025-02-03 21:28 618
                         - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
1193b4be Poul Sander 2025-02-03 21:28 619
 - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 620
                        - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
1193b4be Poul Sander 2025-02-03 21:28 621
                        - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
1193b4be Poul Sander 2025-02-03 21:28 622
                          - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.
1193b4be Poul Sander 2025-02-03 21:28 623
                          - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
1193b4be Poul Sander 2025-02-03 21:28 624
                        - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
1193b4be Poul Sander 2025-02-03 21:28 625
                          - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
1193b4be Poul Sander 2025-02-03 21:28 626
                          - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
1193b4be Poul Sander 2025-02-03 21:28 627
 - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
1193b4be Poul Sander 2025-02-03 21:28 628
                        - Official backends from 1.87+                  -> no issue.
1193b4be Poul Sander 2025-02-03 21:28 629
                        - Official backends from 1.60 to 1.86           -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
1193b4be Poul Sander 2025-02-03 21:28 630
                        - Custom backends not writing to io.NavInputs[] -> no issue.
1193b4be Poul Sander 2025-02-03 21:28 631
                        - Custom backends writing to io.NavInputs[]     -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
1193b4be Poul Sander 2025-02-03 21:28 632
                        - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
1193b4be Poul Sander 2025-02-03 21:28 633
 - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 634
 - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.
1193b4be Poul Sander 2025-02-03 21:28 635
 - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
1193b4be Poul Sander 2025-02-03 21:28 636
 - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
1193b4be Poul Sander 2025-02-03 21:28 637
                        - Backend writing to io.NavInputs[]            -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
1193b4be Poul Sander 2025-02-03 21:28 638
 - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).
1193b4be Poul Sander 2025-02-03 21:28 639
 - 2022/01/17 (1.87) - inputs: reworked mouse IO.
1193b4be Poul Sander 2025-02-03 21:28 640
                        - Backend writing to io.MousePos               -> backend should call io.AddMousePosEvent()
1193b4be Poul Sander 2025-02-03 21:28 641
                        - Backend writing to io.MouseDown[]            -> backend should call io.AddMouseButtonEvent()
1193b4be Poul Sander 2025-02-03 21:28 642
                        - Backend writing to io.MouseWheel             -> backend should call io.AddMouseWheelEvent()
1193b4be Poul Sander 2025-02-03 21:28 643
                        - Backend writing to io.MouseHoveredViewport   -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
1193b4be Poul Sander 2025-02-03 21:28 644
                       note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
1193b4be Poul Sander 2025-02-03 21:28 645
                       read https://github.com/ocornut/imgui/issues/4921 for details.
1193b4be Poul Sander 2025-02-03 21:28 646
 - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
1193b4be Poul Sander 2025-02-03 21:28 647
                        - IsKeyPressed(MY_NATIVE_KEY_XXX)              -> use IsKeyPressed(ImGuiKey_XXX)
1193b4be Poul Sander 2025-02-03 21:28 648
                        - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))      -> use IsKeyPressed(ImGuiKey_XXX)
1193b4be Poul Sander 2025-02-03 21:28 649
                        - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes).
1193b4be Poul Sander 2025-02-03 21:28 650
                        - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*
1193b4be Poul Sander 2025-02-03 21:28 651
                     - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert.
1193b4be Poul Sander 2025-02-03 21:28 652
                     - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.
1193b4be Poul Sander 2025-02-03 21:28 653
 - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
1193b4be Poul Sander 2025-02-03 21:28 654
 - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
1193b4be Poul Sander 2025-02-03 21:28 655
 - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
1193b4be Poul Sander 2025-02-03 21:28 656
                        - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()
1193b4be Poul Sander 2025-02-03 21:28 657
                        - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
1193b4be Poul Sander 2025-02-03 21:28 658
                        - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
1193b4be Poul Sander 2025-02-03 21:28 659
                        - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect
1193b4be Poul Sander 2025-02-03 21:28 660
                        - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
1193b4be Poul Sander 2025-02-03 21:28 661
 - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings
1193b4be Poul Sander 2025-02-03 21:28 662
 - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
1193b4be Poul Sander 2025-02-03 21:28 663
 - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
1193b4be Poul Sander 2025-02-03 21:28 664
 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
1193b4be Poul Sander 2025-02-03 21:28 665
                        - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
1193b4be Poul Sander 2025-02-03 21:28 666
                        - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder
1193b4be Poul Sander 2025-02-03 21:28 667
 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
1193b4be Poul Sander 2025-02-03 21:28 668
                        - if you are using official backends from the source tree: you have nothing to do.
1193b4be Poul Sander 2025-02-03 21:28 669
                        - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
1193b4be Poul Sander 2025-02-03 21:28 670
 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
1193b4be Poul Sander 2025-02-03 21:28 671
                        - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft
1193b4be Poul Sander 2025-02-03 21:28 672
                        - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
1193b4be Poul Sander 2025-02-03 21:28 673
                        - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.
1193b4be Poul Sander 2025-02-03 21:28 674
                       flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
1193b4be Poul Sander 2025-02-03 21:28 675
                       breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
1193b4be Poul Sander 2025-02-03 21:28 676
                        - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)
1193b4be Poul Sander 2025-02-03 21:28 677
                        - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)
1193b4be Poul Sander 2025-02-03 21:28 678
                        - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)
1193b4be Poul Sander 2025-02-03 21:28 679
                        - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
1193b4be Poul Sander 2025-02-03 21:28 680
                       this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
1193b4be Poul Sander 2025-02-03 21:28 681
                       the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
1193b4be Poul Sander 2025-02-03 21:28 682
                       legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
1193b4be Poul Sander 2025-02-03 21:28 683
 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
1193b4be Poul Sander 2025-02-03 21:28 684
                        - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()
1193b4be Poul Sander 2025-02-03 21:28 685
 - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
1193b4be Poul Sander 2025-02-03 21:28 686
 - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
1193b4be Poul Sander 2025-02-03 21:28 687
 - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
1193b4be Poul Sander 2025-02-03 21:28 688
 - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
1193b4be Poul Sander 2025-02-03 21:28 689
 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 690
                     - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 691
                     - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 692
 - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
1193b4be Poul Sander 2025-02-03 21:28 693
                     - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
1193b4be Poul Sander 2025-02-03 21:28 694
                     - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
1193b4be Poul Sander 2025-02-03 21:28 695
 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
1193b4be Poul Sander 2025-02-03 21:28 696
                        - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
1193b4be Poul Sander 2025-02-03 21:28 697
                        - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
1193b4be Poul Sander 2025-02-03 21:28 698
                        - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
1193b4be Poul Sander 2025-02-03 21:28 699
                        - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
1193b4be Poul Sander 2025-02-03 21:28 700
 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 701
 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
1193b4be Poul Sander 2025-02-03 21:28 702
 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
1193b4be Poul Sander 2025-02-03 21:28 703
 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
1193b4be Poul Sander 2025-02-03 21:28 704
 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
1193b4be Poul Sander 2025-02-03 21:28 705
 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
1193b4be Poul Sander 2025-02-03 21:28 706
                        - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
1193b4be Poul Sander 2025-02-03 21:28 707
                        - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
1193b4be Poul Sander 2025-02-03 21:28 708
                        - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
1193b4be Poul Sander 2025-02-03 21:28 709
                        - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
1193b4be Poul Sander 2025-02-03 21:28 710
                        - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
1193b4be Poul Sander 2025-02-03 21:28 711
                      - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
1193b4be Poul Sander 2025-02-03 21:28 712
                        - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
1193b4be Poul Sander 2025-02-03 21:28 713
                        - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
1193b4be Poul Sander 2025-02-03 21:28 714
 - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
1193b4be Poul Sander 2025-02-03 21:28 715
 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
1193b4be Poul Sander 2025-02-03 21:28 716
 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
1193b4be Poul Sander 2025-02-03 21:28 717
 - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
1193b4be Poul Sander 2025-02-03 21:28 718
 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
1193b4be Poul Sander 2025-02-03 21:28 719
 - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
1193b4be Poul Sander 2025-02-03 21:28 720
 - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
1193b4be Poul Sander 2025-02-03 21:28 721
                       replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
1193b4be Poul Sander 2025-02-03 21:28 722
                       worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
1193b4be Poul Sander 2025-02-03 21:28 723
                       - if you omitted the 'power' parameter (likely!), you are not affected.
1193b4be Poul Sander 2025-02-03 21:28 724
                       - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
1193b4be Poul Sander 2025-02-03 21:28 725
                       - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
1193b4be Poul Sander 2025-02-03 21:28 726
                       see https://github.com/ocornut/imgui/issues/3361 for all details.
1193b4be Poul Sander 2025-02-03 21:28 727
                       kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
1193b4be Poul Sander 2025-02-03 21:28 728
                       for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
1193b4be Poul Sander 2025-02-03 21:28 729
                     - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
1193b4be Poul Sander 2025-02-03 21:28 730
 - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
1193b4be Poul Sander 2025-02-03 21:28 731
 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
1193b4be Poul Sander 2025-02-03 21:28 732
 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
1193b4be Poul Sander 2025-02-03 21:28 733
 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
1193b4be Poul Sander 2025-02-03 21:28 734
 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
1193b4be Poul Sander 2025-02-03 21:28 735
 - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
1193b4be Poul Sander 2025-02-03 21:28 736
 - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
1193b4be Poul Sander 2025-02-03 21:28 737
 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
1193b4be Poul Sander 2025-02-03 21:28 738
                       - ShowTestWindow()                    -> use ShowDemoWindow()
1193b4be Poul Sander 2025-02-03 21:28 739
                       - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
1193b4be Poul Sander 2025-02-03 21:28 740
                       - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 741
                       - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 742
                       - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 743
                       - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
1193b4be Poul Sander 2025-02-03 21:28 744
                       - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
1193b4be Poul Sander 2025-02-03 21:28 745
                       - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
1193b4be Poul Sander 2025-02-03 21:28 746
                       - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
1193b4be Poul Sander 2025-02-03 21:28 747
 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
1193b4be Poul Sander 2025-02-03 21:28 748
 - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
1193b4be Poul Sander 2025-02-03 21:28 749
 - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
1193b4be Poul Sander 2025-02-03 21:28 750
 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
1193b4be Poul Sander 2025-02-03 21:28 751
 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
1193b4be Poul Sander 2025-02-03 21:28 752
 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
1193b4be Poul Sander 2025-02-03 21:28 753
                       - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
1193b4be Poul Sander 2025-02-03 21:28 754
                       - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 755
                       - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
1193b4be Poul Sander 2025-02-03 21:28 756
                       - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
1193b4be Poul Sander 2025-02-03 21:28 757
                       - ImFont::Glyph                       -> use ImFontGlyph
1193b4be Poul Sander 2025-02-03 21:28 758
 - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
1193b4be Poul Sander 2025-02-03 21:28 759
                       if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
1193b4be Poul Sander 2025-02-03 21:28 760
                       The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
1193b4be Poul Sander 2025-02-03 21:28 761
                       If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
1193b4be Poul Sander 2025-02-03 21:28 762
 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 763
 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 764
 - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
1193b4be Poul Sander 2025-02-03 21:28 765
 - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
1193b4be Poul Sander 2025-02-03 21:28 766
                       overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
1193b4be Poul Sander 2025-02-03 21:28 767
                       This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
1193b4be Poul Sander 2025-02-03 21:28 768
                       Please reach out if you are affected.
1193b4be Poul Sander 2025-02-03 21:28 769
 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 770
 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
1193b4be Poul Sander 2025-02-03 21:28 771
 - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
1193b4be Poul Sander 2025-02-03 21:28 772
 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 773
 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 774
 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 775
 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
1193b4be Poul Sander 2025-02-03 21:28 776
 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
1193b4be Poul Sander 2025-02-03 21:28 777
 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
1193b4be Poul Sander 2025-02-03 21:28 778
 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 779
 - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects.
1193b4be Poul Sander 2025-02-03 21:28 780
 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
1193b4be Poul Sander 2025-02-03 21:28 781
 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
1193b4be Poul Sander 2025-02-03 21:28 782
 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 783
 - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.
1193b4be Poul Sander 2025-02-03 21:28 784
                       If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
1193b4be Poul Sander 2025-02-03 21:28 785
 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
1193b4be Poul Sander 2025-02-03 21:28 786
 - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.
1193b4be Poul Sander 2025-02-03 21:28 787
                       NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
1193b4be Poul Sander 2025-02-03 21:28 788
                       Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
1193b4be Poul Sander 2025-02-03 21:28 789
 - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).
1193b4be Poul Sander 2025-02-03 21:28 790
 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 791
 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
1193b4be Poul Sander 2025-02-03 21:28 792
 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.
1193b4be Poul Sander 2025-02-03 21:28 793
 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
1193b4be Poul Sander 2025-02-03 21:28 794
 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
1193b4be Poul Sander 2025-02-03 21:28 795
 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 796
 - 2018/06/08 (1.62) - examples: the imgui_impl_XXX files have been split to separate platform (Win32, GLFW, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan,  etc.).
1193b4be Poul Sander 2025-02-03 21:28 797
                       old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
1193b4be Poul Sander 2025-02-03 21:28 798
                       when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
1193b4be Poul Sander 2025-02-03 21:28 799
                       in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
1193b4be Poul Sander 2025-02-03 21:28 800
 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
1193b4be Poul Sander 2025-02-03 21:28 801
 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
1193b4be Poul Sander 2025-02-03 21:28 802
 - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
1193b4be Poul Sander 2025-02-03 21:28 803
                       If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
1193b4be Poul Sander 2025-02-03 21:28 804
                       To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
1193b4be Poul Sander 2025-02-03 21:28 805
                       If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
1193b4be Poul Sander 2025-02-03 21:28 806
 - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
1193b4be Poul Sander 2025-02-03 21:28 807
                       consistent with other functions. Kept redirection functions (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 808
 - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
1193b4be Poul Sander 2025-02-03 21:28 809
 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).
1193b4be Poul Sander 2025-02-03 21:28 810
 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
1193b4be Poul Sander 2025-02-03 21:28 811
 - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
1193b4be Poul Sander 2025-02-03 21:28 812
 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
1193b4be Poul Sander 2025-02-03 21:28 813
 - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
1193b4be Poul Sander 2025-02-03 21:28 814
 - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
1193b4be Poul Sander 2025-02-03 21:28 815
 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
1193b4be Poul Sander 2025-02-03 21:28 816
                       - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
1193b4be Poul Sander 2025-02-03 21:28 817
                       - removed Shutdown() function, as DestroyContext() serve this purpose.
1193b4be Poul Sander 2025-02-03 21:28 818
                       - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
1193b4be Poul Sander 2025-02-03 21:28 819
                       - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
1193b4be Poul Sander 2025-02-03 21:28 820
                       - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
1193b4be Poul Sander 2025-02-03 21:28 821
 - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
1193b4be Poul Sander 2025-02-03 21:28 822
 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 823
 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 824
 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
1193b4be Poul Sander 2025-02-03 21:28 825
 - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
1193b4be Poul Sander 2025-02-03 21:28 826
 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 827
 - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
1193b4be Poul Sander 2025-02-03 21:28 828
 - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
1193b4be Poul Sander 2025-02-03 21:28 829
 - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
1193b4be Poul Sander 2025-02-03 21:28 830
 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 831
 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 832
                     - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 833
 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 834
 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 835
 - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
1193b4be Poul Sander 2025-02-03 21:28 836
 - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
1193b4be Poul Sander 2025-02-03 21:28 837
                       Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
1193b4be Poul Sander 2025-02-03 21:28 838
 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
1193b4be Poul Sander 2025-02-03 21:28 839
 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
1193b4be Poul Sander 2025-02-03 21:28 840
 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
1193b4be Poul Sander 2025-02-03 21:28 841
 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
1193b4be Poul Sander 2025-02-03 21:28 842
 - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
1193b4be Poul Sander 2025-02-03 21:28 843
 - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
1193b4be Poul Sander 2025-02-03 21:28 844
 - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
1193b4be Poul Sander 2025-02-03 21:28 845
                       removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
1193b4be Poul Sander 2025-02-03 21:28 846
                         IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
1193b4be Poul Sander 2025-02-03 21:28 847
                         IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
1193b4be Poul Sander 2025-02-03 21:28 848
                         IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
1193b4be Poul Sander 2025-02-03 21:28 849
 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
1193b4be Poul Sander 2025-02-03 21:28 850
 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 851
 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 852
 - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 853
 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
1193b4be Poul Sander 2025-02-03 21:28 854
 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
1193b4be Poul Sander 2025-02-03 21:28 855
                     - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 856
                     - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 857
 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
1193b4be Poul Sander 2025-02-03 21:28 858
 - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
1193b4be Poul Sander 2025-02-03 21:28 859
 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
1193b4be Poul Sander 2025-02-03 21:28 860
 - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
1193b4be Poul Sander 2025-02-03 21:28 861
 - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 862
 - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 863
 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
1193b4be Poul Sander 2025-02-03 21:28 864
 - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
1193b4be Poul Sander 2025-02-03 21:28 865
                     - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
1193b4be Poul Sander 2025-02-03 21:28 866
                     - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))'
1193b4be Poul Sander 2025-02-03 21:28 867
 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
1193b4be Poul Sander 2025-02-03 21:28 868
 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
1193b4be Poul Sander 2025-02-03 21:28 869
 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
1193b4be Poul Sander 2025-02-03 21:28 870
 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild().
1193b4be Poul Sander 2025-02-03 21:28 871
 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
1193b4be Poul Sander 2025-02-03 21:28 872
 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
1193b4be Poul Sander 2025-02-03 21:28 873
 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal.
1193b4be Poul Sander 2025-02-03 21:28 874
 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
1193b4be Poul Sander 2025-02-03 21:28 875
                       If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
1193b4be Poul Sander 2025-02-03 21:28 876
                       This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color:
1193b4be Poul Sander 2025-02-03 21:28 877
                       ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }
1193b4be Poul Sander 2025-02-03 21:28 878
                       If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
1193b4be Poul Sander 2025-02-03 21:28 879
 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
1193b4be Poul Sander 2025-02-03 21:28 880
 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
1193b4be Poul Sander 2025-02-03 21:28 881
 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
1193b4be Poul Sander 2025-02-03 21:28 882
 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
1193b4be Poul Sander 2025-02-03 21:28 883
 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337).
1193b4be Poul Sander 2025-02-03 21:28 884
 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
1193b4be Poul Sander 2025-02-03 21:28 885
 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 886
 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
1193b4be Poul Sander 2025-02-03 21:28 887
 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
1193b4be Poul Sander 2025-02-03 21:28 888
 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
1193b4be Poul Sander 2025-02-03 21:28 889
 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
1193b4be Poul Sander 2025-02-03 21:28 890
 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
1193b4be Poul Sander 2025-02-03 21:28 891
                       GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
1193b4be Poul Sander 2025-02-03 21:28 892
                       GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
1193b4be Poul Sander 2025-02-03 21:28 893
 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
1193b4be Poul Sander 2025-02-03 21:28 894
 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
1193b4be Poul Sander 2025-02-03 21:28 895
 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
1193b4be Poul Sander 2025-02-03 21:28 896
 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
1193b4be Poul Sander 2025-02-03 21:28 897
                       you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
1193b4be Poul Sander 2025-02-03 21:28 898
 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
1193b4be Poul Sander 2025-02-03 21:28 899
                       this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
1193b4be Poul Sander 2025-02-03 21:28 900
                     - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
1193b4be Poul Sander 2025-02-03 21:28 901
                     - the signature of the io.RenderDrawListsFn handler has changed!
1193b4be Poul Sander 2025-02-03 21:28 902
                       old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
1193b4be Poul Sander 2025-02-03 21:28 903
                       new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
1193b4be Poul Sander 2025-02-03 21:28 904
                         parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
1193b4be Poul Sander 2025-02-03 21:28 905
                         ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
1193b4be Poul Sander 2025-02-03 21:28 906
                         ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
1193b4be Poul Sander 2025-02-03 21:28 907
                     - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
1193b4be Poul Sander 2025-02-03 21:28 908
                     - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
1193b4be Poul Sander 2025-02-03 21:28 909
                     - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
1193b4be Poul Sander 2025-02-03 21:28 910
 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
1193b4be Poul Sander 2025-02-03 21:28 911
 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 912
 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
1193b4be Poul Sander 2025-02-03 21:28 913
 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
1193b4be Poul Sander 2025-02-03 21:28 914
 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
1193b4be Poul Sander 2025-02-03 21:28 915
 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 916
 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
1193b4be Poul Sander 2025-02-03 21:28 917
 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
1193b4be Poul Sander 2025-02-03 21:28 918
 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
1193b4be Poul Sander 2025-02-03 21:28 919
 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
1193b4be Poul Sander 2025-02-03 21:28 920
 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
1193b4be Poul Sander 2025-02-03 21:28 921
 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
1193b4be Poul Sander 2025-02-03 21:28 922
 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
1193b4be Poul Sander 2025-02-03 21:28 923
 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
1193b4be Poul Sander 2025-02-03 21:28 924
 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
1193b4be Poul Sander 2025-02-03 21:28 925
 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
1193b4be Poul Sander 2025-02-03 21:28 926
 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
1193b4be Poul Sander 2025-02-03 21:28 927
 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
1193b4be Poul Sander 2025-02-03 21:28 928
 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
1193b4be Poul Sander 2025-02-03 21:28 929
 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
1193b4be Poul Sander 2025-02-03 21:28 930
 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
1193b4be Poul Sander 2025-02-03 21:28 931
 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
1193b4be Poul Sander 2025-02-03 21:28 932
 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 933
 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
1193b4be Poul Sander 2025-02-03 21:28 934
 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
1193b4be Poul Sander 2025-02-03 21:28 935
 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
1193b4be Poul Sander 2025-02-03 21:28 936
 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
1193b4be Poul Sander 2025-02-03 21:28 937
                       - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
1193b4be Poul Sander 2025-02-03 21:28 938
                       - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
1193b4be Poul Sander 2025-02-03 21:28 939
                       you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
1193b4be Poul Sander 2025-02-03 21:28 940
 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
1193b4be Poul Sander 2025-02-03 21:28 941
 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
1193b4be Poul Sander 2025-02-03 21:28 942
 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
1193b4be Poul Sander 2025-02-03 21:28 943
 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
1193b4be Poul Sander 2025-02-03 21:28 944
 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
1193b4be Poul Sander 2025-02-03 21:28 945
 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
1193b4be Poul Sander 2025-02-03 21:28 946
 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
1193b4be Poul Sander 2025-02-03 21:28 947
 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
1193b4be Poul Sander 2025-02-03 21:28 948
 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
1193b4be Poul Sander 2025-02-03 21:28 949
 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
1193b4be Poul Sander 2025-02-03 21:28 950
 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
1193b4be Poul Sander 2025-02-03 21:28 951
 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
1193b4be Poul Sander 2025-02-03 21:28 952
 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
1193b4be Poul Sander 2025-02-03 21:28 953
 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
1193b4be Poul Sander 2025-02-03 21:28 954
1193b4be Poul Sander 2025-02-03 21:28 955
1193b4be Poul Sander 2025-02-03 21:28 956
 FREQUENTLY ASKED QUESTIONS (FAQ)
1193b4be Poul Sander 2025-02-03 21:28 957
 ================================
1193b4be Poul Sander 2025-02-03 21:28 958
1193b4be Poul Sander 2025-02-03 21:28 959
 Read all answers online:
1193b4be Poul Sander 2025-02-03 21:28 960
   https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
1193b4be Poul Sander 2025-02-03 21:28 961
 Read all answers locally (with a text editor or ideally a Markdown viewer):
1193b4be Poul Sander 2025-02-03 21:28 962
   docs/FAQ.md
1193b4be Poul Sander 2025-02-03 21:28 963
 Some answers are copied down here to facilitate searching in code.
1193b4be Poul Sander 2025-02-03 21:28 964
1193b4be Poul Sander 2025-02-03 21:28 965
 Q&A: Basics
1193b4be Poul Sander 2025-02-03 21:28 966
 ===========
1193b4be Poul Sander 2025-02-03 21:28 967
1193b4be Poul Sander 2025-02-03 21:28 968
 Q: Where is the documentation?
1193b4be Poul Sander 2025-02-03 21:28 969
 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
1193b4be Poul Sander 2025-02-03 21:28 970
    - Run the examples/ applications and explore them.
1193b4be Poul Sander 2025-02-03 21:28 971
    - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
1193b4be Poul Sander 2025-02-03 21:28 972
    - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
1193b4be Poul Sander 2025-02-03 21:28 973
    - The demo covers most features of Dear ImGui, so you can read the code and see its output.
1193b4be Poul Sander 2025-02-03 21:28 974
    - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
1193b4be Poul Sander 2025-02-03 21:28 975
    - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
1193b4be Poul Sander 2025-02-03 21:28 976
      examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
1193b4be Poul Sander 2025-02-03 21:28 977
    - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
1193b4be Poul Sander 2025-02-03 21:28 978
    - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
1193b4be Poul Sander 2025-02-03 21:28 979
    - Your programming IDE is your friend, find the type or function declaration to find comments
1193b4be Poul Sander 2025-02-03 21:28 980
      associated with it.
1193b4be Poul Sander 2025-02-03 21:28 981
1193b4be Poul Sander 2025-02-03 21:28 982
 Q: What is this library called?
1193b4be Poul Sander 2025-02-03 21:28 983
 Q: Which version should I get?
1193b4be Poul Sander 2025-02-03 21:28 984
 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
1193b4be Poul Sander 2025-02-03 21:28 985
 >> See https://www.dearimgui.com/faq for details.
1193b4be Poul Sander 2025-02-03 21:28 986
1193b4be Poul Sander 2025-02-03 21:28 987
 Q&A: Integration
1193b4be Poul Sander 2025-02-03 21:28 988
 ================
1193b4be Poul Sander 2025-02-03 21:28 989
1193b4be Poul Sander 2025-02-03 21:28 990
 Q: How to get started?
1193b4be Poul Sander 2025-02-03 21:28 991
 A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
1193b4be Poul Sander 2025-02-03 21:28 992
1193b4be Poul Sander 2025-02-03 21:28 993
 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
1193b4be Poul Sander 2025-02-03 21:28 994
 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1193b4be Poul Sander 2025-02-03 21:28 995
 >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
1193b4be Poul Sander 2025-02-03 21:28 996
1193b4be Poul Sander 2025-02-03 21:28 997
 Q. How can I enable keyboard or gamepad controls?
1193b4be Poul Sander 2025-02-03 21:28 998
 Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
1193b4be Poul Sander 2025-02-03 21:28 999
 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1193b4be Poul Sander 2025-02-03 21:28 1000
 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1193b4be Poul Sander 2025-02-03 21:28 1001
 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1193b4be Poul Sander 2025-02-03 21:28 1002
 >> See https://www.dearimgui.com/faq
1193b4be Poul Sander 2025-02-03 21:28 1003
1193b4be Poul Sander 2025-02-03 21:28 1004
 Q&A: Usage
1193b4be Poul Sander 2025-02-03 21:28 1005
 ----------
1193b4be Poul Sander 2025-02-03 21:28 1006
1193b4be Poul Sander 2025-02-03 21:28 1007
 Q: About the ID Stack system..
1193b4be Poul Sander 2025-02-03 21:28 1008
   - Why is my widget not reacting when I click on it?
1193b4be Poul Sander 2025-02-03 21:28 1009
   - How can I have widgets with an empty label?
1193b4be Poul Sander 2025-02-03 21:28 1010
   - How can I have multiple widgets with the same label?
1193b4be Poul Sander 2025-02-03 21:28 1011
   - How can I have multiple windows with the same label?
1193b4be Poul Sander 2025-02-03 21:28 1012
 Q: How can I display an image? What is ImTextureID, how does it work?
1193b4be Poul Sander 2025-02-03 21:28 1013
 Q: How can I use my own math types instead of ImVec2?
1193b4be Poul Sander 2025-02-03 21:28 1014
 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1193b4be Poul Sander 2025-02-03 21:28 1015
 Q: How can I display custom shapes? (using low-level ImDrawList API)
1193b4be Poul Sander 2025-02-03 21:28 1016
 >> See https://www.dearimgui.com/faq
1193b4be Poul Sander 2025-02-03 21:28 1017
1193b4be Poul Sander 2025-02-03 21:28 1018
 Q&A: Fonts, Text
1193b4be Poul Sander 2025-02-03 21:28 1019
 ================
1193b4be Poul Sander 2025-02-03 21:28 1020
1193b4be Poul Sander 2025-02-03 21:28 1021
 Q: How should I handle DPI in my application?
1193b4be Poul Sander 2025-02-03 21:28 1022
 Q: How can I load a different font than the default?
1193b4be Poul Sander 2025-02-03 21:28 1023
 Q: How can I easily use icons in my application?
1193b4be Poul Sander 2025-02-03 21:28 1024
 Q: How can I load multiple fonts?
1193b4be Poul Sander 2025-02-03 21:28 1025
 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1193b4be Poul Sander 2025-02-03 21:28 1026
 >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1193b4be Poul Sander 2025-02-03 21:28 1027
1193b4be Poul Sander 2025-02-03 21:28 1028
 Q&A: Concerns
1193b4be Poul Sander 2025-02-03 21:28 1029
 =============
1193b4be Poul Sander 2025-02-03 21:28 1030
1193b4be Poul Sander 2025-02-03 21:28 1031
 Q: Who uses Dear ImGui?
1193b4be Poul Sander 2025-02-03 21:28 1032
 Q: Can you create elaborate/serious tools with Dear ImGui?
1193b4be Poul Sander 2025-02-03 21:28 1033
 Q: Can you reskin the look of Dear ImGui?
1193b4be Poul Sander 2025-02-03 21:28 1034
 Q: Why using C++ (as opposed to C)?
1193b4be Poul Sander 2025-02-03 21:28 1035
 >> See https://www.dearimgui.com/faq
1193b4be Poul Sander 2025-02-03 21:28 1036
1193b4be Poul Sander 2025-02-03 21:28 1037
 Q&A: Community
1193b4be Poul Sander 2025-02-03 21:28 1038
 ==============
1193b4be Poul Sander 2025-02-03 21:28 1039
1193b4be Poul Sander 2025-02-03 21:28 1040
 Q: How can I help?
1193b4be Poul Sander 2025-02-03 21:28 1041
 A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1193b4be Poul Sander 2025-02-03 21:28 1042
      We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1193b4be Poul Sander 2025-02-03 21:28 1043
      This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1193b4be Poul Sander 2025-02-03 21:28 1044
      >>> See https://github.com/ocornut/imgui/wiki/Funding
1193b4be Poul Sander 2025-02-03 21:28 1045
    - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1193b4be Poul Sander 2025-02-03 21:28 1046
    - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!
1193b4be Poul Sander 2025-02-03 21:28 1047
    - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1193b4be Poul Sander 2025-02-03 21:28 1048
      You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1193b4be Poul Sander 2025-02-03 21:28 1049
      But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1193b4be Poul Sander 2025-02-03 21:28 1050
    - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately).
1193b4be Poul Sander 2025-02-03 21:28 1051
1193b4be Poul Sander 2025-02-03 21:28 1052
*/
1193b4be Poul Sander 2025-02-03 21:28 1053
1193b4be Poul Sander 2025-02-03 21:28 1054
//-------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1055
// [SECTION] INCLUDES
1193b4be Poul Sander 2025-02-03 21:28 1056
//-------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1057
1193b4be Poul Sander 2025-02-03 21:28 1058
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1193b4be Poul Sander 2025-02-03 21:28 1059
#define _CRT_SECURE_NO_WARNINGS
1193b4be Poul Sander 2025-02-03 21:28 1060
#endif
1193b4be Poul Sander 2025-02-03 21:28 1061
1193b4be Poul Sander 2025-02-03 21:28 1062
#ifndef IMGUI_DEFINE_MATH_OPERATORS
1193b4be Poul Sander 2025-02-03 21:28 1063
#define IMGUI_DEFINE_MATH_OPERATORS
1193b4be Poul Sander 2025-02-03 21:28 1064
#endif
1193b4be Poul Sander 2025-02-03 21:28 1065
1193b4be Poul Sander 2025-02-03 21:28 1066
#include "imgui.h"
1193b4be Poul Sander 2025-02-03 21:28 1067
#ifndef IMGUI_DISABLE
1193b4be Poul Sander 2025-02-03 21:28 1068
#include "imgui_internal.h"
1193b4be Poul Sander 2025-02-03 21:28 1069
1193b4be Poul Sander 2025-02-03 21:28 1070
// System includes
1193b4be Poul Sander 2025-02-03 21:28 1071
#include <stdio.h>      // vsnprintf, sscanf, printf
1193b4be Poul Sander 2025-02-03 21:28 1072
#include <stdint.h>     // intptr_t
1193b4be Poul Sander 2025-02-03 21:28 1073
1193b4be Poul Sander 2025-02-03 21:28 1074
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1193b4be Poul Sander 2025-02-03 21:28 1075
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 1076
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1077
#endif
1193b4be Poul Sander 2025-02-03 21:28 1078
1193b4be Poul Sander 2025-02-03 21:28 1079
// [Windows] OS specific includes (optional)
1193b4be Poul Sander 2025-02-03 21:28 1080
#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 1081
#define IMGUI_DISABLE_WIN32_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1082
#endif
1193b4be Poul Sander 2025-02-03 21:28 1083
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 1084
#ifndef WIN32_LEAN_AND_MEAN
1193b4be Poul Sander 2025-02-03 21:28 1085
#define WIN32_LEAN_AND_MEAN
1193b4be Poul Sander 2025-02-03 21:28 1086
#endif
1193b4be Poul Sander 2025-02-03 21:28 1087
#ifndef NOMINMAX
1193b4be Poul Sander 2025-02-03 21:28 1088
#define NOMINMAX
1193b4be Poul Sander 2025-02-03 21:28 1089
#endif
1193b4be Poul Sander 2025-02-03 21:28 1090
#ifndef __MINGW32__
1193b4be Poul Sander 2025-02-03 21:28 1091
#include <Windows.h>        // _wfopen, OpenClipboard
1193b4be Poul Sander 2025-02-03 21:28 1092
#else
1193b4be Poul Sander 2025-02-03 21:28 1093
#include <windows.h>
1193b4be Poul Sander 2025-02-03 21:28 1094
#endif
1193b4be Poul Sander 2025-02-03 21:28 1095
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
1193b4be Poul Sander 2025-02-03 21:28 1096
// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1193b4be Poul Sander 2025-02-03 21:28 1097
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1098
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1099
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1100
#endif
1193b4be Poul Sander 2025-02-03 21:28 1101
#endif
1193b4be Poul Sander 2025-02-03 21:28 1102
1193b4be Poul Sander 2025-02-03 21:28 1103
// [Apple] OS specific includes
1193b4be Poul Sander 2025-02-03 21:28 1104
#if defined(__APPLE__)
1193b4be Poul Sander 2025-02-03 21:28 1105
#include <TargetConditionals.h>
1193b4be Poul Sander 2025-02-03 21:28 1106
#endif
1193b4be Poul Sander 2025-02-03 21:28 1107
1193b4be Poul Sander 2025-02-03 21:28 1108
// Visual Studio warnings
1193b4be Poul Sander 2025-02-03 21:28 1109
#ifdef _MSC_VER
1193b4be Poul Sander 2025-02-03 21:28 1110
#pragma warning (disable: 4127)             // condition expression is constant
1193b4be Poul Sander 2025-02-03 21:28 1111
#pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1193b4be Poul Sander 2025-02-03 21:28 1112
#if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
1193b4be Poul Sander 2025-02-03 21:28 1113
#pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
1193b4be Poul Sander 2025-02-03 21:28 1114
#endif
1193b4be Poul Sander 2025-02-03 21:28 1115
#pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
1193b4be Poul Sander 2025-02-03 21:28 1116
#pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1193b4be Poul Sander 2025-02-03 21:28 1117
#pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1193b4be Poul Sander 2025-02-03 21:28 1118
#endif
1193b4be Poul Sander 2025-02-03 21:28 1119
1193b4be Poul Sander 2025-02-03 21:28 1120
// Clang/GCC warnings with -Weverything
1193b4be Poul Sander 2025-02-03 21:28 1121
#if defined(__clang__)
1193b4be Poul Sander 2025-02-03 21:28 1122
#if __has_warning("-Wunknown-warning-option")
1193b4be Poul Sander 2025-02-03 21:28 1123
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
1193b4be Poul Sander 2025-02-03 21:28 1124
#endif
1193b4be Poul Sander 2025-02-03 21:28 1125
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
1193b4be Poul Sander 2025-02-03 21:28 1126
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
1193b4be Poul Sander 2025-02-03 21:28 1127
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
1193b4be Poul Sander 2025-02-03 21:28 1128
#pragma clang diagnostic ignored "-Wformat-nonliteral"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
1193b4be Poul Sander 2025-02-03 21:28 1129
#pragma clang diagnostic ignored "-Wexit-time-destructors"          // warning: declaration requires an exit-time destructor     // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
1193b4be Poul Sander 2025-02-03 21:28 1130
#pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
1193b4be Poul Sander 2025-02-03 21:28 1131
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
1193b4be Poul Sander 2025-02-03 21:28 1132
#pragma clang diagnostic ignored "-Wformat-pedantic"                // warning: format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
1193b4be Poul Sander 2025-02-03 21:28 1133
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
1193b4be Poul Sander 2025-02-03 21:28 1134
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
1193b4be Poul Sander 2025-02-03 21:28 1135
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
1193b4be Poul Sander 2025-02-03 21:28 1136
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
1193b4be Poul Sander 2025-02-03 21:28 1137
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
1193b4be Poul Sander 2025-02-03 21:28 1138
#elif defined(__GNUC__)
1193b4be Poul Sander 2025-02-03 21:28 1139
// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1193b4be Poul Sander 2025-02-03 21:28 1140
#pragma GCC diagnostic ignored "-Wpragmas"                  // warning: unknown option after '#pragma GCC diagnostic' kind
1193b4be Poul Sander 2025-02-03 21:28 1141
#pragma GCC diagnostic ignored "-Wunused-function"          // warning: 'xxxx' defined but not used
1193b4be Poul Sander 2025-02-03 21:28 1142
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"      // warning: cast to pointer from integer of different size
1193b4be Poul Sander 2025-02-03 21:28 1143
#pragma GCC diagnostic ignored "-Wformat"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
1193b4be Poul Sander 2025-02-03 21:28 1144
#pragma GCC diagnostic ignored "-Wdouble-promotion"         // warning: implicit conversion from 'float' to 'double' when passing argument to function
1193b4be Poul Sander 2025-02-03 21:28 1145
#pragma GCC diagnostic ignored "-Wconversion"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1193b4be Poul Sander 2025-02-03 21:28 1146
#pragma GCC diagnostic ignored "-Wformat-nonliteral"        // warning: format not a string literal, format string not checked
1193b4be Poul Sander 2025-02-03 21:28 1147
#pragma GCC diagnostic ignored "-Wstrict-overflow"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1193b4be Poul Sander 2025-02-03 21:28 1148
#pragma GCC diagnostic ignored "-Wclass-memaccess"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
1193b4be Poul Sander 2025-02-03 21:28 1149
#endif
1193b4be Poul Sander 2025-02-03 21:28 1150
1193b4be Poul Sander 2025-02-03 21:28 1151
// Debug options
1193b4be Poul Sander 2025-02-03 21:28 1152
#define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction.
1193b4be Poul Sander 2025-02-03 21:28 1153
#define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
1193b4be Poul Sander 2025-02-03 21:28 1154
1193b4be Poul Sander 2025-02-03 21:28 1155
// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
1193b4be Poul Sander 2025-02-03 21:28 1156
static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
1193b4be Poul Sander 2025-02-03 21:28 1157
static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
1193b4be Poul Sander 2025-02-03 21:28 1158
1193b4be Poul Sander 2025-02-03 21:28 1159
static const float NAV_ACTIVATE_HIGHLIGHT_TIMER             = 0.10f;    // Time to highlight an item activated by a shortcut.
1193b4be Poul Sander 2025-02-03 21:28 1160
1193b4be Poul Sander 2025-02-03 21:28 1161
// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
1193b4be Poul Sander 2025-02-03 21:28 1162
static const float WINDOWS_HOVER_PADDING                    = 4.0f;     // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
1193b4be Poul Sander 2025-02-03 21:28 1163
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
1193b4be Poul Sander 2025-02-03 21:28 1164
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
1193b4be Poul Sander 2025-02-03 21:28 1165
1193b4be Poul Sander 2025-02-03 21:28 1166
// Tooltip offset
1193b4be Poul Sander 2025-02-03 21:28 1167
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10);      // Multiplied by g.Style.MouseCursorScale
1193b4be Poul Sander 2025-02-03 21:28 1168
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20);      // Multiplied by g.Style.MouseCursorScale
1193b4be Poul Sander 2025-02-03 21:28 1169
static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f);   // Multiplied by g.Style.MouseCursorScale
1193b4be Poul Sander 2025-02-03 21:28 1170
1193b4be Poul Sander 2025-02-03 21:28 1171
// Docking
1193b4be Poul Sander 2025-02-03 21:28 1172
static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA        = 0.50f;    // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
1193b4be Poul Sander 2025-02-03 21:28 1173
1193b4be Poul Sander 2025-02-03 21:28 1174
//-------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1175
// [SECTION] FORWARD DECLARATIONS
1193b4be Poul Sander 2025-02-03 21:28 1176
//-------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1177
1193b4be Poul Sander 2025-02-03 21:28 1178
static void             SetCurrentWindow(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1179
static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1193b4be Poul Sander 2025-02-03 21:28 1180
static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1181
1193b4be Poul Sander 2025-02-03 21:28 1182
static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1183
1193b4be Poul Sander 2025-02-03 21:28 1184
// Settings
1193b4be Poul Sander 2025-02-03 21:28 1185
static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1193b4be Poul Sander 2025-02-03 21:28 1186
static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1193b4be Poul Sander 2025-02-03 21:28 1187
static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1193b4be Poul Sander 2025-02-03 21:28 1188
static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1193b4be Poul Sander 2025-02-03 21:28 1189
static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1193b4be Poul Sander 2025-02-03 21:28 1190
1193b4be Poul Sander 2025-02-03 21:28 1191
// Platform Dependents default implementation for ImGuiPlatformIO functions
1193b4be Poul Sander 2025-02-03 21:28 1192
static const char*      Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1193b4be Poul Sander 2025-02-03 21:28 1193
static void             Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1193b4be Poul Sander 2025-02-03 21:28 1194
static void             Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1193b4be Poul Sander 2025-02-03 21:28 1195
static bool             Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1193b4be Poul Sander 2025-02-03 21:28 1196
1193b4be Poul Sander 2025-02-03 21:28 1197
namespace ImGui
1193b4be Poul Sander 2025-02-03 21:28 1198
{
1193b4be Poul Sander 2025-02-03 21:28 1199
// Item
1193b4be Poul Sander 2025-02-03 21:28 1200
static void             ItemHandleShortcut(ImGuiID id);
1193b4be Poul Sander 2025-02-03 21:28 1201
1193b4be Poul Sander 2025-02-03 21:28 1202
// Navigation
1193b4be Poul Sander 2025-02-03 21:28 1203
static void             NavUpdate();
1193b4be Poul Sander 2025-02-03 21:28 1204
static void             NavUpdateWindowing();
1193b4be Poul Sander 2025-02-03 21:28 1205
static void             NavUpdateWindowingOverlay();
1193b4be Poul Sander 2025-02-03 21:28 1206
static void             NavUpdateCancelRequest();
1193b4be Poul Sander 2025-02-03 21:28 1207
static void             NavUpdateCreateMoveRequest();
1193b4be Poul Sander 2025-02-03 21:28 1208
static void             NavUpdateCreateTabbingRequest();
1193b4be Poul Sander 2025-02-03 21:28 1209
static float            NavUpdatePageUpPageDown();
1193b4be Poul Sander 2025-02-03 21:28 1210
static inline void      NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 1211
static void             NavUpdateCreateWrappingRequest();
1193b4be Poul Sander 2025-02-03 21:28 1212
static void             NavEndFrame();
1193b4be Poul Sander 2025-02-03 21:28 1213
static bool             NavScoreItem(ImGuiNavItemData* result);
1193b4be Poul Sander 2025-02-03 21:28 1214
static void             NavApplyItemToResult(ImGuiNavItemData* result);
1193b4be Poul Sander 2025-02-03 21:28 1215
static void             NavProcessItem();
1193b4be Poul Sander 2025-02-03 21:28 1216
static void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1193b4be Poul Sander 2025-02-03 21:28 1217
static ImGuiInputSource NavCalcPreferredRefPosSource();
1193b4be Poul Sander 2025-02-03 21:28 1218
static ImVec2           NavCalcPreferredRefPos();
1193b4be Poul Sander 2025-02-03 21:28 1219
static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1193b4be Poul Sander 2025-02-03 21:28 1220
static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1221
static void             NavRestoreLayer(ImGuiNavLayer layer);
1193b4be Poul Sander 2025-02-03 21:28 1222
static int              FindWindowFocusIndex(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1223
1193b4be Poul Sander 2025-02-03 21:28 1224
// Error Checking and Debug Tools
1193b4be Poul Sander 2025-02-03 21:28 1225
static void             ErrorCheckNewFrameSanityChecks();
1193b4be Poul Sander 2025-02-03 21:28 1226
static void             ErrorCheckEndFrameSanityChecks();
1193b4be Poul Sander 2025-02-03 21:28 1227
static void             UpdateDebugToolItemPicker();
1193b4be Poul Sander 2025-02-03 21:28 1228
static void             UpdateDebugToolStackQueries();
1193b4be Poul Sander 2025-02-03 21:28 1229
static void             UpdateDebugToolFlashStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 1230
1193b4be Poul Sander 2025-02-03 21:28 1231
// Inputs
1193b4be Poul Sander 2025-02-03 21:28 1232
static void             UpdateKeyboardInputs();
1193b4be Poul Sander 2025-02-03 21:28 1233
static void             UpdateMouseInputs();
1193b4be Poul Sander 2025-02-03 21:28 1234
static void             UpdateMouseWheel();
1193b4be Poul Sander 2025-02-03 21:28 1235
static void             UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1193b4be Poul Sander 2025-02-03 21:28 1236
1193b4be Poul Sander 2025-02-03 21:28 1237
// Misc
1193b4be Poul Sander 2025-02-03 21:28 1238
static void             UpdateSettings();
1193b4be Poul Sander 2025-02-03 21:28 1239
static int              UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
1193b4be Poul Sander 2025-02-03 21:28 1240
static void             RenderWindowOuterBorders(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1241
static void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
1193b4be Poul Sander 2025-02-03 21:28 1242
static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1193b4be Poul Sander 2025-02-03 21:28 1243
static void             RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1193b4be Poul Sander 2025-02-03 21:28 1244
static void             RenderDimmedBackgrounds();
1193b4be Poul Sander 2025-02-03 21:28 1245
static void             SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1193b4be Poul Sander 2025-02-03 21:28 1246
1193b4be Poul Sander 2025-02-03 21:28 1247
// Viewports
1193b4be Poul Sander 2025-02-03 21:28 1248
const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
1193b4be Poul Sander 2025-02-03 21:28 1249
static ImGuiViewportP*  AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
1193b4be Poul Sander 2025-02-03 21:28 1250
static void             DestroyViewport(ImGuiViewportP* viewport);
1193b4be Poul Sander 2025-02-03 21:28 1251
static void             UpdateViewportsNewFrame();
1193b4be Poul Sander 2025-02-03 21:28 1252
static void             UpdateViewportsEndFrame();
1193b4be Poul Sander 2025-02-03 21:28 1253
static void             WindowSelectViewport(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1254
static void             WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);
1193b4be Poul Sander 2025-02-03 21:28 1255
static bool             UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
1193b4be Poul Sander 2025-02-03 21:28 1256
static bool             UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1257
static bool             GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 1258
static int              FindPlatformMonitorForPos(const ImVec2& pos);
1193b4be Poul Sander 2025-02-03 21:28 1259
static int              FindPlatformMonitorForRect(const ImRect& r);
1193b4be Poul Sander 2025-02-03 21:28 1260
static void             UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
1193b4be Poul Sander 2025-02-03 21:28 1261
1193b4be Poul Sander 2025-02-03 21:28 1262
}
1193b4be Poul Sander 2025-02-03 21:28 1263
1193b4be Poul Sander 2025-02-03 21:28 1264
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1265
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1193b4be Poul Sander 2025-02-03 21:28 1266
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1267
1193b4be Poul Sander 2025-02-03 21:28 1268
// DLL users:
1193b4be Poul Sander 2025-02-03 21:28 1269
// - Heaps and globals are not shared across DLL boundaries!
1193b4be Poul Sander 2025-02-03 21:28 1270
// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1193b4be Poul Sander 2025-02-03 21:28 1271
// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1193b4be Poul Sander 2025-02-03 21:28 1272
// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
1193b4be Poul Sander 2025-02-03 21:28 1273
// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
1193b4be Poul Sander 2025-02-03 21:28 1274
1193b4be Poul Sander 2025-02-03 21:28 1275
// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1193b4be Poul Sander 2025-02-03 21:28 1276
// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1193b4be Poul Sander 2025-02-03 21:28 1277
//   Change to a different context by calling ImGui::SetCurrentContext().
1193b4be Poul Sander 2025-02-03 21:28 1278
// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1193b4be Poul Sander 2025-02-03 21:28 1279
//   If you want thread-safety to allow N threads to access N different contexts:
1193b4be Poul Sander 2025-02-03 21:28 1280
//   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1193b4be Poul Sander 2025-02-03 21:28 1281
//         struct ImGuiContext;
1193b4be Poul Sander 2025-02-03 21:28 1282
//         extern thread_local ImGuiContext* MyImGuiTLS;
1193b4be Poul Sander 2025-02-03 21:28 1283
//         #define GImGui MyImGuiTLS
1193b4be Poul Sander 2025-02-03 21:28 1284
//     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
1193b4be Poul Sander 2025-02-03 21:28 1285
//   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1193b4be Poul Sander 2025-02-03 21:28 1286
//   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1193b4be Poul Sander 2025-02-03 21:28 1287
// - DLL users: read comments above.
1193b4be Poul Sander 2025-02-03 21:28 1288
#ifndef GImGui
1193b4be Poul Sander 2025-02-03 21:28 1289
ImGuiContext*   GImGui = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1290
#endif
1193b4be Poul Sander 2025-02-03 21:28 1291
1193b4be Poul Sander 2025-02-03 21:28 1292
// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1193b4be Poul Sander 2025-02-03 21:28 1293
// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
1193b4be Poul Sander 2025-02-03 21:28 1294
// - DLL users: read comments above.
1193b4be Poul Sander 2025-02-03 21:28 1295
#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1193b4be Poul Sander 2025-02-03 21:28 1296
static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
1193b4be Poul Sander 2025-02-03 21:28 1297
static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
1193b4be Poul Sander 2025-02-03 21:28 1298
#else
1193b4be Poul Sander 2025-02-03 21:28 1299
static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1193b4be Poul Sander 2025-02-03 21:28 1300
static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1193b4be Poul Sander 2025-02-03 21:28 1301
#endif
1193b4be Poul Sander 2025-02-03 21:28 1302
static ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;
1193b4be Poul Sander 2025-02-03 21:28 1303
static ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;
1193b4be Poul Sander 2025-02-03 21:28 1304
static void*                GImAllocatorUserData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1305
1193b4be Poul Sander 2025-02-03 21:28 1306
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1307
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1193b4be Poul Sander 2025-02-03 21:28 1308
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1309
1193b4be Poul Sander 2025-02-03 21:28 1310
ImGuiStyle::ImGuiStyle()
1193b4be Poul Sander 2025-02-03 21:28 1311
{
1193b4be Poul Sander 2025-02-03 21:28 1312
    Alpha                       = 1.0f;             // Global alpha applies to everything in Dear ImGui.
1193b4be Poul Sander 2025-02-03 21:28 1313
    DisabledAlpha               = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1193b4be Poul Sander 2025-02-03 21:28 1314
    WindowPadding               = ImVec2(8,8);      // Padding within a window
1193b4be Poul Sander 2025-02-03 21:28 1315
    WindowRounding              = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
1193b4be Poul Sander 2025-02-03 21:28 1316
    WindowBorderSize            = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1193b4be Poul Sander 2025-02-03 21:28 1317
    WindowMinSize               = ImVec2(32,32);    // Minimum window size
1193b4be Poul Sander 2025-02-03 21:28 1318
    WindowTitleAlign            = ImVec2(0.0f,0.5f);// Alignment for title bar text
1193b4be Poul Sander 2025-02-03 21:28 1319
    WindowMenuButtonPosition    = ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1193b4be Poul Sander 2025-02-03 21:28 1320
    ChildRounding               = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1193b4be Poul Sander 2025-02-03 21:28 1321
    ChildBorderSize             = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1193b4be Poul Sander 2025-02-03 21:28 1322
    PopupRounding               = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1193b4be Poul Sander 2025-02-03 21:28 1323
    PopupBorderSize             = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1193b4be Poul Sander 2025-02-03 21:28 1324
    FramePadding                = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1193b4be Poul Sander 2025-02-03 21:28 1325
    FrameRounding               = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1193b4be Poul Sander 2025-02-03 21:28 1326
    FrameBorderSize             = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1193b4be Poul Sander 2025-02-03 21:28 1327
    ItemSpacing                 = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1193b4be Poul Sander 2025-02-03 21:28 1328
    ItemInnerSpacing            = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1193b4be Poul Sander 2025-02-03 21:28 1329
    CellPadding                 = ImVec2(4,2);      // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1193b4be Poul Sander 2025-02-03 21:28 1330
    TouchExtraPadding           = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
1193b4be Poul Sander 2025-02-03 21:28 1331
    IndentSpacing               = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1193b4be Poul Sander 2025-02-03 21:28 1332
    ColumnsMinSpacing           = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1193b4be Poul Sander 2025-02-03 21:28 1333
    ScrollbarSize               = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1193b4be Poul Sander 2025-02-03 21:28 1334
    ScrollbarRounding           = 9.0f;             // Radius of grab corners rounding for scrollbar
1193b4be Poul Sander 2025-02-03 21:28 1335
    GrabMinSize                 = 12.0f;            // Minimum width/height of a grab box for slider/scrollbar
1193b4be Poul Sander 2025-02-03 21:28 1336
    GrabRounding                = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1193b4be Poul Sander 2025-02-03 21:28 1337
    LogSliderDeadzone           = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1193b4be Poul Sander 2025-02-03 21:28 1338
    TabRounding                 = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1193b4be Poul Sander 2025-02-03 21:28 1339
    TabBorderSize               = 0.0f;             // Thickness of border around tabs.
1193b4be Poul Sander 2025-02-03 21:28 1340
    TabMinWidthForCloseButton   = 0.0f;             // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
1193b4be Poul Sander 2025-02-03 21:28 1341
    TabBarBorderSize            = 1.0f;             // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1193b4be Poul Sander 2025-02-03 21:28 1342
    TabBarOverlineSize          = 2.0f;             // Thickness of tab-bar overline, which highlights the selected tab-bar.
1193b4be Poul Sander 2025-02-03 21:28 1343
    TableAngledHeadersAngle     = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1193b4be Poul Sander 2025-02-03 21:28 1344
    TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1193b4be Poul Sander 2025-02-03 21:28 1345
    ColorButtonPosition         = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1193b4be Poul Sander 2025-02-03 21:28 1346
    ButtonTextAlign             = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1193b4be Poul Sander 2025-02-03 21:28 1347
    SelectableTextAlign         = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
1193b4be Poul Sander 2025-02-03 21:28 1348
    SeparatorTextBorderSize     = 3.0f;             // Thickness of border in SeparatorText()
1193b4be Poul Sander 2025-02-03 21:28 1349
    SeparatorTextAlign          = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1193b4be Poul Sander 2025-02-03 21:28 1350
    SeparatorTextPadding        = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
1193b4be Poul Sander 2025-02-03 21:28 1351
    DisplayWindowPadding        = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
1193b4be Poul Sander 2025-02-03 21:28 1352
    DisplaySafeAreaPadding      = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
1193b4be Poul Sander 2025-02-03 21:28 1353
    DockingSeparatorSize        = 2.0f;             // Thickness of resizing border between docked windows
1193b4be Poul Sander 2025-02-03 21:28 1354
    MouseCursorScale            = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1193b4be Poul Sander 2025-02-03 21:28 1355
    AntiAliasedLines            = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1193b4be Poul Sander 2025-02-03 21:28 1356
    AntiAliasedLinesUseTex      = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1193b4be Poul Sander 2025-02-03 21:28 1357
    AntiAliasedFill             = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1193b4be Poul Sander 2025-02-03 21:28 1358
    CurveTessellationTol        = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
1193b4be Poul Sander 2025-02-03 21:28 1359
    CircleTessellationMaxError  = 0.30f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1193b4be Poul Sander 2025-02-03 21:28 1360
1193b4be Poul Sander 2025-02-03 21:28 1361
    // Behaviors
1193b4be Poul Sander 2025-02-03 21:28 1362
    HoverStationaryDelay        = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1193b4be Poul Sander 2025-02-03 21:28 1363
    HoverDelayShort             = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1193b4be Poul Sander 2025-02-03 21:28 1364
    HoverDelayNormal            = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1193b4be Poul Sander 2025-02-03 21:28 1365
    HoverFlagsForTooltipMouse   = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled;    // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1193b4be Poul Sander 2025-02-03 21:28 1366
    HoverFlagsForTooltipNav     = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1193b4be Poul Sander 2025-02-03 21:28 1367
1193b4be Poul Sander 2025-02-03 21:28 1368
    // Default theme
1193b4be Poul Sander 2025-02-03 21:28 1369
    ImGui::StyleColorsDark(this);
1193b4be Poul Sander 2025-02-03 21:28 1370
}
1193b4be Poul Sander 2025-02-03 21:28 1371
1193b4be Poul Sander 2025-02-03 21:28 1372
// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
1193b4be Poul Sander 2025-02-03 21:28 1373
// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
1193b4be Poul Sander 2025-02-03 21:28 1374
void ImGuiStyle::ScaleAllSizes(float scale_factor)
1193b4be Poul Sander 2025-02-03 21:28 1375
{
1193b4be Poul Sander 2025-02-03 21:28 1376
    WindowPadding = ImTrunc(WindowPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1377
    WindowRounding = ImTrunc(WindowRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1378
    WindowMinSize = ImTrunc(WindowMinSize * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1379
    ChildRounding = ImTrunc(ChildRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1380
    PopupRounding = ImTrunc(PopupRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1381
    FramePadding = ImTrunc(FramePadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1382
    FrameRounding = ImTrunc(FrameRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1383
    ItemSpacing = ImTrunc(ItemSpacing * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1384
    ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1385
    CellPadding = ImTrunc(CellPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1386
    TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1387
    IndentSpacing = ImTrunc(IndentSpacing * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1388
    ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1389
    ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1390
    ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1391
    GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1392
    GrabRounding = ImTrunc(GrabRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1393
    LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1394
    TabRounding = ImTrunc(TabRounding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1395
    TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 1396
    TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1397
    SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1398
    DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1399
    DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1400
    DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1401
    MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 1402
}
1193b4be Poul Sander 2025-02-03 21:28 1403
1193b4be Poul Sander 2025-02-03 21:28 1404
ImGuiIO::ImGuiIO()
1193b4be Poul Sander 2025-02-03 21:28 1405
{
1193b4be Poul Sander 2025-02-03 21:28 1406
    // Most fields are initialized with zero
1193b4be Poul Sander 2025-02-03 21:28 1407
    memset(this, 0, sizeof(*this));
1193b4be Poul Sander 2025-02-03 21:28 1408
    IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 1409
1193b4be Poul Sander 2025-02-03 21:28 1410
    // Settings
1193b4be Poul Sander 2025-02-03 21:28 1411
    ConfigFlags = ImGuiConfigFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 1412
    BackendFlags = ImGuiBackendFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 1413
    DisplaySize = ImVec2(-1.0f, -1.0f);
1193b4be Poul Sander 2025-02-03 21:28 1414
    DeltaTime = 1.0f / 60.0f;
1193b4be Poul Sander 2025-02-03 21:28 1415
    IniSavingRate = 5.0f;
1193b4be Poul Sander 2025-02-03 21:28 1416
    IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
1193b4be Poul Sander 2025-02-03 21:28 1417
    LogFilename = "imgui_log.txt";
1193b4be Poul Sander 2025-02-03 21:28 1418
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 1419
    for (int i = 0; i < ImGuiKey_COUNT; i++)
1193b4be Poul Sander 2025-02-03 21:28 1420
        KeyMap[i] = -1;
1193b4be Poul Sander 2025-02-03 21:28 1421
#endif
1193b4be Poul Sander 2025-02-03 21:28 1422
    UserData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1423
1193b4be Poul Sander 2025-02-03 21:28 1424
    Fonts = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1425
    FontGlobalScale = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1426
    FontDefault = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1427
    FontAllowUserScaling = false;
1193b4be Poul Sander 2025-02-03 21:28 1428
    DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 1429
1193b4be Poul Sander 2025-02-03 21:28 1430
    // Keyboard/Gamepad Navigation options
1193b4be Poul Sander 2025-02-03 21:28 1431
    ConfigNavSwapGamepadButtons = false;
1193b4be Poul Sander 2025-02-03 21:28 1432
    ConfigNavMoveSetMousePos = false;
1193b4be Poul Sander 2025-02-03 21:28 1433
    ConfigNavCaptureKeyboard = true;
1193b4be Poul Sander 2025-02-03 21:28 1434
    ConfigNavEscapeClearFocusItem = true;
1193b4be Poul Sander 2025-02-03 21:28 1435
    ConfigNavEscapeClearFocusWindow = false;
1193b4be Poul Sander 2025-02-03 21:28 1436
    ConfigNavCursorVisibleAuto = true;
1193b4be Poul Sander 2025-02-03 21:28 1437
    ConfigNavCursorVisibleAlways = false;
1193b4be Poul Sander 2025-02-03 21:28 1438
1193b4be Poul Sander 2025-02-03 21:28 1439
    // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1193b4be Poul Sander 2025-02-03 21:28 1440
    ConfigDockingNoSplit = false;
1193b4be Poul Sander 2025-02-03 21:28 1441
    ConfigDockingWithShift = false;
1193b4be Poul Sander 2025-02-03 21:28 1442
    ConfigDockingAlwaysTabBar = false;
1193b4be Poul Sander 2025-02-03 21:28 1443
    ConfigDockingTransparentPayload = false;
1193b4be Poul Sander 2025-02-03 21:28 1444
1193b4be Poul Sander 2025-02-03 21:28 1445
    // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1193b4be Poul Sander 2025-02-03 21:28 1446
    ConfigViewportsNoAutoMerge = false;
1193b4be Poul Sander 2025-02-03 21:28 1447
    ConfigViewportsNoTaskBarIcon = false;
1193b4be Poul Sander 2025-02-03 21:28 1448
    ConfigViewportsNoDecoration = true;
1193b4be Poul Sander 2025-02-03 21:28 1449
    ConfigViewportsNoDefaultParent = false;
1193b4be Poul Sander 2025-02-03 21:28 1450
1193b4be Poul Sander 2025-02-03 21:28 1451
    // Miscellaneous options
1193b4be Poul Sander 2025-02-03 21:28 1452
    MouseDrawCursor = false;
1193b4be Poul Sander 2025-02-03 21:28 1453
#ifdef __APPLE__
1193b4be Poul Sander 2025-02-03 21:28 1454
    ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1193b4be Poul Sander 2025-02-03 21:28 1455
#else
1193b4be Poul Sander 2025-02-03 21:28 1456
    ConfigMacOSXBehaviors = false;
1193b4be Poul Sander 2025-02-03 21:28 1457
#endif
1193b4be Poul Sander 2025-02-03 21:28 1458
    ConfigInputTrickleEventQueue = true;
1193b4be Poul Sander 2025-02-03 21:28 1459
    ConfigInputTextCursorBlink = true;
1193b4be Poul Sander 2025-02-03 21:28 1460
    ConfigInputTextEnterKeepActive = false;
1193b4be Poul Sander 2025-02-03 21:28 1461
    ConfigDragClickToInputText = false;
1193b4be Poul Sander 2025-02-03 21:28 1462
    ConfigWindowsResizeFromEdges = true;
1193b4be Poul Sander 2025-02-03 21:28 1463
    ConfigWindowsMoveFromTitleBarOnly = false;
1193b4be Poul Sander 2025-02-03 21:28 1464
    ConfigScrollbarScrollByPage = true;
1193b4be Poul Sander 2025-02-03 21:28 1465
    ConfigMemoryCompactTimer = 60.0f;
1193b4be Poul Sander 2025-02-03 21:28 1466
    ConfigDebugIsDebuggerPresent = false;
1193b4be Poul Sander 2025-02-03 21:28 1467
    ConfigDebugHighlightIdConflicts = true;
1193b4be Poul Sander 2025-02-03 21:28 1468
    ConfigDebugBeginReturnValueOnce = false;
1193b4be Poul Sander 2025-02-03 21:28 1469
    ConfigDebugBeginReturnValueLoop = false;
1193b4be Poul Sander 2025-02-03 21:28 1470
1193b4be Poul Sander 2025-02-03 21:28 1471
    ConfigErrorRecovery = true;
1193b4be Poul Sander 2025-02-03 21:28 1472
    ConfigErrorRecoveryEnableAssert = true;
1193b4be Poul Sander 2025-02-03 21:28 1473
    ConfigErrorRecoveryEnableDebugLog = true;
1193b4be Poul Sander 2025-02-03 21:28 1474
    ConfigErrorRecoveryEnableTooltip = true;
1193b4be Poul Sander 2025-02-03 21:28 1475
1193b4be Poul Sander 2025-02-03 21:28 1476
    // Inputs Behaviors
1193b4be Poul Sander 2025-02-03 21:28 1477
    MouseDoubleClickTime = 0.30f;
1193b4be Poul Sander 2025-02-03 21:28 1478
    MouseDoubleClickMaxDist = 6.0f;
1193b4be Poul Sander 2025-02-03 21:28 1479
    MouseDragThreshold = 6.0f;
1193b4be Poul Sander 2025-02-03 21:28 1480
    KeyRepeatDelay = 0.275f;
1193b4be Poul Sander 2025-02-03 21:28 1481
    KeyRepeatRate = 0.050f;
1193b4be Poul Sander 2025-02-03 21:28 1482
1193b4be Poul Sander 2025-02-03 21:28 1483
    // Platform Functions
1193b4be Poul Sander 2025-02-03 21:28 1484
    // Note: Initialize() will setup default clipboard/ime handlers.
1193b4be Poul Sander 2025-02-03 21:28 1485
    BackendPlatformName = BackendRendererName = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1486
    BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 1487
1193b4be Poul Sander 2025-02-03 21:28 1488
    // Input (NB: we already have memset zero the entire structure!)
1193b4be Poul Sander 2025-02-03 21:28 1489
    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 1490
    MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 1491
    MouseSource = ImGuiMouseSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 1492
    for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1493
    for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1193b4be Poul Sander 2025-02-03 21:28 1494
    AppAcceptingEvents = true;
1193b4be Poul Sander 2025-02-03 21:28 1495
    BackendUsingLegacyKeyArrays = (ImS8)-1;
1193b4be Poul Sander 2025-02-03 21:28 1496
    BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
1193b4be Poul Sander 2025-02-03 21:28 1497
}
1193b4be Poul Sander 2025-02-03 21:28 1498
1193b4be Poul Sander 2025-02-03 21:28 1499
// Pass in translated ASCII characters for text input.
1193b4be Poul Sander 2025-02-03 21:28 1500
// - with glfw you can get those from the callback set in glfwSetCharCallback()
1193b4be Poul Sander 2025-02-03 21:28 1501
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1193b4be Poul Sander 2025-02-03 21:28 1502
// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1193b4be Poul Sander 2025-02-03 21:28 1503
void ImGuiIO::AddInputCharacter(unsigned int c)
1193b4be Poul Sander 2025-02-03 21:28 1504
{
1193b4be Poul Sander 2025-02-03 21:28 1505
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1506
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1507
    if (c == 0 || !AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1508
        return;
1193b4be Poul Sander 2025-02-03 21:28 1509
1193b4be Poul Sander 2025-02-03 21:28 1510
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1511
    e.Type = ImGuiInputEventType_Text;
1193b4be Poul Sander 2025-02-03 21:28 1512
    e.Source = ImGuiInputSource_Keyboard;
1193b4be Poul Sander 2025-02-03 21:28 1513
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1514
    e.Text.Char = c;
1193b4be Poul Sander 2025-02-03 21:28 1515
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1516
}
1193b4be Poul Sander 2025-02-03 21:28 1517
1193b4be Poul Sander 2025-02-03 21:28 1518
// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1193b4be Poul Sander 2025-02-03 21:28 1519
// we should save the high surrogate.
1193b4be Poul Sander 2025-02-03 21:28 1520
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1193b4be Poul Sander 2025-02-03 21:28 1521
{
1193b4be Poul Sander 2025-02-03 21:28 1522
    if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1523
        return;
1193b4be Poul Sander 2025-02-03 21:28 1524
1193b4be Poul Sander 2025-02-03 21:28 1525
    if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1193b4be Poul Sander 2025-02-03 21:28 1526
    {
1193b4be Poul Sander 2025-02-03 21:28 1527
        if (InputQueueSurrogate != 0)
1193b4be Poul Sander 2025-02-03 21:28 1528
            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1193b4be Poul Sander 2025-02-03 21:28 1529
        InputQueueSurrogate = c;
1193b4be Poul Sander 2025-02-03 21:28 1530
        return;
1193b4be Poul Sander 2025-02-03 21:28 1531
    }
1193b4be Poul Sander 2025-02-03 21:28 1532
1193b4be Poul Sander 2025-02-03 21:28 1533
    ImWchar cp = c;
1193b4be Poul Sander 2025-02-03 21:28 1534
    if (InputQueueSurrogate != 0)
1193b4be Poul Sander 2025-02-03 21:28 1535
    {
1193b4be Poul Sander 2025-02-03 21:28 1536
        if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1193b4be Poul Sander 2025-02-03 21:28 1537
        {
1193b4be Poul Sander 2025-02-03 21:28 1538
            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1193b4be Poul Sander 2025-02-03 21:28 1539
        }
1193b4be Poul Sander 2025-02-03 21:28 1540
        else
1193b4be Poul Sander 2025-02-03 21:28 1541
        {
1193b4be Poul Sander 2025-02-03 21:28 1542
#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1193b4be Poul Sander 2025-02-03 21:28 1543
            cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1193b4be Poul Sander 2025-02-03 21:28 1544
#else
1193b4be Poul Sander 2025-02-03 21:28 1545
            cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1193b4be Poul Sander 2025-02-03 21:28 1546
#endif
1193b4be Poul Sander 2025-02-03 21:28 1547
        }
1193b4be Poul Sander 2025-02-03 21:28 1548
1193b4be Poul Sander 2025-02-03 21:28 1549
        InputQueueSurrogate = 0;
1193b4be Poul Sander 2025-02-03 21:28 1550
    }
1193b4be Poul Sander 2025-02-03 21:28 1551
    AddInputCharacter((unsigned)cp);
1193b4be Poul Sander 2025-02-03 21:28 1552
}
1193b4be Poul Sander 2025-02-03 21:28 1553
1193b4be Poul Sander 2025-02-03 21:28 1554
void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1193b4be Poul Sander 2025-02-03 21:28 1555
{
1193b4be Poul Sander 2025-02-03 21:28 1556
    if (!AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1557
        return;
1193b4be Poul Sander 2025-02-03 21:28 1558
    while (*utf8_chars != 0)
1193b4be Poul Sander 2025-02-03 21:28 1559
    {
1193b4be Poul Sander 2025-02-03 21:28 1560
        unsigned int c = 0;
1193b4be Poul Sander 2025-02-03 21:28 1561
        utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1193b4be Poul Sander 2025-02-03 21:28 1562
        AddInputCharacter(c);
1193b4be Poul Sander 2025-02-03 21:28 1563
    }
1193b4be Poul Sander 2025-02-03 21:28 1564
}
1193b4be Poul Sander 2025-02-03 21:28 1565
1193b4be Poul Sander 2025-02-03 21:28 1566
// Clear all incoming events.
1193b4be Poul Sander 2025-02-03 21:28 1567
void ImGuiIO::ClearEventsQueue()
1193b4be Poul Sander 2025-02-03 21:28 1568
{
1193b4be Poul Sander 2025-02-03 21:28 1569
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1570
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1571
    g.InputEventsQueue.clear();
1193b4be Poul Sander 2025-02-03 21:28 1572
}
1193b4be Poul Sander 2025-02-03 21:28 1573
1193b4be Poul Sander 2025-02-03 21:28 1574
// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1193b4be Poul Sander 2025-02-03 21:28 1575
void ImGuiIO::ClearInputKeys()
1193b4be Poul Sander 2025-02-03 21:28 1576
{
1193b4be Poul Sander 2025-02-03 21:28 1577
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 1578
    memset(KeysDown, 0, sizeof(KeysDown));
1193b4be Poul Sander 2025-02-03 21:28 1579
#endif
1193b4be Poul Sander 2025-02-03 21:28 1580
    for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
1193b4be Poul Sander 2025-02-03 21:28 1581
    {
1193b4be Poul Sander 2025-02-03 21:28 1582
        if (ImGui::IsMouseKey((ImGuiKey)(n + ImGuiKey_KeysData_OFFSET)))
1193b4be Poul Sander 2025-02-03 21:28 1583
            continue;
1193b4be Poul Sander 2025-02-03 21:28 1584
        KeysData[n].Down             = false;
1193b4be Poul Sander 2025-02-03 21:28 1585
        KeysData[n].DownDuration     = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1586
        KeysData[n].DownDurationPrev = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1587
    }
1193b4be Poul Sander 2025-02-03 21:28 1588
    KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1193b4be Poul Sander 2025-02-03 21:28 1589
    KeyMods = ImGuiMod_None;
1193b4be Poul Sander 2025-02-03 21:28 1590
    InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1193b4be Poul Sander 2025-02-03 21:28 1591
}
1193b4be Poul Sander 2025-02-03 21:28 1592
1193b4be Poul Sander 2025-02-03 21:28 1593
void ImGuiIO::ClearInputMouse()
1193b4be Poul Sander 2025-02-03 21:28 1594
{
1193b4be Poul Sander 2025-02-03 21:28 1595
    for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 1596
    {
1193b4be Poul Sander 2025-02-03 21:28 1597
        ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_KeysData_OFFSET];
1193b4be Poul Sander 2025-02-03 21:28 1598
        key_data->Down = false;
1193b4be Poul Sander 2025-02-03 21:28 1599
        key_data->DownDuration = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1600
        key_data->DownDurationPrev = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1601
    }
1193b4be Poul Sander 2025-02-03 21:28 1602
    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 1603
    for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1193b4be Poul Sander 2025-02-03 21:28 1604
    {
1193b4be Poul Sander 2025-02-03 21:28 1605
        MouseDown[n] = false;
1193b4be Poul Sander 2025-02-03 21:28 1606
        MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 1607
    }
1193b4be Poul Sander 2025-02-03 21:28 1608
    MouseWheel = MouseWheelH = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 1609
}
1193b4be Poul Sander 2025-02-03 21:28 1610
1193b4be Poul Sander 2025-02-03 21:28 1611
// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1193b4be Poul Sander 2025-02-03 21:28 1612
// Current frame character buffer is now also cleared by ClearInputKeys().
1193b4be Poul Sander 2025-02-03 21:28 1613
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 1614
void ImGuiIO::ClearInputCharacters()
1193b4be Poul Sander 2025-02-03 21:28 1615
{
1193b4be Poul Sander 2025-02-03 21:28 1616
    InputQueueCharacters.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 1617
}
1193b4be Poul Sander 2025-02-03 21:28 1618
#endif
1193b4be Poul Sander 2025-02-03 21:28 1619
1193b4be Poul Sander 2025-02-03 21:28 1620
static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1193b4be Poul Sander 2025-02-03 21:28 1621
{
1193b4be Poul Sander 2025-02-03 21:28 1622
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 1623
    for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 1624
    {
1193b4be Poul Sander 2025-02-03 21:28 1625
        ImGuiInputEvent* e = &g.InputEventsQueue[n];
1193b4be Poul Sander 2025-02-03 21:28 1626
        if (e->Type != type)
1193b4be Poul Sander 2025-02-03 21:28 1627
            continue;
1193b4be Poul Sander 2025-02-03 21:28 1628
        if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1193b4be Poul Sander 2025-02-03 21:28 1629
            continue;
1193b4be Poul Sander 2025-02-03 21:28 1630
        if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1193b4be Poul Sander 2025-02-03 21:28 1631
            continue;
1193b4be Poul Sander 2025-02-03 21:28 1632
        return e;
1193b4be Poul Sander 2025-02-03 21:28 1633
    }
1193b4be Poul Sander 2025-02-03 21:28 1634
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 1635
}
1193b4be Poul Sander 2025-02-03 21:28 1636
1193b4be Poul Sander 2025-02-03 21:28 1637
// Queue a new key down/up event.
1193b4be Poul Sander 2025-02-03 21:28 1638
// - ImGuiKey key:       Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1193b4be Poul Sander 2025-02-03 21:28 1639
// - bool down:          Is the key down? use false to signify a key release.
1193b4be Poul Sander 2025-02-03 21:28 1640
// - float analog_value: 0.0f..1.0f
1193b4be Poul Sander 2025-02-03 21:28 1641
// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1193b4be Poul Sander 2025-02-03 21:28 1642
// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1193b4be Poul Sander 2025-02-03 21:28 1643
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1193b4be Poul Sander 2025-02-03 21:28 1644
{
1193b4be Poul Sander 2025-02-03 21:28 1645
    //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
1193b4be Poul Sander 2025-02-03 21:28 1646
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1647
    if (key == ImGuiKey_None || !AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1648
        return;
1193b4be Poul Sander 2025-02-03 21:28 1649
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1650
    IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
1193b4be Poul Sander 2025-02-03 21:28 1651
    IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1193b4be Poul Sander 2025-02-03 21:28 1652
1193b4be Poul Sander 2025-02-03 21:28 1653
    // MacOS: swap Cmd(Super) and Ctrl
1193b4be Poul Sander 2025-02-03 21:28 1654
    if (g.IO.ConfigMacOSXBehaviors)
1193b4be Poul Sander 2025-02-03 21:28 1655
    {
1193b4be Poul Sander 2025-02-03 21:28 1656
        if (key == ImGuiMod_Super)          { key = ImGuiMod_Ctrl; }
1193b4be Poul Sander 2025-02-03 21:28 1657
        else if (key == ImGuiMod_Ctrl)      { key = ImGuiMod_Super; }
1193b4be Poul Sander 2025-02-03 21:28 1658
        else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1193b4be Poul Sander 2025-02-03 21:28 1659
        else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1193b4be Poul Sander 2025-02-03 21:28 1660
        else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_LeftSuper; }
1193b4be Poul Sander 2025-02-03 21:28 1661
        else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1193b4be Poul Sander 2025-02-03 21:28 1662
    }
1193b4be Poul Sander 2025-02-03 21:28 1663
1193b4be Poul Sander 2025-02-03 21:28 1664
    // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
1193b4be Poul Sander 2025-02-03 21:28 1665
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 1666
    IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1193b4be Poul Sander 2025-02-03 21:28 1667
    if (BackendUsingLegacyKeyArrays == -1)
1193b4be Poul Sander 2025-02-03 21:28 1668
        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
1193b4be Poul Sander 2025-02-03 21:28 1669
            IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1193b4be Poul Sander 2025-02-03 21:28 1670
    BackendUsingLegacyKeyArrays = 0;
1193b4be Poul Sander 2025-02-03 21:28 1671
#endif
1193b4be Poul Sander 2025-02-03 21:28 1672
    if (ImGui::IsGamepadKey(key))
1193b4be Poul Sander 2025-02-03 21:28 1673
        BackendUsingLegacyNavInputArray = false;
1193b4be Poul Sander 2025-02-03 21:28 1674
1193b4be Poul Sander 2025-02-03 21:28 1675
    // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1193b4be Poul Sander 2025-02-03 21:28 1676
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1193b4be Poul Sander 2025-02-03 21:28 1677
    const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 1678
    const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1193b4be Poul Sander 2025-02-03 21:28 1679
    const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1193b4be Poul Sander 2025-02-03 21:28 1680
    if (latest_key_down == down && latest_key_analog == analog_value)
1193b4be Poul Sander 2025-02-03 21:28 1681
        return;
1193b4be Poul Sander 2025-02-03 21:28 1682
1193b4be Poul Sander 2025-02-03 21:28 1683
    // Add event
1193b4be Poul Sander 2025-02-03 21:28 1684
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1685
    e.Type = ImGuiInputEventType_Key;
1193b4be Poul Sander 2025-02-03 21:28 1686
    e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1193b4be Poul Sander 2025-02-03 21:28 1687
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1688
    e.Key.Key = key;
1193b4be Poul Sander 2025-02-03 21:28 1689
    e.Key.Down = down;
1193b4be Poul Sander 2025-02-03 21:28 1690
    e.Key.AnalogValue = analog_value;
1193b4be Poul Sander 2025-02-03 21:28 1691
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1692
}
1193b4be Poul Sander 2025-02-03 21:28 1693
1193b4be Poul Sander 2025-02-03 21:28 1694
void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1193b4be Poul Sander 2025-02-03 21:28 1695
{
1193b4be Poul Sander 2025-02-03 21:28 1696
    if (!AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1697
        return;
1193b4be Poul Sander 2025-02-03 21:28 1698
    AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 1699
}
1193b4be Poul Sander 2025-02-03 21:28 1700
1193b4be Poul Sander 2025-02-03 21:28 1701
// [Optional] Call after AddKeyEvent().
1193b4be Poul Sander 2025-02-03 21:28 1702
// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1193b4be Poul Sander 2025-02-03 21:28 1703
// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.
1193b4be Poul Sander 2025-02-03 21:28 1704
void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1193b4be Poul Sander 2025-02-03 21:28 1705
{
1193b4be Poul Sander 2025-02-03 21:28 1706
    if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 1707
        return;
1193b4be Poul Sander 2025-02-03 21:28 1708
    IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1193b4be Poul Sander 2025-02-03 21:28 1709
    IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1193b4be Poul Sander 2025-02-03 21:28 1710
    IM_UNUSED(native_keycode);  // Yet unused
1193b4be Poul Sander 2025-02-03 21:28 1711
    IM_UNUSED(native_scancode); // Yet unused
1193b4be Poul Sander 2025-02-03 21:28 1712
1193b4be Poul Sander 2025-02-03 21:28 1713
    // Build native->imgui map so old user code can still call key functions with native 0..511 values.
1193b4be Poul Sander 2025-02-03 21:28 1714
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 1715
    const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
1193b4be Poul Sander 2025-02-03 21:28 1716
    if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))
1193b4be Poul Sander 2025-02-03 21:28 1717
        return;
1193b4be Poul Sander 2025-02-03 21:28 1718
    KeyMap[legacy_key] = key;
1193b4be Poul Sander 2025-02-03 21:28 1719
    KeyMap[key] = legacy_key;
1193b4be Poul Sander 2025-02-03 21:28 1720
#else
1193b4be Poul Sander 2025-02-03 21:28 1721
    IM_UNUSED(key);
1193b4be Poul Sander 2025-02-03 21:28 1722
    IM_UNUSED(native_legacy_index);
1193b4be Poul Sander 2025-02-03 21:28 1723
#endif
1193b4be Poul Sander 2025-02-03 21:28 1724
}
1193b4be Poul Sander 2025-02-03 21:28 1725
1193b4be Poul Sander 2025-02-03 21:28 1726
// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
1193b4be Poul Sander 2025-02-03 21:28 1727
void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1193b4be Poul Sander 2025-02-03 21:28 1728
{
1193b4be Poul Sander 2025-02-03 21:28 1729
    AppAcceptingEvents = accepting_events;
1193b4be Poul Sander 2025-02-03 21:28 1730
}
1193b4be Poul Sander 2025-02-03 21:28 1731
1193b4be Poul Sander 2025-02-03 21:28 1732
// Queue a mouse move event
1193b4be Poul Sander 2025-02-03 21:28 1733
void ImGuiIO::AddMousePosEvent(float x, float y)
1193b4be Poul Sander 2025-02-03 21:28 1734
{
1193b4be Poul Sander 2025-02-03 21:28 1735
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1736
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1737
    if (!AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1738
        return;
1193b4be Poul Sander 2025-02-03 21:28 1739
1193b4be Poul Sander 2025-02-03 21:28 1740
    // Apply same flooring as UpdateMouseInputs()
1193b4be Poul Sander 2025-02-03 21:28 1741
    ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1193b4be Poul Sander 2025-02-03 21:28 1742
1193b4be Poul Sander 2025-02-03 21:28 1743
    // Filter duplicate
1193b4be Poul Sander 2025-02-03 21:28 1744
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1193b4be Poul Sander 2025-02-03 21:28 1745
    const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 1746
    if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1193b4be Poul Sander 2025-02-03 21:28 1747
        return;
1193b4be Poul Sander 2025-02-03 21:28 1748
1193b4be Poul Sander 2025-02-03 21:28 1749
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1750
    e.Type = ImGuiInputEventType_MousePos;
1193b4be Poul Sander 2025-02-03 21:28 1751
    e.Source = ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 1752
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1753
    e.MousePos.PosX = pos.x;
1193b4be Poul Sander 2025-02-03 21:28 1754
    e.MousePos.PosY = pos.y;
1193b4be Poul Sander 2025-02-03 21:28 1755
    e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1193b4be Poul Sander 2025-02-03 21:28 1756
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1757
}
1193b4be Poul Sander 2025-02-03 21:28 1758
1193b4be Poul Sander 2025-02-03 21:28 1759
void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1193b4be Poul Sander 2025-02-03 21:28 1760
{
1193b4be Poul Sander 2025-02-03 21:28 1761
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1762
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1763
    IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 1764
    if (!AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1765
        return;
1193b4be Poul Sander 2025-02-03 21:28 1766
1193b4be Poul Sander 2025-02-03 21:28 1767
    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1193b4be Poul Sander 2025-02-03 21:28 1768
    if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1193b4be Poul Sander 2025-02-03 21:28 1769
    {
1193b4be Poul Sander 2025-02-03 21:28 1770
        // Order of both statements matterns: this event will still release mouse button 1
1193b4be Poul Sander 2025-02-03 21:28 1771
        mouse_button = 1;
1193b4be Poul Sander 2025-02-03 21:28 1772
        if (!down)
1193b4be Poul Sander 2025-02-03 21:28 1773
            MouseCtrlLeftAsRightClick = false;
1193b4be Poul Sander 2025-02-03 21:28 1774
    }
1193b4be Poul Sander 2025-02-03 21:28 1775
1193b4be Poul Sander 2025-02-03 21:28 1776
    // Filter duplicate
1193b4be Poul Sander 2025-02-03 21:28 1777
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1193b4be Poul Sander 2025-02-03 21:28 1778
    const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1193b4be Poul Sander 2025-02-03 21:28 1779
    if (latest_button_down == down)
1193b4be Poul Sander 2025-02-03 21:28 1780
        return;
1193b4be Poul Sander 2025-02-03 21:28 1781
1193b4be Poul Sander 2025-02-03 21:28 1782
    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1193b4be Poul Sander 2025-02-03 21:28 1783
    // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1193b4be Poul Sander 2025-02-03 21:28 1784
    // - At this point we want from !down to down, so this is handling the initial press.
1193b4be Poul Sander 2025-02-03 21:28 1785
    if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1193b4be Poul Sander 2025-02-03 21:28 1786
    {
1193b4be Poul Sander 2025-02-03 21:28 1787
        const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1193b4be Poul Sander 2025-02-03 21:28 1788
        if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1193b4be Poul Sander 2025-02-03 21:28 1789
        {
1193b4be Poul Sander 2025-02-03 21:28 1790
            IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1193b4be Poul Sander 2025-02-03 21:28 1791
            MouseCtrlLeftAsRightClick = true;
1193b4be Poul Sander 2025-02-03 21:28 1792
            AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1193b4be Poul Sander 2025-02-03 21:28 1793
            return;
1193b4be Poul Sander 2025-02-03 21:28 1794
        }
1193b4be Poul Sander 2025-02-03 21:28 1795
    }
1193b4be Poul Sander 2025-02-03 21:28 1796
1193b4be Poul Sander 2025-02-03 21:28 1797
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1798
    e.Type = ImGuiInputEventType_MouseButton;
1193b4be Poul Sander 2025-02-03 21:28 1799
    e.Source = ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 1800
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1801
    e.MouseButton.Button = mouse_button;
1193b4be Poul Sander 2025-02-03 21:28 1802
    e.MouseButton.Down = down;
1193b4be Poul Sander 2025-02-03 21:28 1803
    e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1193b4be Poul Sander 2025-02-03 21:28 1804
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1805
}
1193b4be Poul Sander 2025-02-03 21:28 1806
1193b4be Poul Sander 2025-02-03 21:28 1807
// Queue a mouse wheel event (some mouse/API may only have a Y component)
1193b4be Poul Sander 2025-02-03 21:28 1808
void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1193b4be Poul Sander 2025-02-03 21:28 1809
{
1193b4be Poul Sander 2025-02-03 21:28 1810
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1811
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1812
1193b4be Poul Sander 2025-02-03 21:28 1813
    // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1193b4be Poul Sander 2025-02-03 21:28 1814
    if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1193b4be Poul Sander 2025-02-03 21:28 1815
        return;
1193b4be Poul Sander 2025-02-03 21:28 1816
1193b4be Poul Sander 2025-02-03 21:28 1817
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1818
    e.Type = ImGuiInputEventType_MouseWheel;
1193b4be Poul Sander 2025-02-03 21:28 1819
    e.Source = ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 1820
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1821
    e.MouseWheel.WheelX = wheel_x;
1193b4be Poul Sander 2025-02-03 21:28 1822
    e.MouseWheel.WheelY = wheel_y;
1193b4be Poul Sander 2025-02-03 21:28 1823
    e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1193b4be Poul Sander 2025-02-03 21:28 1824
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1825
}
1193b4be Poul Sander 2025-02-03 21:28 1826
1193b4be Poul Sander 2025-02-03 21:28 1827
// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1193b4be Poul Sander 2025-02-03 21:28 1828
// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1193b4be Poul Sander 2025-02-03 21:28 1829
void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1193b4be Poul Sander 2025-02-03 21:28 1830
{
1193b4be Poul Sander 2025-02-03 21:28 1831
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1832
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1833
    g.InputEventsNextMouseSource = source;
1193b4be Poul Sander 2025-02-03 21:28 1834
}
1193b4be Poul Sander 2025-02-03 21:28 1835
1193b4be Poul Sander 2025-02-03 21:28 1836
void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id)
1193b4be Poul Sander 2025-02-03 21:28 1837
{
1193b4be Poul Sander 2025-02-03 21:28 1838
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1839
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1840
    //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport);
1193b4be Poul Sander 2025-02-03 21:28 1841
    if (!AppAcceptingEvents)
1193b4be Poul Sander 2025-02-03 21:28 1842
        return;
1193b4be Poul Sander 2025-02-03 21:28 1843
1193b4be Poul Sander 2025-02-03 21:28 1844
    // Filter duplicate
1193b4be Poul Sander 2025-02-03 21:28 1845
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport);
1193b4be Poul Sander 2025-02-03 21:28 1846
    const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport;
1193b4be Poul Sander 2025-02-03 21:28 1847
    if (latest_viewport_id == viewport_id)
1193b4be Poul Sander 2025-02-03 21:28 1848
        return;
1193b4be Poul Sander 2025-02-03 21:28 1849
1193b4be Poul Sander 2025-02-03 21:28 1850
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1851
    e.Type = ImGuiInputEventType_MouseViewport;
1193b4be Poul Sander 2025-02-03 21:28 1852
    e.Source = ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 1853
    e.MouseViewport.HoveredViewportID = viewport_id;
1193b4be Poul Sander 2025-02-03 21:28 1854
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1855
}
1193b4be Poul Sander 2025-02-03 21:28 1856
1193b4be Poul Sander 2025-02-03 21:28 1857
void ImGuiIO::AddFocusEvent(bool focused)
1193b4be Poul Sander 2025-02-03 21:28 1858
{
1193b4be Poul Sander 2025-02-03 21:28 1859
    IM_ASSERT(Ctx != NULL);
1193b4be Poul Sander 2025-02-03 21:28 1860
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 1861
1193b4be Poul Sander 2025-02-03 21:28 1862
    // Filter duplicate
1193b4be Poul Sander 2025-02-03 21:28 1863
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1193b4be Poul Sander 2025-02-03 21:28 1864
    const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1193b4be Poul Sander 2025-02-03 21:28 1865
    if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1193b4be Poul Sander 2025-02-03 21:28 1866
        return;
1193b4be Poul Sander 2025-02-03 21:28 1867
1193b4be Poul Sander 2025-02-03 21:28 1868
    ImGuiInputEvent e;
1193b4be Poul Sander 2025-02-03 21:28 1869
    e.Type = ImGuiInputEventType_Focus;
1193b4be Poul Sander 2025-02-03 21:28 1870
    e.EventId = g.InputEventsNextEventId++;
1193b4be Poul Sander 2025-02-03 21:28 1871
    e.AppFocused.Focused = focused;
1193b4be Poul Sander 2025-02-03 21:28 1872
    g.InputEventsQueue.push_back(e);
1193b4be Poul Sander 2025-02-03 21:28 1873
}
1193b4be Poul Sander 2025-02-03 21:28 1874
1193b4be Poul Sander 2025-02-03 21:28 1875
ImGuiPlatformIO::ImGuiPlatformIO()
1193b4be Poul Sander 2025-02-03 21:28 1876
{
1193b4be Poul Sander 2025-02-03 21:28 1877
    // Most fields are initialized with zero
1193b4be Poul Sander 2025-02-03 21:28 1878
    memset(this, 0, sizeof(*this));
1193b4be Poul Sander 2025-02-03 21:28 1879
    Platform_LocaleDecimalPoint = '.';
1193b4be Poul Sander 2025-02-03 21:28 1880
}
1193b4be Poul Sander 2025-02-03 21:28 1881
1193b4be Poul Sander 2025-02-03 21:28 1882
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1883
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1193b4be Poul Sander 2025-02-03 21:28 1884
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 1885
1193b4be Poul Sander 2025-02-03 21:28 1886
ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1193b4be Poul Sander 2025-02-03 21:28 1887
{
1193b4be Poul Sander 2025-02-03 21:28 1888
    IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1193b4be Poul Sander 2025-02-03 21:28 1889
    ImVec2 p_last = p1;
1193b4be Poul Sander 2025-02-03 21:28 1890
    ImVec2 p_closest;
1193b4be Poul Sander 2025-02-03 21:28 1891
    float p_closest_dist2 = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 1892
    float t_step = 1.0f / (float)num_segments;
1193b4be Poul Sander 2025-02-03 21:28 1893
    for (int i_step = 1; i_step <= num_segments; i_step++)
1193b4be Poul Sander 2025-02-03 21:28 1894
    {
1193b4be Poul Sander 2025-02-03 21:28 1895
        ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1193b4be Poul Sander 2025-02-03 21:28 1896
        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1193b4be Poul Sander 2025-02-03 21:28 1897
        float dist2 = ImLengthSqr(p - p_line);
1193b4be Poul Sander 2025-02-03 21:28 1898
        if (dist2 < p_closest_dist2)
1193b4be Poul Sander 2025-02-03 21:28 1899
        {
1193b4be Poul Sander 2025-02-03 21:28 1900
            p_closest = p_line;
1193b4be Poul Sander 2025-02-03 21:28 1901
            p_closest_dist2 = dist2;
1193b4be Poul Sander 2025-02-03 21:28 1902
        }
1193b4be Poul Sander 2025-02-03 21:28 1903
        p_last = p_current;
1193b4be Poul Sander 2025-02-03 21:28 1904
    }
1193b4be Poul Sander 2025-02-03 21:28 1905
    return p_closest;
1193b4be Poul Sander 2025-02-03 21:28 1906
}
1193b4be Poul Sander 2025-02-03 21:28 1907
1193b4be Poul Sander 2025-02-03 21:28 1908
// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1193b4be Poul Sander 2025-02-03 21:28 1909
static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1193b4be Poul Sander 2025-02-03 21:28 1910
{
1193b4be Poul Sander 2025-02-03 21:28 1911
    float dx = x4 - x1;
1193b4be Poul Sander 2025-02-03 21:28 1912
    float dy = y4 - y1;
1193b4be Poul Sander 2025-02-03 21:28 1913
    float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1193b4be Poul Sander 2025-02-03 21:28 1914
    float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1193b4be Poul Sander 2025-02-03 21:28 1915
    d2 = (d2 >= 0) ? d2 : -d2;
1193b4be Poul Sander 2025-02-03 21:28 1916
    d3 = (d3 >= 0) ? d3 : -d3;
1193b4be Poul Sander 2025-02-03 21:28 1917
    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1193b4be Poul Sander 2025-02-03 21:28 1918
    {
1193b4be Poul Sander 2025-02-03 21:28 1919
        ImVec2 p_current(x4, y4);
1193b4be Poul Sander 2025-02-03 21:28 1920
        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1193b4be Poul Sander 2025-02-03 21:28 1921
        float dist2 = ImLengthSqr(p - p_line);
1193b4be Poul Sander 2025-02-03 21:28 1922
        if (dist2 < p_closest_dist2)
1193b4be Poul Sander 2025-02-03 21:28 1923
        {
1193b4be Poul Sander 2025-02-03 21:28 1924
            p_closest = p_line;
1193b4be Poul Sander 2025-02-03 21:28 1925
            p_closest_dist2 = dist2;
1193b4be Poul Sander 2025-02-03 21:28 1926
        }
1193b4be Poul Sander 2025-02-03 21:28 1927
        p_last = p_current;
1193b4be Poul Sander 2025-02-03 21:28 1928
    }
1193b4be Poul Sander 2025-02-03 21:28 1929
    else if (level < 10)
1193b4be Poul Sander 2025-02-03 21:28 1930
    {
1193b4be Poul Sander 2025-02-03 21:28 1931
        float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1932
        float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1933
        float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1934
        float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1935
        float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1936
        float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1193b4be Poul Sander 2025-02-03 21:28 1937
        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1193b4be Poul Sander 2025-02-03 21:28 1938
        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1193b4be Poul Sander 2025-02-03 21:28 1939
    }
1193b4be Poul Sander 2025-02-03 21:28 1940
}
1193b4be Poul Sander 2025-02-03 21:28 1941
1193b4be Poul Sander 2025-02-03 21:28 1942
// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1193b4be Poul Sander 2025-02-03 21:28 1943
// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1193b4be Poul Sander 2025-02-03 21:28 1944
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1193b4be Poul Sander 2025-02-03 21:28 1945
{
1193b4be Poul Sander 2025-02-03 21:28 1946
    IM_ASSERT(tess_tol > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 1947
    ImVec2 p_last = p1;
1193b4be Poul Sander 2025-02-03 21:28 1948
    ImVec2 p_closest;
1193b4be Poul Sander 2025-02-03 21:28 1949
    float p_closest_dist2 = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 1950
    ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1193b4be Poul Sander 2025-02-03 21:28 1951
    return p_closest;
1193b4be Poul Sander 2025-02-03 21:28 1952
}
1193b4be Poul Sander 2025-02-03 21:28 1953
1193b4be Poul Sander 2025-02-03 21:28 1954
ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1193b4be Poul Sander 2025-02-03 21:28 1955
{
1193b4be Poul Sander 2025-02-03 21:28 1956
    ImVec2 ap = p - a;
1193b4be Poul Sander 2025-02-03 21:28 1957
    ImVec2 ab_dir = b - a;
1193b4be Poul Sander 2025-02-03 21:28 1958
    float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1193b4be Poul Sander 2025-02-03 21:28 1959
    if (dot < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 1960
        return a;
1193b4be Poul Sander 2025-02-03 21:28 1961
    float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1193b4be Poul Sander 2025-02-03 21:28 1962
    if (dot > ab_len_sqr)
1193b4be Poul Sander 2025-02-03 21:28 1963
        return b;
1193b4be Poul Sander 2025-02-03 21:28 1964
    return a + ab_dir * dot / ab_len_sqr;
1193b4be Poul Sander 2025-02-03 21:28 1965
}
1193b4be Poul Sander 2025-02-03 21:28 1966
1193b4be Poul Sander 2025-02-03 21:28 1967
bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1193b4be Poul Sander 2025-02-03 21:28 1968
{
1193b4be Poul Sander 2025-02-03 21:28 1969
    bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 1970
    bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 1971
    bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 1972
    return ((b1 == b2) && (b2 == b3));
1193b4be Poul Sander 2025-02-03 21:28 1973
}
1193b4be Poul Sander 2025-02-03 21:28 1974
1193b4be Poul Sander 2025-02-03 21:28 1975
void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1193b4be Poul Sander 2025-02-03 21:28 1976
{
1193b4be Poul Sander 2025-02-03 21:28 1977
    ImVec2 v0 = b - a;
1193b4be Poul Sander 2025-02-03 21:28 1978
    ImVec2 v1 = c - a;
1193b4be Poul Sander 2025-02-03 21:28 1979
    ImVec2 v2 = p - a;
1193b4be Poul Sander 2025-02-03 21:28 1980
    const float denom = v0.x * v1.y - v1.x * v0.y;
1193b4be Poul Sander 2025-02-03 21:28 1981
    out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1193b4be Poul Sander 2025-02-03 21:28 1982
    out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1193b4be Poul Sander 2025-02-03 21:28 1983
    out_u = 1.0f - out_v - out_w;
1193b4be Poul Sander 2025-02-03 21:28 1984
}
1193b4be Poul Sander 2025-02-03 21:28 1985
1193b4be Poul Sander 2025-02-03 21:28 1986
ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1193b4be Poul Sander 2025-02-03 21:28 1987
{
1193b4be Poul Sander 2025-02-03 21:28 1988
    ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1193b4be Poul Sander 2025-02-03 21:28 1989
    ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1193b4be Poul Sander 2025-02-03 21:28 1990
    ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1193b4be Poul Sander 2025-02-03 21:28 1991
    float dist2_ab = ImLengthSqr(p - proj_ab);
1193b4be Poul Sander 2025-02-03 21:28 1992
    float dist2_bc = ImLengthSqr(p - proj_bc);
1193b4be Poul Sander 2025-02-03 21:28 1993
    float dist2_ca = ImLengthSqr(p - proj_ca);
1193b4be Poul Sander 2025-02-03 21:28 1994
    float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1193b4be Poul Sander 2025-02-03 21:28 1995
    if (m == dist2_ab)
1193b4be Poul Sander 2025-02-03 21:28 1996
        return proj_ab;
1193b4be Poul Sander 2025-02-03 21:28 1997
    if (m == dist2_bc)
1193b4be Poul Sander 2025-02-03 21:28 1998
        return proj_bc;
1193b4be Poul Sander 2025-02-03 21:28 1999
    return proj_ca;
1193b4be Poul Sander 2025-02-03 21:28 2000
}
1193b4be Poul Sander 2025-02-03 21:28 2001
1193b4be Poul Sander 2025-02-03 21:28 2002
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2003
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1193b4be Poul Sander 2025-02-03 21:28 2004
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2005
1193b4be Poul Sander 2025-02-03 21:28 2006
// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
1193b4be Poul Sander 2025-02-03 21:28 2007
int ImStricmp(const char* str1, const char* str2)
1193b4be Poul Sander 2025-02-03 21:28 2008
{
1193b4be Poul Sander 2025-02-03 21:28 2009
    int d;
1193b4be Poul Sander 2025-02-03 21:28 2010
    while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
1193b4be Poul Sander 2025-02-03 21:28 2011
    return d;
1193b4be Poul Sander 2025-02-03 21:28 2012
}
1193b4be Poul Sander 2025-02-03 21:28 2013
1193b4be Poul Sander 2025-02-03 21:28 2014
int ImStrnicmp(const char* str1, const char* str2, size_t count)
1193b4be Poul Sander 2025-02-03 21:28 2015
{
1193b4be Poul Sander 2025-02-03 21:28 2016
    int d = 0;
1193b4be Poul Sander 2025-02-03 21:28 2017
    while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1193b4be Poul Sander 2025-02-03 21:28 2018
    return d;
1193b4be Poul Sander 2025-02-03 21:28 2019
}
1193b4be Poul Sander 2025-02-03 21:28 2020
1193b4be Poul Sander 2025-02-03 21:28 2021
void ImStrncpy(char* dst, const char* src, size_t count)
1193b4be Poul Sander 2025-02-03 21:28 2022
{
1193b4be Poul Sander 2025-02-03 21:28 2023
    if (count < 1)
1193b4be Poul Sander 2025-02-03 21:28 2024
        return;
1193b4be Poul Sander 2025-02-03 21:28 2025
    if (count > 1)
1193b4be Poul Sander 2025-02-03 21:28 2026
        strncpy(dst, src, count - 1);
1193b4be Poul Sander 2025-02-03 21:28 2027
    dst[count - 1] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2028
}
1193b4be Poul Sander 2025-02-03 21:28 2029
1193b4be Poul Sander 2025-02-03 21:28 2030
char* ImStrdup(const char* str)
1193b4be Poul Sander 2025-02-03 21:28 2031
{
1193b4be Poul Sander 2025-02-03 21:28 2032
    size_t len = strlen(str);
1193b4be Poul Sander 2025-02-03 21:28 2033
    void* buf = IM_ALLOC(len + 1);
1193b4be Poul Sander 2025-02-03 21:28 2034
    return (char*)memcpy(buf, (const void*)str, len + 1);
1193b4be Poul Sander 2025-02-03 21:28 2035
}
1193b4be Poul Sander 2025-02-03 21:28 2036
1193b4be Poul Sander 2025-02-03 21:28 2037
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1193b4be Poul Sander 2025-02-03 21:28 2038
{
1193b4be Poul Sander 2025-02-03 21:28 2039
    size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1193b4be Poul Sander 2025-02-03 21:28 2040
    size_t src_size = strlen(src) + 1;
1193b4be Poul Sander 2025-02-03 21:28 2041
    if (dst_buf_size < src_size)
1193b4be Poul Sander 2025-02-03 21:28 2042
    {
1193b4be Poul Sander 2025-02-03 21:28 2043
        IM_FREE(dst);
1193b4be Poul Sander 2025-02-03 21:28 2044
        dst = (char*)IM_ALLOC(src_size);
1193b4be Poul Sander 2025-02-03 21:28 2045
        if (p_dst_size)
1193b4be Poul Sander 2025-02-03 21:28 2046
            *p_dst_size = src_size;
1193b4be Poul Sander 2025-02-03 21:28 2047
    }
1193b4be Poul Sander 2025-02-03 21:28 2048
    return (char*)memcpy(dst, (const void*)src, src_size);
1193b4be Poul Sander 2025-02-03 21:28 2049
}
1193b4be Poul Sander 2025-02-03 21:28 2050
1193b4be Poul Sander 2025-02-03 21:28 2051
const char* ImStrchrRange(const char* str, const char* str_end, char c)
1193b4be Poul Sander 2025-02-03 21:28 2052
{
1193b4be Poul Sander 2025-02-03 21:28 2053
    const char* p = (const char*)memchr(str, (int)c, str_end - str);
1193b4be Poul Sander 2025-02-03 21:28 2054
    return p;
1193b4be Poul Sander 2025-02-03 21:28 2055
}
1193b4be Poul Sander 2025-02-03 21:28 2056
1193b4be Poul Sander 2025-02-03 21:28 2057
int ImStrlenW(const ImWchar* str)
1193b4be Poul Sander 2025-02-03 21:28 2058
{
1193b4be Poul Sander 2025-02-03 21:28 2059
    //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
1193b4be Poul Sander 2025-02-03 21:28 2060
    int n = 0;
1193b4be Poul Sander 2025-02-03 21:28 2061
    while (*str++) n++;
1193b4be Poul Sander 2025-02-03 21:28 2062
    return n;
1193b4be Poul Sander 2025-02-03 21:28 2063
}
1193b4be Poul Sander 2025-02-03 21:28 2064
1193b4be Poul Sander 2025-02-03 21:28 2065
// Find end-of-line. Return pointer will point to either first \n, either str_end.
1193b4be Poul Sander 2025-02-03 21:28 2066
const char* ImStreolRange(const char* str, const char* str_end)
1193b4be Poul Sander 2025-02-03 21:28 2067
{
1193b4be Poul Sander 2025-02-03 21:28 2068
    const char* p = (const char*)memchr(str, '\n', str_end - str);
1193b4be Poul Sander 2025-02-03 21:28 2069
    return p ? p : str_end;
1193b4be Poul Sander 2025-02-03 21:28 2070
}
1193b4be Poul Sander 2025-02-03 21:28 2071
1193b4be Poul Sander 2025-02-03 21:28 2072
const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
1193b4be Poul Sander 2025-02-03 21:28 2073
{
1193b4be Poul Sander 2025-02-03 21:28 2074
    while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1193b4be Poul Sander 2025-02-03 21:28 2075
        buf_mid_line--;
1193b4be Poul Sander 2025-02-03 21:28 2076
    return buf_mid_line;
1193b4be Poul Sander 2025-02-03 21:28 2077
}
1193b4be Poul Sander 2025-02-03 21:28 2078
1193b4be Poul Sander 2025-02-03 21:28 2079
const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1193b4be Poul Sander 2025-02-03 21:28 2080
{
1193b4be Poul Sander 2025-02-03 21:28 2081
    if (!needle_end)
1193b4be Poul Sander 2025-02-03 21:28 2082
        needle_end = needle + strlen(needle);
1193b4be Poul Sander 2025-02-03 21:28 2083
1193b4be Poul Sander 2025-02-03 21:28 2084
    const char un0 = (char)ImToUpper(*needle);
1193b4be Poul Sander 2025-02-03 21:28 2085
    while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1193b4be Poul Sander 2025-02-03 21:28 2086
    {
1193b4be Poul Sander 2025-02-03 21:28 2087
        if (ImToUpper(*haystack) == un0)
1193b4be Poul Sander 2025-02-03 21:28 2088
        {
1193b4be Poul Sander 2025-02-03 21:28 2089
            const char* b = needle + 1;
1193b4be Poul Sander 2025-02-03 21:28 2090
            for (const char* a = haystack + 1; b < needle_end; a++, b++)
1193b4be Poul Sander 2025-02-03 21:28 2091
                if (ImToUpper(*a) != ImToUpper(*b))
1193b4be Poul Sander 2025-02-03 21:28 2092
                    break;
1193b4be Poul Sander 2025-02-03 21:28 2093
            if (b == needle_end)
1193b4be Poul Sander 2025-02-03 21:28 2094
                return haystack;
1193b4be Poul Sander 2025-02-03 21:28 2095
        }
1193b4be Poul Sander 2025-02-03 21:28 2096
        haystack++;
1193b4be Poul Sander 2025-02-03 21:28 2097
    }
1193b4be Poul Sander 2025-02-03 21:28 2098
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2099
}
1193b4be Poul Sander 2025-02-03 21:28 2100
1193b4be Poul Sander 2025-02-03 21:28 2101
// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
1193b4be Poul Sander 2025-02-03 21:28 2102
void ImStrTrimBlanks(char* buf)
1193b4be Poul Sander 2025-02-03 21:28 2103
{
1193b4be Poul Sander 2025-02-03 21:28 2104
    char* p = buf;
1193b4be Poul Sander 2025-02-03 21:28 2105
    while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
1193b4be Poul Sander 2025-02-03 21:28 2106
        p++;
1193b4be Poul Sander 2025-02-03 21:28 2107
    char* p_start = p;
1193b4be Poul Sander 2025-02-03 21:28 2108
    while (*p != 0)                         // Find end of string
1193b4be Poul Sander 2025-02-03 21:28 2109
        p++;
1193b4be Poul Sander 2025-02-03 21:28 2110
    while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
1193b4be Poul Sander 2025-02-03 21:28 2111
        p--;
1193b4be Poul Sander 2025-02-03 21:28 2112
    if (p_start != buf)                     // Copy memory if we had leading blanks
1193b4be Poul Sander 2025-02-03 21:28 2113
        memmove(buf, p_start, p - p_start);
1193b4be Poul Sander 2025-02-03 21:28 2114
    buf[p - p_start] = 0;                   // Zero terminate
1193b4be Poul Sander 2025-02-03 21:28 2115
}
1193b4be Poul Sander 2025-02-03 21:28 2116
1193b4be Poul Sander 2025-02-03 21:28 2117
const char* ImStrSkipBlank(const char* str)
1193b4be Poul Sander 2025-02-03 21:28 2118
{
1193b4be Poul Sander 2025-02-03 21:28 2119
    while (str[0] == ' ' || str[0] == '\t')
1193b4be Poul Sander 2025-02-03 21:28 2120
        str++;
1193b4be Poul Sander 2025-02-03 21:28 2121
    return str;
1193b4be Poul Sander 2025-02-03 21:28 2122
}
1193b4be Poul Sander 2025-02-03 21:28 2123
1193b4be Poul Sander 2025-02-03 21:28 2124
// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1193b4be Poul Sander 2025-02-03 21:28 2125
// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
1193b4be Poul Sander 2025-02-03 21:28 2126
// B) When buf==NULL vsnprintf() will return the output size.
1193b4be Poul Sander 2025-02-03 21:28 2127
#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 2128
1193b4be Poul Sander 2025-02-03 21:28 2129
// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1193b4be Poul Sander 2025-02-03 21:28 2130
// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 2131
// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1193b4be Poul Sander 2025-02-03 21:28 2132
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1193b4be Poul Sander 2025-02-03 21:28 2133
#ifdef IMGUI_USE_STB_SPRINTF
1193b4be Poul Sander 2025-02-03 21:28 2134
#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
1193b4be Poul Sander 2025-02-03 21:28 2135
#define STB_SPRINTF_IMPLEMENTATION
1193b4be Poul Sander 2025-02-03 21:28 2136
#endif
1193b4be Poul Sander 2025-02-03 21:28 2137
#ifdef IMGUI_STB_SPRINTF_FILENAME
1193b4be Poul Sander 2025-02-03 21:28 2138
#include IMGUI_STB_SPRINTF_FILENAME
1193b4be Poul Sander 2025-02-03 21:28 2139
#else
1193b4be Poul Sander 2025-02-03 21:28 2140
#include "stb_sprintf.h"
1193b4be Poul Sander 2025-02-03 21:28 2141
#endif
1193b4be Poul Sander 2025-02-03 21:28 2142
#endif // #ifdef IMGUI_USE_STB_SPRINTF
1193b4be Poul Sander 2025-02-03 21:28 2143
1193b4be Poul Sander 2025-02-03 21:28 2144
#if defined(_MSC_VER) && !defined(vsnprintf)
1193b4be Poul Sander 2025-02-03 21:28 2145
#define vsnprintf _vsnprintf
1193b4be Poul Sander 2025-02-03 21:28 2146
#endif
1193b4be Poul Sander 2025-02-03 21:28 2147
1193b4be Poul Sander 2025-02-03 21:28 2148
int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 2149
{
1193b4be Poul Sander 2025-02-03 21:28 2150
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 2151
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 2152
#ifdef IMGUI_USE_STB_SPRINTF
1193b4be Poul Sander 2025-02-03 21:28 2153
    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2154
#else
1193b4be Poul Sander 2025-02-03 21:28 2155
    int w = vsnprintf(buf, buf_size, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2156
#endif
1193b4be Poul Sander 2025-02-03 21:28 2157
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 2158
    if (buf == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2159
        return w;
1193b4be Poul Sander 2025-02-03 21:28 2160
    if (w == -1 || w >= (int)buf_size)
1193b4be Poul Sander 2025-02-03 21:28 2161
        w = (int)buf_size - 1;
1193b4be Poul Sander 2025-02-03 21:28 2162
    buf[w] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2163
    return w;
1193b4be Poul Sander 2025-02-03 21:28 2164
}
1193b4be Poul Sander 2025-02-03 21:28 2165
1193b4be Poul Sander 2025-02-03 21:28 2166
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 2167
{
1193b4be Poul Sander 2025-02-03 21:28 2168
#ifdef IMGUI_USE_STB_SPRINTF
1193b4be Poul Sander 2025-02-03 21:28 2169
    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2170
#else
1193b4be Poul Sander 2025-02-03 21:28 2171
    int w = vsnprintf(buf, buf_size, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2172
#endif
1193b4be Poul Sander 2025-02-03 21:28 2173
    if (buf == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2174
        return w;
1193b4be Poul Sander 2025-02-03 21:28 2175
    if (w == -1 || w >= (int)buf_size)
1193b4be Poul Sander 2025-02-03 21:28 2176
        w = (int)buf_size - 1;
1193b4be Poul Sander 2025-02-03 21:28 2177
    buf[w] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2178
    return w;
1193b4be Poul Sander 2025-02-03 21:28 2179
}
1193b4be Poul Sander 2025-02-03 21:28 2180
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 2181
1193b4be Poul Sander 2025-02-03 21:28 2182
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 2183
{
1193b4be Poul Sander 2025-02-03 21:28 2184
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 2185
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 2186
    ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2187
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 2188
}
1193b4be Poul Sander 2025-02-03 21:28 2189
1193b4be Poul Sander 2025-02-03 21:28 2190
// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
1193b4be Poul Sander 2025-02-03 21:28 2191
// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
1193b4be Poul Sander 2025-02-03 21:28 2192
//  ImGuiTempBufferToken token;
1193b4be Poul Sander 2025-02-03 21:28 2193
//  ImFormatStringToTempBuffer(token, ...);
1193b4be Poul Sander 2025-02-03 21:28 2194
void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 2195
{
1193b4be Poul Sander 2025-02-03 21:28 2196
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 2197
    if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
1193b4be Poul Sander 2025-02-03 21:28 2198
    {
1193b4be Poul Sander 2025-02-03 21:28 2199
        const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
1193b4be Poul Sander 2025-02-03 21:28 2200
        if (buf == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2201
            buf = "(null)";
1193b4be Poul Sander 2025-02-03 21:28 2202
        *out_buf = buf;
1193b4be Poul Sander 2025-02-03 21:28 2203
        if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
1193b4be Poul Sander 2025-02-03 21:28 2204
    }
1193b4be Poul Sander 2025-02-03 21:28 2205
    else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
1193b4be Poul Sander 2025-02-03 21:28 2206
    {
1193b4be Poul Sander 2025-02-03 21:28 2207
        int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
1193b4be Poul Sander 2025-02-03 21:28 2208
        const char* buf = va_arg(args, const char*);
1193b4be Poul Sander 2025-02-03 21:28 2209
        if (buf == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2210
        {
1193b4be Poul Sander 2025-02-03 21:28 2211
            buf = "(null)";
1193b4be Poul Sander 2025-02-03 21:28 2212
            buf_len = ImMin(buf_len, 6);
1193b4be Poul Sander 2025-02-03 21:28 2213
        }
1193b4be Poul Sander 2025-02-03 21:28 2214
        *out_buf = buf;
1193b4be Poul Sander 2025-02-03 21:28 2215
        *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
1193b4be Poul Sander 2025-02-03 21:28 2216
    }
1193b4be Poul Sander 2025-02-03 21:28 2217
    else
1193b4be Poul Sander 2025-02-03 21:28 2218
    {
1193b4be Poul Sander 2025-02-03 21:28 2219
        int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2220
        *out_buf = g.TempBuffer.Data;
1193b4be Poul Sander 2025-02-03 21:28 2221
        if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
1193b4be Poul Sander 2025-02-03 21:28 2222
    }
1193b4be Poul Sander 2025-02-03 21:28 2223
}
1193b4be Poul Sander 2025-02-03 21:28 2224
1193b4be Poul Sander 2025-02-03 21:28 2225
// CRC32 needs a 1KB lookup table (not cache friendly)
1193b4be Poul Sander 2025-02-03 21:28 2226
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1193b4be Poul Sander 2025-02-03 21:28 2227
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1193b4be Poul Sander 2025-02-03 21:28 2228
static const ImU32 GCrc32LookupTable[256] =
1193b4be Poul Sander 2025-02-03 21:28 2229
{
1193b4be Poul Sander 2025-02-03 21:28 2230
    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1193b4be Poul Sander 2025-02-03 21:28 2231
    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1193b4be Poul Sander 2025-02-03 21:28 2232
    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1193b4be Poul Sander 2025-02-03 21:28 2233
    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1193b4be Poul Sander 2025-02-03 21:28 2234
    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1193b4be Poul Sander 2025-02-03 21:28 2235
    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1193b4be Poul Sander 2025-02-03 21:28 2236
    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1193b4be Poul Sander 2025-02-03 21:28 2237
    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1193b4be Poul Sander 2025-02-03 21:28 2238
    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1193b4be Poul Sander 2025-02-03 21:28 2239
    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1193b4be Poul Sander 2025-02-03 21:28 2240
    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1193b4be Poul Sander 2025-02-03 21:28 2241
    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1193b4be Poul Sander 2025-02-03 21:28 2242
    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1193b4be Poul Sander 2025-02-03 21:28 2243
    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1193b4be Poul Sander 2025-02-03 21:28 2244
    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1193b4be Poul Sander 2025-02-03 21:28 2245
    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1193b4be Poul Sander 2025-02-03 21:28 2246
};
1193b4be Poul Sander 2025-02-03 21:28 2247
1193b4be Poul Sander 2025-02-03 21:28 2248
// Known size hash
1193b4be Poul Sander 2025-02-03 21:28 2249
// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1193b4be Poul Sander 2025-02-03 21:28 2250
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1193b4be Poul Sander 2025-02-03 21:28 2251
ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
1193b4be Poul Sander 2025-02-03 21:28 2252
{
1193b4be Poul Sander 2025-02-03 21:28 2253
    ImU32 crc = ~seed;
1193b4be Poul Sander 2025-02-03 21:28 2254
    const unsigned char* data = (const unsigned char*)data_p;
1193b4be Poul Sander 2025-02-03 21:28 2255
    const ImU32* crc32_lut = GCrc32LookupTable;
1193b4be Poul Sander 2025-02-03 21:28 2256
    while (data_size-- != 0)
1193b4be Poul Sander 2025-02-03 21:28 2257
        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1193b4be Poul Sander 2025-02-03 21:28 2258
    return ~crc;
1193b4be Poul Sander 2025-02-03 21:28 2259
}
1193b4be Poul Sander 2025-02-03 21:28 2260
1193b4be Poul Sander 2025-02-03 21:28 2261
// Zero-terminated string hash, with support for ### to reset back to seed value
1193b4be Poul Sander 2025-02-03 21:28 2262
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1193b4be Poul Sander 2025-02-03 21:28 2263
// Because this syntax is rarely used we are optimizing for the common case.
1193b4be Poul Sander 2025-02-03 21:28 2264
// - If we reach ### in the string we discard the hash so far and reset to the seed.
1193b4be Poul Sander 2025-02-03 21:28 2265
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1193b4be Poul Sander 2025-02-03 21:28 2266
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1193b4be Poul Sander 2025-02-03 21:28 2267
ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
1193b4be Poul Sander 2025-02-03 21:28 2268
{
1193b4be Poul Sander 2025-02-03 21:28 2269
    seed = ~seed;
1193b4be Poul Sander 2025-02-03 21:28 2270
    ImU32 crc = seed;
1193b4be Poul Sander 2025-02-03 21:28 2271
    const unsigned char* data = (const unsigned char*)data_p;
1193b4be Poul Sander 2025-02-03 21:28 2272
    const ImU32* crc32_lut = GCrc32LookupTable;
1193b4be Poul Sander 2025-02-03 21:28 2273
    if (data_size != 0)
1193b4be Poul Sander 2025-02-03 21:28 2274
    {
1193b4be Poul Sander 2025-02-03 21:28 2275
        while (data_size-- != 0)
1193b4be Poul Sander 2025-02-03 21:28 2276
        {
1193b4be Poul Sander 2025-02-03 21:28 2277
            unsigned char c = *data++;
1193b4be Poul Sander 2025-02-03 21:28 2278
            if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1193b4be Poul Sander 2025-02-03 21:28 2279
                crc = seed;
1193b4be Poul Sander 2025-02-03 21:28 2280
            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1193b4be Poul Sander 2025-02-03 21:28 2281
        }
1193b4be Poul Sander 2025-02-03 21:28 2282
    }
1193b4be Poul Sander 2025-02-03 21:28 2283
    else
1193b4be Poul Sander 2025-02-03 21:28 2284
    {
1193b4be Poul Sander 2025-02-03 21:28 2285
        while (unsigned char c = *data++)
1193b4be Poul Sander 2025-02-03 21:28 2286
        {
1193b4be Poul Sander 2025-02-03 21:28 2287
            if (c == '#' && data[0] == '#' && data[1] == '#')
1193b4be Poul Sander 2025-02-03 21:28 2288
                crc = seed;
1193b4be Poul Sander 2025-02-03 21:28 2289
            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1193b4be Poul Sander 2025-02-03 21:28 2290
        }
1193b4be Poul Sander 2025-02-03 21:28 2291
    }
1193b4be Poul Sander 2025-02-03 21:28 2292
    return ~crc;
1193b4be Poul Sander 2025-02-03 21:28 2293
}
1193b4be Poul Sander 2025-02-03 21:28 2294
1193b4be Poul Sander 2025-02-03 21:28 2295
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2296
// [SECTION] MISC HELPERS/UTILITIES (File functions)
1193b4be Poul Sander 2025-02-03 21:28 2297
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2298
1193b4be Poul Sander 2025-02-03 21:28 2299
// Default file functions
1193b4be Poul Sander 2025-02-03 21:28 2300
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 2301
1193b4be Poul Sander 2025-02-03 21:28 2302
ImFileHandle ImFileOpen(const char* filename, const char* mode)
1193b4be Poul Sander 2025-02-03 21:28 2303
{
1193b4be Poul Sander 2025-02-03 21:28 2304
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1193b4be Poul Sander 2025-02-03 21:28 2305
    // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1193b4be Poul Sander 2025-02-03 21:28 2306
    // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1193b4be Poul Sander 2025-02-03 21:28 2307
    const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1193b4be Poul Sander 2025-02-03 21:28 2308
    const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1193b4be Poul Sander 2025-02-03 21:28 2309
1193b4be Poul Sander 2025-02-03 21:28 2310
    // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
1193b4be Poul Sander 2025-02-03 21:28 2311
    // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
1193b4be Poul Sander 2025-02-03 21:28 2312
    wchar_t local_temp_stack[FILENAME_MAX];
1193b4be Poul Sander 2025-02-03 21:28 2313
    ImVector<wchar_t> local_temp_heap;
1193b4be Poul Sander 2025-02-03 21:28 2314
    if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
1193b4be Poul Sander 2025-02-03 21:28 2315
        local_temp_heap.resize(filename_wsize + mode_wsize);
1193b4be Poul Sander 2025-02-03 21:28 2316
    wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
1193b4be Poul Sander 2025-02-03 21:28 2317
    wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
1193b4be Poul Sander 2025-02-03 21:28 2318
    ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
1193b4be Poul Sander 2025-02-03 21:28 2319
    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
1193b4be Poul Sander 2025-02-03 21:28 2320
    return ::_wfopen(filename_wbuf, mode_wbuf);
1193b4be Poul Sander 2025-02-03 21:28 2321
#else
1193b4be Poul Sander 2025-02-03 21:28 2322
    return fopen(filename, mode);
1193b4be Poul Sander 2025-02-03 21:28 2323
#endif
1193b4be Poul Sander 2025-02-03 21:28 2324
}
1193b4be Poul Sander 2025-02-03 21:28 2325
1193b4be Poul Sander 2025-02-03 21:28 2326
// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
1193b4be Poul Sander 2025-02-03 21:28 2327
bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
1193b4be Poul Sander 2025-02-03 21:28 2328
ImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
1193b4be Poul Sander 2025-02-03 21:28 2329
ImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }
1193b4be Poul Sander 2025-02-03 21:28 2330
ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
1193b4be Poul Sander 2025-02-03 21:28 2331
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 2332
1193b4be Poul Sander 2025-02-03 21:28 2333
// Helper: Load file content into memory
1193b4be Poul Sander 2025-02-03 21:28 2334
// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1193b4be Poul Sander 2025-02-03 21:28 2335
// This can't really be used with "rt" because fseek size won't match read size.
1193b4be Poul Sander 2025-02-03 21:28 2336
void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1193b4be Poul Sander 2025-02-03 21:28 2337
{
1193b4be Poul Sander 2025-02-03 21:28 2338
    IM_ASSERT(filename && mode);
1193b4be Poul Sander 2025-02-03 21:28 2339
    if (out_file_size)
1193b4be Poul Sander 2025-02-03 21:28 2340
        *out_file_size = 0;
1193b4be Poul Sander 2025-02-03 21:28 2341
1193b4be Poul Sander 2025-02-03 21:28 2342
    ImFileHandle f;
1193b4be Poul Sander 2025-02-03 21:28 2343
    if ((f = ImFileOpen(filename, mode)) == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2344
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2345
1193b4be Poul Sander 2025-02-03 21:28 2346
    size_t file_size = (size_t)ImFileGetSize(f);
1193b4be Poul Sander 2025-02-03 21:28 2347
    if (file_size == (size_t)-1)
1193b4be Poul Sander 2025-02-03 21:28 2348
    {
1193b4be Poul Sander 2025-02-03 21:28 2349
        ImFileClose(f);
1193b4be Poul Sander 2025-02-03 21:28 2350
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2351
    }
1193b4be Poul Sander 2025-02-03 21:28 2352
1193b4be Poul Sander 2025-02-03 21:28 2353
    void* file_data = IM_ALLOC(file_size + padding_bytes);
1193b4be Poul Sander 2025-02-03 21:28 2354
    if (file_data == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2355
    {
1193b4be Poul Sander 2025-02-03 21:28 2356
        ImFileClose(f);
1193b4be Poul Sander 2025-02-03 21:28 2357
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2358
    }
1193b4be Poul Sander 2025-02-03 21:28 2359
    if (ImFileRead(file_data, 1, file_size, f) != file_size)
1193b4be Poul Sander 2025-02-03 21:28 2360
    {
1193b4be Poul Sander 2025-02-03 21:28 2361
        ImFileClose(f);
1193b4be Poul Sander 2025-02-03 21:28 2362
        IM_FREE(file_data);
1193b4be Poul Sander 2025-02-03 21:28 2363
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2364
    }
1193b4be Poul Sander 2025-02-03 21:28 2365
    if (padding_bytes > 0)
1193b4be Poul Sander 2025-02-03 21:28 2366
        memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1193b4be Poul Sander 2025-02-03 21:28 2367
1193b4be Poul Sander 2025-02-03 21:28 2368
    ImFileClose(f);
1193b4be Poul Sander 2025-02-03 21:28 2369
    if (out_file_size)
1193b4be Poul Sander 2025-02-03 21:28 2370
        *out_file_size = file_size;
1193b4be Poul Sander 2025-02-03 21:28 2371
1193b4be Poul Sander 2025-02-03 21:28 2372
    return file_data;
1193b4be Poul Sander 2025-02-03 21:28 2373
}
1193b4be Poul Sander 2025-02-03 21:28 2374
1193b4be Poul Sander 2025-02-03 21:28 2375
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2376
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1193b4be Poul Sander 2025-02-03 21:28 2377
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2378
1193b4be Poul Sander 2025-02-03 21:28 2379
IM_MSVC_RUNTIME_CHECKS_OFF
1193b4be Poul Sander 2025-02-03 21:28 2380
1193b4be Poul Sander 2025-02-03 21:28 2381
// Convert UTF-8 to 32-bit character, process single character input.
1193b4be Poul Sander 2025-02-03 21:28 2382
// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1193b4be Poul Sander 2025-02-03 21:28 2383
// We handle UTF-8 decoding error by skipping forward.
1193b4be Poul Sander 2025-02-03 21:28 2384
int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2385
{
1193b4be Poul Sander 2025-02-03 21:28 2386
    static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
1193b4be Poul Sander 2025-02-03 21:28 2387
    static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1193b4be Poul Sander 2025-02-03 21:28 2388
    static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1193b4be Poul Sander 2025-02-03 21:28 2389
    static const int shiftc[] = { 0, 18, 12, 6, 0 };
1193b4be Poul Sander 2025-02-03 21:28 2390
    static const int shifte[] = { 0, 6, 4, 2, 0 };
1193b4be Poul Sander 2025-02-03 21:28 2391
    int len = lengths[*(const unsigned char*)in_text >> 3];
1193b4be Poul Sander 2025-02-03 21:28 2392
    int wanted = len + (len ? 0 : 1);
1193b4be Poul Sander 2025-02-03 21:28 2393
1193b4be Poul Sander 2025-02-03 21:28 2394
    if (in_text_end == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2395
        in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1193b4be Poul Sander 2025-02-03 21:28 2396
1193b4be Poul Sander 2025-02-03 21:28 2397
    // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1193b4be Poul Sander 2025-02-03 21:28 2398
    // so it is fast even with excessive branching.
1193b4be Poul Sander 2025-02-03 21:28 2399
    unsigned char s[4];
1193b4be Poul Sander 2025-02-03 21:28 2400
    s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1193b4be Poul Sander 2025-02-03 21:28 2401
    s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1193b4be Poul Sander 2025-02-03 21:28 2402
    s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1193b4be Poul Sander 2025-02-03 21:28 2403
    s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1193b4be Poul Sander 2025-02-03 21:28 2404
1193b4be Poul Sander 2025-02-03 21:28 2405
    // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1193b4be Poul Sander 2025-02-03 21:28 2406
    *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
1193b4be Poul Sander 2025-02-03 21:28 2407
    *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1193b4be Poul Sander 2025-02-03 21:28 2408
    *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
1193b4be Poul Sander 2025-02-03 21:28 2409
    *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
1193b4be Poul Sander 2025-02-03 21:28 2410
    *out_char >>= shiftc[len];
1193b4be Poul Sander 2025-02-03 21:28 2411
1193b4be Poul Sander 2025-02-03 21:28 2412
    // Accumulate the various error conditions.
1193b4be Poul Sander 2025-02-03 21:28 2413
    int e = 0;
1193b4be Poul Sander 2025-02-03 21:28 2414
    e  = (*out_char < mins[len]) << 6; // non-canonical encoding
1193b4be Poul Sander 2025-02-03 21:28 2415
    e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
1193b4be Poul Sander 2025-02-03 21:28 2416
    e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?
1193b4be Poul Sander 2025-02-03 21:28 2417
    e |= (s[1] & 0xc0) >> 2;
1193b4be Poul Sander 2025-02-03 21:28 2418
    e |= (s[2] & 0xc0) >> 4;
1193b4be Poul Sander 2025-02-03 21:28 2419
    e |= (s[3]       ) >> 6;
1193b4be Poul Sander 2025-02-03 21:28 2420
    e ^= 0x2a; // top two bits of each tail byte correct?
1193b4be Poul Sander 2025-02-03 21:28 2421
    e >>= shifte[len];
1193b4be Poul Sander 2025-02-03 21:28 2422
1193b4be Poul Sander 2025-02-03 21:28 2423
    if (e)
1193b4be Poul Sander 2025-02-03 21:28 2424
    {
1193b4be Poul Sander 2025-02-03 21:28 2425
        // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1193b4be Poul Sander 2025-02-03 21:28 2426
        // One byte is consumed in case of invalid first byte of in_text.
1193b4be Poul Sander 2025-02-03 21:28 2427
        // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1193b4be Poul Sander 2025-02-03 21:28 2428
        // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1193b4be Poul Sander 2025-02-03 21:28 2429
        wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1193b4be Poul Sander 2025-02-03 21:28 2430
        *out_char = IM_UNICODE_CODEPOINT_INVALID;
1193b4be Poul Sander 2025-02-03 21:28 2431
    }
1193b4be Poul Sander 2025-02-03 21:28 2432
1193b4be Poul Sander 2025-02-03 21:28 2433
    return wanted;
1193b4be Poul Sander 2025-02-03 21:28 2434
}
1193b4be Poul Sander 2025-02-03 21:28 2435
1193b4be Poul Sander 2025-02-03 21:28 2436
int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1193b4be Poul Sander 2025-02-03 21:28 2437
{
1193b4be Poul Sander 2025-02-03 21:28 2438
    ImWchar* buf_out = buf;
1193b4be Poul Sander 2025-02-03 21:28 2439
    ImWchar* buf_end = buf + buf_size;
1193b4be Poul Sander 2025-02-03 21:28 2440
    while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1193b4be Poul Sander 2025-02-03 21:28 2441
    {
1193b4be Poul Sander 2025-02-03 21:28 2442
        unsigned int c;
1193b4be Poul Sander 2025-02-03 21:28 2443
        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1193b4be Poul Sander 2025-02-03 21:28 2444
        *buf_out++ = (ImWchar)c;
1193b4be Poul Sander 2025-02-03 21:28 2445
    }
1193b4be Poul Sander 2025-02-03 21:28 2446
    *buf_out = 0;
1193b4be Poul Sander 2025-02-03 21:28 2447
    if (in_text_remaining)
1193b4be Poul Sander 2025-02-03 21:28 2448
        *in_text_remaining = in_text;
1193b4be Poul Sander 2025-02-03 21:28 2449
    return (int)(buf_out - buf);
1193b4be Poul Sander 2025-02-03 21:28 2450
}
1193b4be Poul Sander 2025-02-03 21:28 2451
1193b4be Poul Sander 2025-02-03 21:28 2452
int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2453
{
1193b4be Poul Sander 2025-02-03 21:28 2454
    int char_count = 0;
1193b4be Poul Sander 2025-02-03 21:28 2455
    while ((!in_text_end || in_text < in_text_end) && *in_text)
1193b4be Poul Sander 2025-02-03 21:28 2456
    {
1193b4be Poul Sander 2025-02-03 21:28 2457
        unsigned int c;
1193b4be Poul Sander 2025-02-03 21:28 2458
        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1193b4be Poul Sander 2025-02-03 21:28 2459
        char_count++;
1193b4be Poul Sander 2025-02-03 21:28 2460
    }
1193b4be Poul Sander 2025-02-03 21:28 2461
    return char_count;
1193b4be Poul Sander 2025-02-03 21:28 2462
}
1193b4be Poul Sander 2025-02-03 21:28 2463
1193b4be Poul Sander 2025-02-03 21:28 2464
// Based on stb_to_utf8() from github.com/nothings/stb/
1193b4be Poul Sander 2025-02-03 21:28 2465
static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
1193b4be Poul Sander 2025-02-03 21:28 2466
{
1193b4be Poul Sander 2025-02-03 21:28 2467
    if (c < 0x80)
1193b4be Poul Sander 2025-02-03 21:28 2468
    {
1193b4be Poul Sander 2025-02-03 21:28 2469
        buf[0] = (char)c;
1193b4be Poul Sander 2025-02-03 21:28 2470
        return 1;
1193b4be Poul Sander 2025-02-03 21:28 2471
    }
1193b4be Poul Sander 2025-02-03 21:28 2472
    if (c < 0x800)
1193b4be Poul Sander 2025-02-03 21:28 2473
    {
1193b4be Poul Sander 2025-02-03 21:28 2474
        if (buf_size < 2) return 0;
1193b4be Poul Sander 2025-02-03 21:28 2475
        buf[0] = (char)(0xc0 + (c >> 6));
1193b4be Poul Sander 2025-02-03 21:28 2476
        buf[1] = (char)(0x80 + (c & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2477
        return 2;
1193b4be Poul Sander 2025-02-03 21:28 2478
    }
1193b4be Poul Sander 2025-02-03 21:28 2479
    if (c < 0x10000)
1193b4be Poul Sander 2025-02-03 21:28 2480
    {
1193b4be Poul Sander 2025-02-03 21:28 2481
        if (buf_size < 3) return 0;
1193b4be Poul Sander 2025-02-03 21:28 2482
        buf[0] = (char)(0xe0 + (c >> 12));
1193b4be Poul Sander 2025-02-03 21:28 2483
        buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2484
        buf[2] = (char)(0x80 + ((c ) & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2485
        return 3;
1193b4be Poul Sander 2025-02-03 21:28 2486
    }
1193b4be Poul Sander 2025-02-03 21:28 2487
    if (c <= 0x10FFFF)
1193b4be Poul Sander 2025-02-03 21:28 2488
    {
1193b4be Poul Sander 2025-02-03 21:28 2489
        if (buf_size < 4) return 0;
1193b4be Poul Sander 2025-02-03 21:28 2490
        buf[0] = (char)(0xf0 + (c >> 18));
1193b4be Poul Sander 2025-02-03 21:28 2491
        buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2492
        buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2493
        buf[3] = (char)(0x80 + ((c ) & 0x3f));
1193b4be Poul Sander 2025-02-03 21:28 2494
        return 4;
1193b4be Poul Sander 2025-02-03 21:28 2495
    }
1193b4be Poul Sander 2025-02-03 21:28 2496
    // Invalid code point, the max unicode is 0x10FFFF
1193b4be Poul Sander 2025-02-03 21:28 2497
    return 0;
1193b4be Poul Sander 2025-02-03 21:28 2498
}
1193b4be Poul Sander 2025-02-03 21:28 2499
1193b4be Poul Sander 2025-02-03 21:28 2500
const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
1193b4be Poul Sander 2025-02-03 21:28 2501
{
1193b4be Poul Sander 2025-02-03 21:28 2502
    int count = ImTextCharToUtf8_inline(out_buf, 5, c);
1193b4be Poul Sander 2025-02-03 21:28 2503
    out_buf[count] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2504
    return out_buf;
1193b4be Poul Sander 2025-02-03 21:28 2505
}
1193b4be Poul Sander 2025-02-03 21:28 2506
1193b4be Poul Sander 2025-02-03 21:28 2507
// Not optimal but we very rarely use this function.
1193b4be Poul Sander 2025-02-03 21:28 2508
int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2509
{
1193b4be Poul Sander 2025-02-03 21:28 2510
    unsigned int unused = 0;
1193b4be Poul Sander 2025-02-03 21:28 2511
    return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1193b4be Poul Sander 2025-02-03 21:28 2512
}
1193b4be Poul Sander 2025-02-03 21:28 2513
1193b4be Poul Sander 2025-02-03 21:28 2514
static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1193b4be Poul Sander 2025-02-03 21:28 2515
{
1193b4be Poul Sander 2025-02-03 21:28 2516
    if (c < 0x80) return 1;
1193b4be Poul Sander 2025-02-03 21:28 2517
    if (c < 0x800) return 2;
1193b4be Poul Sander 2025-02-03 21:28 2518
    if (c < 0x10000) return 3;
1193b4be Poul Sander 2025-02-03 21:28 2519
    if (c <= 0x10FFFF) return 4;
1193b4be Poul Sander 2025-02-03 21:28 2520
    return 3;
1193b4be Poul Sander 2025-02-03 21:28 2521
}
1193b4be Poul Sander 2025-02-03 21:28 2522
1193b4be Poul Sander 2025-02-03 21:28 2523
int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2524
{
1193b4be Poul Sander 2025-02-03 21:28 2525
    char* buf_p = out_buf;
1193b4be Poul Sander 2025-02-03 21:28 2526
    const char* buf_end = out_buf + out_buf_size;
1193b4be Poul Sander 2025-02-03 21:28 2527
    while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1193b4be Poul Sander 2025-02-03 21:28 2528
    {
1193b4be Poul Sander 2025-02-03 21:28 2529
        unsigned int c = (unsigned int)(*in_text++);
1193b4be Poul Sander 2025-02-03 21:28 2530
        if (c < 0x80)
1193b4be Poul Sander 2025-02-03 21:28 2531
            *buf_p++ = (char)c;
1193b4be Poul Sander 2025-02-03 21:28 2532
        else
1193b4be Poul Sander 2025-02-03 21:28 2533
            buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
1193b4be Poul Sander 2025-02-03 21:28 2534
    }
1193b4be Poul Sander 2025-02-03 21:28 2535
    *buf_p = 0;
1193b4be Poul Sander 2025-02-03 21:28 2536
    return (int)(buf_p - out_buf);
1193b4be Poul Sander 2025-02-03 21:28 2537
}
1193b4be Poul Sander 2025-02-03 21:28 2538
1193b4be Poul Sander 2025-02-03 21:28 2539
int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2540
{
1193b4be Poul Sander 2025-02-03 21:28 2541
    int bytes_count = 0;
1193b4be Poul Sander 2025-02-03 21:28 2542
    while ((!in_text_end || in_text < in_text_end) && *in_text)
1193b4be Poul Sander 2025-02-03 21:28 2543
    {
1193b4be Poul Sander 2025-02-03 21:28 2544
        unsigned int c = (unsigned int)(*in_text++);
1193b4be Poul Sander 2025-02-03 21:28 2545
        if (c < 0x80)
1193b4be Poul Sander 2025-02-03 21:28 2546
            bytes_count++;
1193b4be Poul Sander 2025-02-03 21:28 2547
        else
1193b4be Poul Sander 2025-02-03 21:28 2548
            bytes_count += ImTextCountUtf8BytesFromChar(c);
1193b4be Poul Sander 2025-02-03 21:28 2549
    }
1193b4be Poul Sander 2025-02-03 21:28 2550
    return bytes_count;
1193b4be Poul Sander 2025-02-03 21:28 2551
}
1193b4be Poul Sander 2025-02-03 21:28 2552
1193b4be Poul Sander 2025-02-03 21:28 2553
const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
1193b4be Poul Sander 2025-02-03 21:28 2554
{
1193b4be Poul Sander 2025-02-03 21:28 2555
    while (in_text_curr > in_text_start)
1193b4be Poul Sander 2025-02-03 21:28 2556
    {
1193b4be Poul Sander 2025-02-03 21:28 2557
        in_text_curr--;
1193b4be Poul Sander 2025-02-03 21:28 2558
        if ((*in_text_curr & 0xC0) != 0x80)
1193b4be Poul Sander 2025-02-03 21:28 2559
            return in_text_curr;
1193b4be Poul Sander 2025-02-03 21:28 2560
    }
1193b4be Poul Sander 2025-02-03 21:28 2561
    return in_text_start;
1193b4be Poul Sander 2025-02-03 21:28 2562
}
1193b4be Poul Sander 2025-02-03 21:28 2563
1193b4be Poul Sander 2025-02-03 21:28 2564
int ImTextCountLines(const char* in_text, const char* in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2565
{
1193b4be Poul Sander 2025-02-03 21:28 2566
    if (in_text_end == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2567
        in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
1193b4be Poul Sander 2025-02-03 21:28 2568
    int count = 0;
1193b4be Poul Sander 2025-02-03 21:28 2569
    while (in_text < in_text_end)
1193b4be Poul Sander 2025-02-03 21:28 2570
    {
1193b4be Poul Sander 2025-02-03 21:28 2571
        const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text);
1193b4be Poul Sander 2025-02-03 21:28 2572
        in_text = line_end ? line_end + 1 : in_text_end;
1193b4be Poul Sander 2025-02-03 21:28 2573
        count++;
1193b4be Poul Sander 2025-02-03 21:28 2574
    }
1193b4be Poul Sander 2025-02-03 21:28 2575
    return count;
1193b4be Poul Sander 2025-02-03 21:28 2576
}
1193b4be Poul Sander 2025-02-03 21:28 2577
1193b4be Poul Sander 2025-02-03 21:28 2578
IM_MSVC_RUNTIME_CHECKS_RESTORE
1193b4be Poul Sander 2025-02-03 21:28 2579
1193b4be Poul Sander 2025-02-03 21:28 2580
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2581
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
1193b4be Poul Sander 2025-02-03 21:28 2582
// Note: The Convert functions are early design which are not consistent with other API.
1193b4be Poul Sander 2025-02-03 21:28 2583
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2584
1193b4be Poul Sander 2025-02-03 21:28 2585
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1193b4be Poul Sander 2025-02-03 21:28 2586
{
1193b4be Poul Sander 2025-02-03 21:28 2587
    float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1193b4be Poul Sander 2025-02-03 21:28 2588
    int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1193b4be Poul Sander 2025-02-03 21:28 2589
    int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1193b4be Poul Sander 2025-02-03 21:28 2590
    int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1193b4be Poul Sander 2025-02-03 21:28 2591
    return IM_COL32(r, g, b, 0xFF);
1193b4be Poul Sander 2025-02-03 21:28 2592
}
1193b4be Poul Sander 2025-02-03 21:28 2593
1193b4be Poul Sander 2025-02-03 21:28 2594
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1193b4be Poul Sander 2025-02-03 21:28 2595
{
1193b4be Poul Sander 2025-02-03 21:28 2596
    float s = 1.0f / 255.0f;
1193b4be Poul Sander 2025-02-03 21:28 2597
    return ImVec4(
1193b4be Poul Sander 2025-02-03 21:28 2598
        ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1193b4be Poul Sander 2025-02-03 21:28 2599
        ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1193b4be Poul Sander 2025-02-03 21:28 2600
        ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1193b4be Poul Sander 2025-02-03 21:28 2601
        ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1193b4be Poul Sander 2025-02-03 21:28 2602
}
1193b4be Poul Sander 2025-02-03 21:28 2603
1193b4be Poul Sander 2025-02-03 21:28 2604
ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1193b4be Poul Sander 2025-02-03 21:28 2605
{
1193b4be Poul Sander 2025-02-03 21:28 2606
    ImU32 out;
1193b4be Poul Sander 2025-02-03 21:28 2607
    out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1193b4be Poul Sander 2025-02-03 21:28 2608
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1193b4be Poul Sander 2025-02-03 21:28 2609
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1193b4be Poul Sander 2025-02-03 21:28 2610
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1193b4be Poul Sander 2025-02-03 21:28 2611
    return out;
1193b4be Poul Sander 2025-02-03 21:28 2612
}
1193b4be Poul Sander 2025-02-03 21:28 2613
1193b4be Poul Sander 2025-02-03 21:28 2614
// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1193b4be Poul Sander 2025-02-03 21:28 2615
// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1193b4be Poul Sander 2025-02-03 21:28 2616
void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1193b4be Poul Sander 2025-02-03 21:28 2617
{
1193b4be Poul Sander 2025-02-03 21:28 2618
    float K = 0.f;
1193b4be Poul Sander 2025-02-03 21:28 2619
    if (g < b)
1193b4be Poul Sander 2025-02-03 21:28 2620
    {
1193b4be Poul Sander 2025-02-03 21:28 2621
        ImSwap(g, b);
1193b4be Poul Sander 2025-02-03 21:28 2622
        K = -1.f;
1193b4be Poul Sander 2025-02-03 21:28 2623
    }
1193b4be Poul Sander 2025-02-03 21:28 2624
    if (r < g)
1193b4be Poul Sander 2025-02-03 21:28 2625
    {
1193b4be Poul Sander 2025-02-03 21:28 2626
        ImSwap(r, g);
1193b4be Poul Sander 2025-02-03 21:28 2627
        K = -2.f / 6.f - K;
1193b4be Poul Sander 2025-02-03 21:28 2628
    }
1193b4be Poul Sander 2025-02-03 21:28 2629
1193b4be Poul Sander 2025-02-03 21:28 2630
    const float chroma = r - (g < b ? g : b);
1193b4be Poul Sander 2025-02-03 21:28 2631
    out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1193b4be Poul Sander 2025-02-03 21:28 2632
    out_s = chroma / (r + 1e-20f);
1193b4be Poul Sander 2025-02-03 21:28 2633
    out_v = r;
1193b4be Poul Sander 2025-02-03 21:28 2634
}
1193b4be Poul Sander 2025-02-03 21:28 2635
1193b4be Poul Sander 2025-02-03 21:28 2636
// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1193b4be Poul Sander 2025-02-03 21:28 2637
// also http://en.wikipedia.org/wiki/HSL_and_HSV
1193b4be Poul Sander 2025-02-03 21:28 2638
void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1193b4be Poul Sander 2025-02-03 21:28 2639
{
1193b4be Poul Sander 2025-02-03 21:28 2640
    if (s == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 2641
    {
1193b4be Poul Sander 2025-02-03 21:28 2642
        // gray
1193b4be Poul Sander 2025-02-03 21:28 2643
        out_r = out_g = out_b = v;
1193b4be Poul Sander 2025-02-03 21:28 2644
        return;
1193b4be Poul Sander 2025-02-03 21:28 2645
    }
1193b4be Poul Sander 2025-02-03 21:28 2646
1193b4be Poul Sander 2025-02-03 21:28 2647
    h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1193b4be Poul Sander 2025-02-03 21:28 2648
    int   i = (int)h;
1193b4be Poul Sander 2025-02-03 21:28 2649
    float f = h - (float)i;
1193b4be Poul Sander 2025-02-03 21:28 2650
    float p = v * (1.0f - s);
1193b4be Poul Sander 2025-02-03 21:28 2651
    float q = v * (1.0f - s * f);
1193b4be Poul Sander 2025-02-03 21:28 2652
    float t = v * (1.0f - s * (1.0f - f));
1193b4be Poul Sander 2025-02-03 21:28 2653
1193b4be Poul Sander 2025-02-03 21:28 2654
    switch (i)
1193b4be Poul Sander 2025-02-03 21:28 2655
    {
1193b4be Poul Sander 2025-02-03 21:28 2656
    case 0: out_r = v; out_g = t; out_b = p; break;
1193b4be Poul Sander 2025-02-03 21:28 2657
    case 1: out_r = q; out_g = v; out_b = p; break;
1193b4be Poul Sander 2025-02-03 21:28 2658
    case 2: out_r = p; out_g = v; out_b = t; break;
1193b4be Poul Sander 2025-02-03 21:28 2659
    case 3: out_r = p; out_g = q; out_b = v; break;
1193b4be Poul Sander 2025-02-03 21:28 2660
    case 4: out_r = t; out_g = p; out_b = v; break;
1193b4be Poul Sander 2025-02-03 21:28 2661
    case 5: default: out_r = v; out_g = p; out_b = q; break;
1193b4be Poul Sander 2025-02-03 21:28 2662
    }
1193b4be Poul Sander 2025-02-03 21:28 2663
}
1193b4be Poul Sander 2025-02-03 21:28 2664
1193b4be Poul Sander 2025-02-03 21:28 2665
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2666
// [SECTION] ImGuiStorage
1193b4be Poul Sander 2025-02-03 21:28 2667
// Helper: Key->value storage
1193b4be Poul Sander 2025-02-03 21:28 2668
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2669
1193b4be Poul Sander 2025-02-03 21:28 2670
// std::lower_bound but without the bullshit
1193b4be Poul Sander 2025-02-03 21:28 2671
ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
1193b4be Poul Sander 2025-02-03 21:28 2672
{
1193b4be Poul Sander 2025-02-03 21:28 2673
    ImGuiStoragePair* in_p = in_begin;
1193b4be Poul Sander 2025-02-03 21:28 2674
    for (size_t count = (size_t)(in_end - in_p); count > 0; )
1193b4be Poul Sander 2025-02-03 21:28 2675
    {
1193b4be Poul Sander 2025-02-03 21:28 2676
        size_t count2 = count >> 1;
1193b4be Poul Sander 2025-02-03 21:28 2677
        ImGuiStoragePair* mid = in_p + count2;
1193b4be Poul Sander 2025-02-03 21:28 2678
        if (mid->key < key)
1193b4be Poul Sander 2025-02-03 21:28 2679
        {
1193b4be Poul Sander 2025-02-03 21:28 2680
            in_p = ++mid;
1193b4be Poul Sander 2025-02-03 21:28 2681
            count -= count2 + 1;
1193b4be Poul Sander 2025-02-03 21:28 2682
        }
1193b4be Poul Sander 2025-02-03 21:28 2683
        else
1193b4be Poul Sander 2025-02-03 21:28 2684
        {
1193b4be Poul Sander 2025-02-03 21:28 2685
            count = count2;
1193b4be Poul Sander 2025-02-03 21:28 2686
        }
1193b4be Poul Sander 2025-02-03 21:28 2687
    }
1193b4be Poul Sander 2025-02-03 21:28 2688
    return in_p;
1193b4be Poul Sander 2025-02-03 21:28 2689
}
1193b4be Poul Sander 2025-02-03 21:28 2690
1193b4be Poul Sander 2025-02-03 21:28 2691
IM_MSVC_RUNTIME_CHECKS_OFF
1193b4be Poul Sander 2025-02-03 21:28 2692
static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
1193b4be Poul Sander 2025-02-03 21:28 2693
{
1193b4be Poul Sander 2025-02-03 21:28 2694
    // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1193b4be Poul Sander 2025-02-03 21:28 2695
    ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
1193b4be Poul Sander 2025-02-03 21:28 2696
    ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
1193b4be Poul Sander 2025-02-03 21:28 2697
    return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
1193b4be Poul Sander 2025-02-03 21:28 2698
}
1193b4be Poul Sander 2025-02-03 21:28 2699
1193b4be Poul Sander 2025-02-03 21:28 2700
// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1193b4be Poul Sander 2025-02-03 21:28 2701
void ImGuiStorage::BuildSortByKey()
1193b4be Poul Sander 2025-02-03 21:28 2702
{
1193b4be Poul Sander 2025-02-03 21:28 2703
    ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
1193b4be Poul Sander 2025-02-03 21:28 2704
}
1193b4be Poul Sander 2025-02-03 21:28 2705
1193b4be Poul Sander 2025-02-03 21:28 2706
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1193b4be Poul Sander 2025-02-03 21:28 2707
{
1193b4be Poul Sander 2025-02-03 21:28 2708
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
1193b4be Poul Sander 2025-02-03 21:28 2709
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2710
        return default_val;
1193b4be Poul Sander 2025-02-03 21:28 2711
    return it->val_i;
1193b4be Poul Sander 2025-02-03 21:28 2712
}
1193b4be Poul Sander 2025-02-03 21:28 2713
1193b4be Poul Sander 2025-02-03 21:28 2714
bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1193b4be Poul Sander 2025-02-03 21:28 2715
{
1193b4be Poul Sander 2025-02-03 21:28 2716
    return GetInt(key, default_val ? 1 : 0) != 0;
1193b4be Poul Sander 2025-02-03 21:28 2717
}
1193b4be Poul Sander 2025-02-03 21:28 2718
1193b4be Poul Sander 2025-02-03 21:28 2719
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1193b4be Poul Sander 2025-02-03 21:28 2720
{
1193b4be Poul Sander 2025-02-03 21:28 2721
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
1193b4be Poul Sander 2025-02-03 21:28 2722
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2723
        return default_val;
1193b4be Poul Sander 2025-02-03 21:28 2724
    return it->val_f;
1193b4be Poul Sander 2025-02-03 21:28 2725
}
1193b4be Poul Sander 2025-02-03 21:28 2726
1193b4be Poul Sander 2025-02-03 21:28 2727
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1193b4be Poul Sander 2025-02-03 21:28 2728
{
1193b4be Poul Sander 2025-02-03 21:28 2729
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
1193b4be Poul Sander 2025-02-03 21:28 2730
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2731
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 2732
    return it->val_p;
1193b4be Poul Sander 2025-02-03 21:28 2733
}
1193b4be Poul Sander 2025-02-03 21:28 2734
1193b4be Poul Sander 2025-02-03 21:28 2735
// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
1193b4be Poul Sander 2025-02-03 21:28 2736
int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1193b4be Poul Sander 2025-02-03 21:28 2737
{
1193b4be Poul Sander 2025-02-03 21:28 2738
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2739
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2740
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
1193b4be Poul Sander 2025-02-03 21:28 2741
    return &it->val_i;
1193b4be Poul Sander 2025-02-03 21:28 2742
}
1193b4be Poul Sander 2025-02-03 21:28 2743
1193b4be Poul Sander 2025-02-03 21:28 2744
bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1193b4be Poul Sander 2025-02-03 21:28 2745
{
1193b4be Poul Sander 2025-02-03 21:28 2746
    return (bool*)GetIntRef(key, default_val ? 1 : 0);
1193b4be Poul Sander 2025-02-03 21:28 2747
}
1193b4be Poul Sander 2025-02-03 21:28 2748
1193b4be Poul Sander 2025-02-03 21:28 2749
float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1193b4be Poul Sander 2025-02-03 21:28 2750
{
1193b4be Poul Sander 2025-02-03 21:28 2751
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2752
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2753
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
1193b4be Poul Sander 2025-02-03 21:28 2754
    return &it->val_f;
1193b4be Poul Sander 2025-02-03 21:28 2755
}
1193b4be Poul Sander 2025-02-03 21:28 2756
1193b4be Poul Sander 2025-02-03 21:28 2757
void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1193b4be Poul Sander 2025-02-03 21:28 2758
{
1193b4be Poul Sander 2025-02-03 21:28 2759
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2760
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2761
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
1193b4be Poul Sander 2025-02-03 21:28 2762
    return &it->val_p;
1193b4be Poul Sander 2025-02-03 21:28 2763
}
1193b4be Poul Sander 2025-02-03 21:28 2764
1193b4be Poul Sander 2025-02-03 21:28 2765
// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
1193b4be Poul Sander 2025-02-03 21:28 2766
void ImGuiStorage::SetInt(ImGuiID key, int val)
1193b4be Poul Sander 2025-02-03 21:28 2767
{
1193b4be Poul Sander 2025-02-03 21:28 2768
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2769
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2770
        Data.insert(it, ImGuiStoragePair(key, val));
1193b4be Poul Sander 2025-02-03 21:28 2771
    else
1193b4be Poul Sander 2025-02-03 21:28 2772
        it->val_i = val;
1193b4be Poul Sander 2025-02-03 21:28 2773
}
1193b4be Poul Sander 2025-02-03 21:28 2774
1193b4be Poul Sander 2025-02-03 21:28 2775
void ImGuiStorage::SetBool(ImGuiID key, bool val)
1193b4be Poul Sander 2025-02-03 21:28 2776
{
1193b4be Poul Sander 2025-02-03 21:28 2777
    SetInt(key, val ? 1 : 0);
1193b4be Poul Sander 2025-02-03 21:28 2778
}
1193b4be Poul Sander 2025-02-03 21:28 2779
1193b4be Poul Sander 2025-02-03 21:28 2780
void ImGuiStorage::SetFloat(ImGuiID key, float val)
1193b4be Poul Sander 2025-02-03 21:28 2781
{
1193b4be Poul Sander 2025-02-03 21:28 2782
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2783
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2784
        Data.insert(it, ImGuiStoragePair(key, val));
1193b4be Poul Sander 2025-02-03 21:28 2785
    else
1193b4be Poul Sander 2025-02-03 21:28 2786
        it->val_f = val;
1193b4be Poul Sander 2025-02-03 21:28 2787
}
1193b4be Poul Sander 2025-02-03 21:28 2788
1193b4be Poul Sander 2025-02-03 21:28 2789
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1193b4be Poul Sander 2025-02-03 21:28 2790
{
1193b4be Poul Sander 2025-02-03 21:28 2791
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
1193b4be Poul Sander 2025-02-03 21:28 2792
    if (it == Data.Data + Data.Size || it->key != key)
1193b4be Poul Sander 2025-02-03 21:28 2793
        Data.insert(it, ImGuiStoragePair(key, val));
1193b4be Poul Sander 2025-02-03 21:28 2794
    else
1193b4be Poul Sander 2025-02-03 21:28 2795
        it->val_p = val;
1193b4be Poul Sander 2025-02-03 21:28 2796
}
1193b4be Poul Sander 2025-02-03 21:28 2797
1193b4be Poul Sander 2025-02-03 21:28 2798
void ImGuiStorage::SetAllInt(int v)
1193b4be Poul Sander 2025-02-03 21:28 2799
{
1193b4be Poul Sander 2025-02-03 21:28 2800
    for (int i = 0; i < Data.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 2801
        Data[i].val_i = v;
1193b4be Poul Sander 2025-02-03 21:28 2802
}
1193b4be Poul Sander 2025-02-03 21:28 2803
IM_MSVC_RUNTIME_CHECKS_RESTORE
1193b4be Poul Sander 2025-02-03 21:28 2804
1193b4be Poul Sander 2025-02-03 21:28 2805
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2806
// [SECTION] ImGuiTextFilter
1193b4be Poul Sander 2025-02-03 21:28 2807
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2808
1193b4be Poul Sander 2025-02-03 21:28 2809
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
1193b4be Poul Sander 2025-02-03 21:28 2810
ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
1193b4be Poul Sander 2025-02-03 21:28 2811
{
1193b4be Poul Sander 2025-02-03 21:28 2812
    InputBuf[0] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2813
    CountGrep = 0;
1193b4be Poul Sander 2025-02-03 21:28 2814
    if (default_filter)
1193b4be Poul Sander 2025-02-03 21:28 2815
    {
1193b4be Poul Sander 2025-02-03 21:28 2816
        ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1193b4be Poul Sander 2025-02-03 21:28 2817
        Build();
1193b4be Poul Sander 2025-02-03 21:28 2818
    }
1193b4be Poul Sander 2025-02-03 21:28 2819
}
1193b4be Poul Sander 2025-02-03 21:28 2820
1193b4be Poul Sander 2025-02-03 21:28 2821
bool ImGuiTextFilter::Draw(const char* label, float width)
1193b4be Poul Sander 2025-02-03 21:28 2822
{
1193b4be Poul Sander 2025-02-03 21:28 2823
    if (width != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 2824
        ImGui::SetNextItemWidth(width);
1193b4be Poul Sander 2025-02-03 21:28 2825
    bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1193b4be Poul Sander 2025-02-03 21:28 2826
    if (value_changed)
1193b4be Poul Sander 2025-02-03 21:28 2827
        Build();
1193b4be Poul Sander 2025-02-03 21:28 2828
    return value_changed;
1193b4be Poul Sander 2025-02-03 21:28 2829
}
1193b4be Poul Sander 2025-02-03 21:28 2830
1193b4be Poul Sander 2025-02-03 21:28 2831
void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1193b4be Poul Sander 2025-02-03 21:28 2832
{
1193b4be Poul Sander 2025-02-03 21:28 2833
    out->resize(0);
1193b4be Poul Sander 2025-02-03 21:28 2834
    const char* wb = b;
1193b4be Poul Sander 2025-02-03 21:28 2835
    const char* we = wb;
1193b4be Poul Sander 2025-02-03 21:28 2836
    while (we < e)
1193b4be Poul Sander 2025-02-03 21:28 2837
    {
1193b4be Poul Sander 2025-02-03 21:28 2838
        if (*we == separator)
1193b4be Poul Sander 2025-02-03 21:28 2839
        {
1193b4be Poul Sander 2025-02-03 21:28 2840
            out->push_back(ImGuiTextRange(wb, we));
1193b4be Poul Sander 2025-02-03 21:28 2841
            wb = we + 1;
1193b4be Poul Sander 2025-02-03 21:28 2842
        }
1193b4be Poul Sander 2025-02-03 21:28 2843
        we++;
1193b4be Poul Sander 2025-02-03 21:28 2844
    }
1193b4be Poul Sander 2025-02-03 21:28 2845
    if (wb != we)
1193b4be Poul Sander 2025-02-03 21:28 2846
        out->push_back(ImGuiTextRange(wb, we));
1193b4be Poul Sander 2025-02-03 21:28 2847
}
1193b4be Poul Sander 2025-02-03 21:28 2848
1193b4be Poul Sander 2025-02-03 21:28 2849
void ImGuiTextFilter::Build()
1193b4be Poul Sander 2025-02-03 21:28 2850
{
1193b4be Poul Sander 2025-02-03 21:28 2851
    Filters.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 2852
    ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
1193b4be Poul Sander 2025-02-03 21:28 2853
    input_range.split(',', &Filters);
1193b4be Poul Sander 2025-02-03 21:28 2854
1193b4be Poul Sander 2025-02-03 21:28 2855
    CountGrep = 0;
1193b4be Poul Sander 2025-02-03 21:28 2856
    for (ImGuiTextRange& f : Filters)
1193b4be Poul Sander 2025-02-03 21:28 2857
    {
1193b4be Poul Sander 2025-02-03 21:28 2858
        while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1193b4be Poul Sander 2025-02-03 21:28 2859
            f.b++;
1193b4be Poul Sander 2025-02-03 21:28 2860
        while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1193b4be Poul Sander 2025-02-03 21:28 2861
            f.e--;
1193b4be Poul Sander 2025-02-03 21:28 2862
        if (f.empty())
1193b4be Poul Sander 2025-02-03 21:28 2863
            continue;
1193b4be Poul Sander 2025-02-03 21:28 2864
        if (f.b[0] != '-')
1193b4be Poul Sander 2025-02-03 21:28 2865
            CountGrep += 1;
1193b4be Poul Sander 2025-02-03 21:28 2866
    }
1193b4be Poul Sander 2025-02-03 21:28 2867
}
1193b4be Poul Sander 2025-02-03 21:28 2868
1193b4be Poul Sander 2025-02-03 21:28 2869
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1193b4be Poul Sander 2025-02-03 21:28 2870
{
1193b4be Poul Sander 2025-02-03 21:28 2871
    if (Filters.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 2872
        return true;
1193b4be Poul Sander 2025-02-03 21:28 2873
1193b4be Poul Sander 2025-02-03 21:28 2874
    if (text == NULL)
1193b4be Poul Sander 2025-02-03 21:28 2875
        text = text_end = "";
1193b4be Poul Sander 2025-02-03 21:28 2876
1193b4be Poul Sander 2025-02-03 21:28 2877
    for (const ImGuiTextRange& f : Filters)
1193b4be Poul Sander 2025-02-03 21:28 2878
    {
1193b4be Poul Sander 2025-02-03 21:28 2879
        if (f.b == f.e)
1193b4be Poul Sander 2025-02-03 21:28 2880
            continue;
1193b4be Poul Sander 2025-02-03 21:28 2881
        if (f.b[0] == '-')
1193b4be Poul Sander 2025-02-03 21:28 2882
        {
1193b4be Poul Sander 2025-02-03 21:28 2883
            // Subtract
1193b4be Poul Sander 2025-02-03 21:28 2884
            if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
1193b4be Poul Sander 2025-02-03 21:28 2885
                return false;
1193b4be Poul Sander 2025-02-03 21:28 2886
        }
1193b4be Poul Sander 2025-02-03 21:28 2887
        else
1193b4be Poul Sander 2025-02-03 21:28 2888
        {
1193b4be Poul Sander 2025-02-03 21:28 2889
            // Grep
1193b4be Poul Sander 2025-02-03 21:28 2890
            if (ImStristr(text, text_end, f.b, f.e) != NULL)
1193b4be Poul Sander 2025-02-03 21:28 2891
                return true;
1193b4be Poul Sander 2025-02-03 21:28 2892
        }
1193b4be Poul Sander 2025-02-03 21:28 2893
    }
1193b4be Poul Sander 2025-02-03 21:28 2894
1193b4be Poul Sander 2025-02-03 21:28 2895
    // Implicit * grep
1193b4be Poul Sander 2025-02-03 21:28 2896
    if (CountGrep == 0)
1193b4be Poul Sander 2025-02-03 21:28 2897
        return true;
1193b4be Poul Sander 2025-02-03 21:28 2898
1193b4be Poul Sander 2025-02-03 21:28 2899
    return false;
1193b4be Poul Sander 2025-02-03 21:28 2900
}
1193b4be Poul Sander 2025-02-03 21:28 2901
1193b4be Poul Sander 2025-02-03 21:28 2902
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2903
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
1193b4be Poul Sander 2025-02-03 21:28 2904
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2905
1193b4be Poul Sander 2025-02-03 21:28 2906
// On some platform vsnprintf() takes va_list by reference and modifies it.
1193b4be Poul Sander 2025-02-03 21:28 2907
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1193b4be Poul Sander 2025-02-03 21:28 2908
#ifndef va_copy
1193b4be Poul Sander 2025-02-03 21:28 2909
#if defined(__GNUC__) || defined(__clang__)
1193b4be Poul Sander 2025-02-03 21:28 2910
#define va_copy(dest, src) __builtin_va_copy(dest, src)
1193b4be Poul Sander 2025-02-03 21:28 2911
#else
1193b4be Poul Sander 2025-02-03 21:28 2912
#define va_copy(dest, src) (dest = src)
1193b4be Poul Sander 2025-02-03 21:28 2913
#endif
1193b4be Poul Sander 2025-02-03 21:28 2914
#endif
1193b4be Poul Sander 2025-02-03 21:28 2915
1193b4be Poul Sander 2025-02-03 21:28 2916
char ImGuiTextBuffer::EmptyString[1] = { 0 };
1193b4be Poul Sander 2025-02-03 21:28 2917
1193b4be Poul Sander 2025-02-03 21:28 2918
void ImGuiTextBuffer::append(const char* str, const char* str_end)
1193b4be Poul Sander 2025-02-03 21:28 2919
{
1193b4be Poul Sander 2025-02-03 21:28 2920
    int len = str_end ? (int)(str_end - str) : (int)strlen(str);
1193b4be Poul Sander 2025-02-03 21:28 2921
1193b4be Poul Sander 2025-02-03 21:28 2922
    // Add zero-terminator the first time
1193b4be Poul Sander 2025-02-03 21:28 2923
    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
1193b4be Poul Sander 2025-02-03 21:28 2924
    const int needed_sz = write_off + len;
1193b4be Poul Sander 2025-02-03 21:28 2925
    if (write_off + len >= Buf.Capacity)
1193b4be Poul Sander 2025-02-03 21:28 2926
    {
1193b4be Poul Sander 2025-02-03 21:28 2927
        int new_capacity = Buf.Capacity * 2;
1193b4be Poul Sander 2025-02-03 21:28 2928
        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
1193b4be Poul Sander 2025-02-03 21:28 2929
    }
1193b4be Poul Sander 2025-02-03 21:28 2930
1193b4be Poul Sander 2025-02-03 21:28 2931
    Buf.resize(needed_sz);
1193b4be Poul Sander 2025-02-03 21:28 2932
    memcpy(&Buf[write_off - 1], str, (size_t)len);
1193b4be Poul Sander 2025-02-03 21:28 2933
    Buf[write_off - 1 + len] = 0;
1193b4be Poul Sander 2025-02-03 21:28 2934
}
1193b4be Poul Sander 2025-02-03 21:28 2935
1193b4be Poul Sander 2025-02-03 21:28 2936
void ImGuiTextBuffer::appendf(const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 2937
{
1193b4be Poul Sander 2025-02-03 21:28 2938
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 2939
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 2940
    appendfv(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 2941
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 2942
}
1193b4be Poul Sander 2025-02-03 21:28 2943
1193b4be Poul Sander 2025-02-03 21:28 2944
// Helper: Text buffer for logging/accumulating text
1193b4be Poul Sander 2025-02-03 21:28 2945
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 2946
{
1193b4be Poul Sander 2025-02-03 21:28 2947
    va_list args_copy;
1193b4be Poul Sander 2025-02-03 21:28 2948
    va_copy(args_copy, args);
1193b4be Poul Sander 2025-02-03 21:28 2949
1193b4be Poul Sander 2025-02-03 21:28 2950
    int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1193b4be Poul Sander 2025-02-03 21:28 2951
    if (len <= 0)
1193b4be Poul Sander 2025-02-03 21:28 2952
    {
1193b4be Poul Sander 2025-02-03 21:28 2953
        va_end(args_copy);
1193b4be Poul Sander 2025-02-03 21:28 2954
        return;
1193b4be Poul Sander 2025-02-03 21:28 2955
    }
1193b4be Poul Sander 2025-02-03 21:28 2956
1193b4be Poul Sander 2025-02-03 21:28 2957
    // Add zero-terminator the first time
1193b4be Poul Sander 2025-02-03 21:28 2958
    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
1193b4be Poul Sander 2025-02-03 21:28 2959
    const int needed_sz = write_off + len;
1193b4be Poul Sander 2025-02-03 21:28 2960
    if (write_off + len >= Buf.Capacity)
1193b4be Poul Sander 2025-02-03 21:28 2961
    {
1193b4be Poul Sander 2025-02-03 21:28 2962
        int new_capacity = Buf.Capacity * 2;
1193b4be Poul Sander 2025-02-03 21:28 2963
        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
1193b4be Poul Sander 2025-02-03 21:28 2964
    }
1193b4be Poul Sander 2025-02-03 21:28 2965
1193b4be Poul Sander 2025-02-03 21:28 2966
    Buf.resize(needed_sz);
1193b4be Poul Sander 2025-02-03 21:28 2967
    ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
1193b4be Poul Sander 2025-02-03 21:28 2968
    va_end(args_copy);
1193b4be Poul Sander 2025-02-03 21:28 2969
}
1193b4be Poul Sander 2025-02-03 21:28 2970
1193b4be Poul Sander 2025-02-03 21:28 2971
void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
1193b4be Poul Sander 2025-02-03 21:28 2972
{
1193b4be Poul Sander 2025-02-03 21:28 2973
    IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
1193b4be Poul Sander 2025-02-03 21:28 2974
    if (old_size == new_size)
1193b4be Poul Sander 2025-02-03 21:28 2975
        return;
1193b4be Poul Sander 2025-02-03 21:28 2976
    if (EndOffset == 0 || base[EndOffset - 1] == '\n')
1193b4be Poul Sander 2025-02-03 21:28 2977
        LineOffsets.push_back(EndOffset);
1193b4be Poul Sander 2025-02-03 21:28 2978
    const char* base_end = base + new_size;
1193b4be Poul Sander 2025-02-03 21:28 2979
    for (const char* p = base + old_size; (p = (const char*)memchr(p, '\n', base_end - p)) != 0; )
1193b4be Poul Sander 2025-02-03 21:28 2980
        if (++p < base_end) // Don't push a trailing offset on last \n
1193b4be Poul Sander 2025-02-03 21:28 2981
            LineOffsets.push_back((int)(intptr_t)(p - base));
1193b4be Poul Sander 2025-02-03 21:28 2982
    EndOffset = ImMax(EndOffset, new_size);
1193b4be Poul Sander 2025-02-03 21:28 2983
}
1193b4be Poul Sander 2025-02-03 21:28 2984
1193b4be Poul Sander 2025-02-03 21:28 2985
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2986
// [SECTION] ImGuiListClipper
1193b4be Poul Sander 2025-02-03 21:28 2987
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 2988
1193b4be Poul Sander 2025-02-03 21:28 2989
// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
1193b4be Poul Sander 2025-02-03 21:28 2990
// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
1193b4be Poul Sander 2025-02-03 21:28 2991
static bool GetSkipItemForListClipping()
1193b4be Poul Sander 2025-02-03 21:28 2992
{
1193b4be Poul Sander 2025-02-03 21:28 2993
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 2994
    return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
1193b4be Poul Sander 2025-02-03 21:28 2995
}
1193b4be Poul Sander 2025-02-03 21:28 2996
1193b4be Poul Sander 2025-02-03 21:28 2997
static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
1193b4be Poul Sander 2025-02-03 21:28 2998
{
1193b4be Poul Sander 2025-02-03 21:28 2999
    if (ranges.Size - offset <= 1)
1193b4be Poul Sander 2025-02-03 21:28 3000
        return;
1193b4be Poul Sander 2025-02-03 21:28 3001
1193b4be Poul Sander 2025-02-03 21:28 3002
    // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
1193b4be Poul Sander 2025-02-03 21:28 3003
    for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
1193b4be Poul Sander 2025-02-03 21:28 3004
        for (int i = offset; i < sort_end + offset; ++i)
1193b4be Poul Sander 2025-02-03 21:28 3005
            if (ranges[i].Min > ranges[i + 1].Min)
1193b4be Poul Sander 2025-02-03 21:28 3006
                ImSwap(ranges[i], ranges[i + 1]);
1193b4be Poul Sander 2025-02-03 21:28 3007
1193b4be Poul Sander 2025-02-03 21:28 3008
    // Now fuse ranges together as much as possible.
1193b4be Poul Sander 2025-02-03 21:28 3009
    for (int i = 1 + offset; i < ranges.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 3010
    {
1193b4be Poul Sander 2025-02-03 21:28 3011
        IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
1193b4be Poul Sander 2025-02-03 21:28 3012
        if (ranges[i - 1].Max < ranges[i].Min)
1193b4be Poul Sander 2025-02-03 21:28 3013
            continue;
1193b4be Poul Sander 2025-02-03 21:28 3014
        ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
1193b4be Poul Sander 2025-02-03 21:28 3015
        ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
1193b4be Poul Sander 2025-02-03 21:28 3016
        ranges.erase(ranges.Data + i);
1193b4be Poul Sander 2025-02-03 21:28 3017
        i--;
1193b4be Poul Sander 2025-02-03 21:28 3018
    }
1193b4be Poul Sander 2025-02-03 21:28 3019
}
1193b4be Poul Sander 2025-02-03 21:28 3020
1193b4be Poul Sander 2025-02-03 21:28 3021
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
1193b4be Poul Sander 2025-02-03 21:28 3022
{
1193b4be Poul Sander 2025-02-03 21:28 3023
    // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
1193b4be Poul Sander 2025-02-03 21:28 3024
    // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
1193b4be Poul Sander 2025-02-03 21:28 3025
    // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
1193b4be Poul Sander 2025-02-03 21:28 3026
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3027
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3028
    float off_y = pos_y - window->DC.CursorPos.y;
1193b4be Poul Sander 2025-02-03 21:28 3029
    window->DC.CursorPos.y = pos_y;
1193b4be Poul Sander 2025-02-03 21:28 3030
    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
1193b4be Poul Sander 2025-02-03 21:28 3031
    window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;  // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
1193b4be Poul Sander 2025-02-03 21:28 3032
    window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y);      // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
1193b4be Poul Sander 2025-02-03 21:28 3033
    if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
1193b4be Poul Sander 2025-02-03 21:28 3034
        columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
1193b4be Poul Sander 2025-02-03 21:28 3035
    if (ImGuiTable* table = g.CurrentTable)
1193b4be Poul Sander 2025-02-03 21:28 3036
    {
1193b4be Poul Sander 2025-02-03 21:28 3037
        if (table->IsInsideRow)
1193b4be Poul Sander 2025-02-03 21:28 3038
            ImGui::TableEndRow(table);
1193b4be Poul Sander 2025-02-03 21:28 3039
        table->RowPosY2 = window->DC.CursorPos.y;
1193b4be Poul Sander 2025-02-03 21:28 3040
        const int row_increase = (int)((off_y / line_height) + 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 3041
        //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
1193b4be Poul Sander 2025-02-03 21:28 3042
        table->RowBgColorCounter += row_increase;
1193b4be Poul Sander 2025-02-03 21:28 3043
    }
1193b4be Poul Sander 2025-02-03 21:28 3044
}
1193b4be Poul Sander 2025-02-03 21:28 3045
1193b4be Poul Sander 2025-02-03 21:28 3046
ImGuiListClipper::ImGuiListClipper()
1193b4be Poul Sander 2025-02-03 21:28 3047
{
1193b4be Poul Sander 2025-02-03 21:28 3048
    memset(this, 0, sizeof(*this));
1193b4be Poul Sander 2025-02-03 21:28 3049
}
1193b4be Poul Sander 2025-02-03 21:28 3050
1193b4be Poul Sander 2025-02-03 21:28 3051
ImGuiListClipper::~ImGuiListClipper()
1193b4be Poul Sander 2025-02-03 21:28 3052
{
1193b4be Poul Sander 2025-02-03 21:28 3053
    End();
1193b4be Poul Sander 2025-02-03 21:28 3054
}
1193b4be Poul Sander 2025-02-03 21:28 3055
1193b4be Poul Sander 2025-02-03 21:28 3056
void ImGuiListClipper::Begin(int items_count, float items_height)
1193b4be Poul Sander 2025-02-03 21:28 3057
{
1193b4be Poul Sander 2025-02-03 21:28 3058
    if (Ctx == NULL)
1193b4be Poul Sander 2025-02-03 21:28 3059
        Ctx = ImGui::GetCurrentContext();
1193b4be Poul Sander 2025-02-03 21:28 3060
1193b4be Poul Sander 2025-02-03 21:28 3061
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 3062
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3063
    IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 3064
1193b4be Poul Sander 2025-02-03 21:28 3065
    if (ImGuiTable* table = g.CurrentTable)
1193b4be Poul Sander 2025-02-03 21:28 3066
        if (table->IsInsideRow)
1193b4be Poul Sander 2025-02-03 21:28 3067
            ImGui::TableEndRow(table);
1193b4be Poul Sander 2025-02-03 21:28 3068
1193b4be Poul Sander 2025-02-03 21:28 3069
    StartPosY = window->DC.CursorPos.y;
1193b4be Poul Sander 2025-02-03 21:28 3070
    ItemsHeight = items_height;
1193b4be Poul Sander 2025-02-03 21:28 3071
    ItemsCount = items_count;
1193b4be Poul Sander 2025-02-03 21:28 3072
    DisplayStart = -1;
1193b4be Poul Sander 2025-02-03 21:28 3073
    DisplayEnd = 0;
1193b4be Poul Sander 2025-02-03 21:28 3074
1193b4be Poul Sander 2025-02-03 21:28 3075
    // Acquire temporary buffer
1193b4be Poul Sander 2025-02-03 21:28 3076
    if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
1193b4be Poul Sander 2025-02-03 21:28 3077
        g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
1193b4be Poul Sander 2025-02-03 21:28 3078
    ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
1193b4be Poul Sander 2025-02-03 21:28 3079
    data->Reset(this);
1193b4be Poul Sander 2025-02-03 21:28 3080
    data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
1193b4be Poul Sander 2025-02-03 21:28 3081
    TempData = data;
1193b4be Poul Sander 2025-02-03 21:28 3082
    StartSeekOffsetY = data->LossynessOffset;
1193b4be Poul Sander 2025-02-03 21:28 3083
}
1193b4be Poul Sander 2025-02-03 21:28 3084
1193b4be Poul Sander 2025-02-03 21:28 3085
void ImGuiListClipper::End()
1193b4be Poul Sander 2025-02-03 21:28 3086
{
1193b4be Poul Sander 2025-02-03 21:28 3087
    if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
1193b4be Poul Sander 2025-02-03 21:28 3088
    {
1193b4be Poul Sander 2025-02-03 21:28 3089
        // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
1193b4be Poul Sander 2025-02-03 21:28 3090
        ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 3091
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 3092
        if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
1193b4be Poul Sander 2025-02-03 21:28 3093
            SeekCursorForItem(ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3094
1193b4be Poul Sander 2025-02-03 21:28 3095
        // Restore temporary buffer and fix back pointers which may be invalidated when nesting
1193b4be Poul Sander 2025-02-03 21:28 3096
        IM_ASSERT(data->ListClipper == this);
1193b4be Poul Sander 2025-02-03 21:28 3097
        data->StepNo = data->Ranges.Size;
1193b4be Poul Sander 2025-02-03 21:28 3098
        if (--g.ClipperTempDataStacked > 0)
1193b4be Poul Sander 2025-02-03 21:28 3099
        {
1193b4be Poul Sander 2025-02-03 21:28 3100
            data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
1193b4be Poul Sander 2025-02-03 21:28 3101
            data->ListClipper->TempData = data;
1193b4be Poul Sander 2025-02-03 21:28 3102
        }
1193b4be Poul Sander 2025-02-03 21:28 3103
        TempData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3104
    }
1193b4be Poul Sander 2025-02-03 21:28 3105
    ItemsCount = -1;
1193b4be Poul Sander 2025-02-03 21:28 3106
}
1193b4be Poul Sander 2025-02-03 21:28 3107
1193b4be Poul Sander 2025-02-03 21:28 3108
void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
1193b4be Poul Sander 2025-02-03 21:28 3109
{
1193b4be Poul Sander 2025-02-03 21:28 3110
    ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
1193b4be Poul Sander 2025-02-03 21:28 3111
    IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
1193b4be Poul Sander 2025-02-03 21:28 3112
    IM_ASSERT(item_begin <= item_end);
1193b4be Poul Sander 2025-02-03 21:28 3113
    if (item_begin < item_end)
1193b4be Poul Sander 2025-02-03 21:28 3114
        data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
1193b4be Poul Sander 2025-02-03 21:28 3115
}
1193b4be Poul Sander 2025-02-03 21:28 3116
1193b4be Poul Sander 2025-02-03 21:28 3117
// This is already called while stepping.
1193b4be Poul Sander 2025-02-03 21:28 3118
// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
1193b4be Poul Sander 2025-02-03 21:28 3119
void ImGuiListClipper::SeekCursorForItem(int item_n)
1193b4be Poul Sander 2025-02-03 21:28 3120
{
1193b4be Poul Sander 2025-02-03 21:28 3121
    // - Perform the add and multiply with double to allow seeking through larger ranges.
1193b4be Poul Sander 2025-02-03 21:28 3122
    // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
1193b4be Poul Sander 2025-02-03 21:28 3123
    // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
1193b4be Poul Sander 2025-02-03 21:28 3124
    float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
1193b4be Poul Sander 2025-02-03 21:28 3125
    ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
1193b4be Poul Sander 2025-02-03 21:28 3126
}
1193b4be Poul Sander 2025-02-03 21:28 3127
1193b4be Poul Sander 2025-02-03 21:28 3128
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
1193b4be Poul Sander 2025-02-03 21:28 3129
{
1193b4be Poul Sander 2025-02-03 21:28 3130
    ImGuiContext& g = *clipper->Ctx;
1193b4be Poul Sander 2025-02-03 21:28 3131
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3132
    ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
1193b4be Poul Sander 2025-02-03 21:28 3133
    IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
1193b4be Poul Sander 2025-02-03 21:28 3134
1193b4be Poul Sander 2025-02-03 21:28 3135
    ImGuiTable* table = g.CurrentTable;
1193b4be Poul Sander 2025-02-03 21:28 3136
    if (table && table->IsInsideRow)
1193b4be Poul Sander 2025-02-03 21:28 3137
        ImGui::TableEndRow(table);
1193b4be Poul Sander 2025-02-03 21:28 3138
1193b4be Poul Sander 2025-02-03 21:28 3139
    // No items
1193b4be Poul Sander 2025-02-03 21:28 3140
    if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
1193b4be Poul Sander 2025-02-03 21:28 3141
        return false;
1193b4be Poul Sander 2025-02-03 21:28 3142
1193b4be Poul Sander 2025-02-03 21:28 3143
    // While we are in frozen row state, keep displaying items one by one, unclipped
1193b4be Poul Sander 2025-02-03 21:28 3144
    // FIXME: Could be stored as a table-agnostic state.
1193b4be Poul Sander 2025-02-03 21:28 3145
    if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
1193b4be Poul Sander 2025-02-03 21:28 3146
    {
1193b4be Poul Sander 2025-02-03 21:28 3147
        clipper->DisplayStart = data->ItemsFrozen;
1193b4be Poul Sander 2025-02-03 21:28 3148
        clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3149
        if (clipper->DisplayStart < clipper->DisplayEnd)
1193b4be Poul Sander 2025-02-03 21:28 3150
            data->ItemsFrozen++;
1193b4be Poul Sander 2025-02-03 21:28 3151
        return true;
1193b4be Poul Sander 2025-02-03 21:28 3152
    }
1193b4be Poul Sander 2025-02-03 21:28 3153
1193b4be Poul Sander 2025-02-03 21:28 3154
    // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
1193b4be Poul Sander 2025-02-03 21:28 3155
    bool calc_clipping = false;
1193b4be Poul Sander 2025-02-03 21:28 3156
    if (data->StepNo == 0)
1193b4be Poul Sander 2025-02-03 21:28 3157
    {
1193b4be Poul Sander 2025-02-03 21:28 3158
        clipper->StartPosY = window->DC.CursorPos.y;
1193b4be Poul Sander 2025-02-03 21:28 3159
        if (clipper->ItemsHeight <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 3160
        {
1193b4be Poul Sander 2025-02-03 21:28 3161
            // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
1193b4be Poul Sander 2025-02-03 21:28 3162
            data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
1193b4be Poul Sander 2025-02-03 21:28 3163
            clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
1193b4be Poul Sander 2025-02-03 21:28 3164
            clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3165
            data->StepNo = 1;
1193b4be Poul Sander 2025-02-03 21:28 3166
            return true;
1193b4be Poul Sander 2025-02-03 21:28 3167
        }
1193b4be Poul Sander 2025-02-03 21:28 3168
        calc_clipping = true;   // If on the first step with known item height, calculate clipping.
1193b4be Poul Sander 2025-02-03 21:28 3169
    }
1193b4be Poul Sander 2025-02-03 21:28 3170
1193b4be Poul Sander 2025-02-03 21:28 3171
    // Step 1: Let the clipper infer height from first range
1193b4be Poul Sander 2025-02-03 21:28 3172
    if (clipper->ItemsHeight <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 3173
    {
1193b4be Poul Sander 2025-02-03 21:28 3174
        IM_ASSERT(data->StepNo == 1);
1193b4be Poul Sander 2025-02-03 21:28 3175
        if (table)
1193b4be Poul Sander 2025-02-03 21:28 3176
            IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
1193b4be Poul Sander 2025-02-03 21:28 3177
1193b4be Poul Sander 2025-02-03 21:28 3178
        clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
1193b4be Poul Sander 2025-02-03 21:28 3179
        bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
1193b4be Poul Sander 2025-02-03 21:28 3180
        if (affected_by_floating_point_precision)
1193b4be Poul Sander 2025-02-03 21:28 3181
            clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
1193b4be Poul Sander 2025-02-03 21:28 3182
        if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
1193b4be Poul Sander 2025-02-03 21:28 3183
            return false;
1193b4be Poul Sander 2025-02-03 21:28 3184
        IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
1193b4be Poul Sander 2025-02-03 21:28 3185
        calc_clipping = true;   // If item height had to be calculated, calculate clipping afterwards.
1193b4be Poul Sander 2025-02-03 21:28 3186
    }
1193b4be Poul Sander 2025-02-03 21:28 3187
1193b4be Poul Sander 2025-02-03 21:28 3188
    // Step 0 or 1: Calculate the actual ranges of visible elements.
1193b4be Poul Sander 2025-02-03 21:28 3189
    const int already_submitted = clipper->DisplayEnd;
1193b4be Poul Sander 2025-02-03 21:28 3190
    if (calc_clipping)
1193b4be Poul Sander 2025-02-03 21:28 3191
    {
1193b4be Poul Sander 2025-02-03 21:28 3192
        // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
1193b4be Poul Sander 2025-02-03 21:28 3193
        clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
1193b4be Poul Sander 2025-02-03 21:28 3194
1193b4be Poul Sander 2025-02-03 21:28 3195
        if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 3196
        {
1193b4be Poul Sander 2025-02-03 21:28 3197
            // If logging is active, do not perform any clipping
1193b4be Poul Sander 2025-02-03 21:28 3198
            data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
1193b4be Poul Sander 2025-02-03 21:28 3199
        }
1193b4be Poul Sander 2025-02-03 21:28 3200
        else
1193b4be Poul Sander 2025-02-03 21:28 3201
        {
1193b4be Poul Sander 2025-02-03 21:28 3202
            // Add range selected to be included for navigation
1193b4be Poul Sander 2025-02-03 21:28 3203
            const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
1193b4be Poul Sander 2025-02-03 21:28 3204
            if (is_nav_request)
1193b4be Poul Sander 2025-02-03 21:28 3205
                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
1193b4be Poul Sander 2025-02-03 21:28 3206
            if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
1193b4be Poul Sander 2025-02-03 21:28 3207
                data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
1193b4be Poul Sander 2025-02-03 21:28 3208
1193b4be Poul Sander 2025-02-03 21:28 3209
            // Add focused/active item
1193b4be Poul Sander 2025-02-03 21:28 3210
            ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
1193b4be Poul Sander 2025-02-03 21:28 3211
            if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
1193b4be Poul Sander 2025-02-03 21:28 3212
                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
1193b4be Poul Sander 2025-02-03 21:28 3213
1193b4be Poul Sander 2025-02-03 21:28 3214
            // Add visible range
1193b4be Poul Sander 2025-02-03 21:28 3215
            float min_y = window->ClipRect.Min.y;
1193b4be Poul Sander 2025-02-03 21:28 3216
            float max_y = window->ClipRect.Max.y;
1193b4be Poul Sander 2025-02-03 21:28 3217
1193b4be Poul Sander 2025-02-03 21:28 3218
            // Add box selection range
1193b4be Poul Sander 2025-02-03 21:28 3219
            ImGuiBoxSelectState* bs = &g.BoxSelectState;
1193b4be Poul Sander 2025-02-03 21:28 3220
            if (bs->IsActive && bs->Window == window)
1193b4be Poul Sander 2025-02-03 21:28 3221
            {
1193b4be Poul Sander 2025-02-03 21:28 3222
                // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
1193b4be Poul Sander 2025-02-03 21:28 3223
                // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
1193b4be Poul Sander 2025-02-03 21:28 3224
                // As a workaround we currently half ItemSpacing worth on each side.
1193b4be Poul Sander 2025-02-03 21:28 3225
                min_y -= g.Style.ItemSpacing.y;
1193b4be Poul Sander 2025-02-03 21:28 3226
                max_y += g.Style.ItemSpacing.y;
1193b4be Poul Sander 2025-02-03 21:28 3227
1193b4be Poul Sander 2025-02-03 21:28 3228
                // Box-select on 2D area requires different clipping.
1193b4be Poul Sander 2025-02-03 21:28 3229
                if (bs->UnclipMode)
1193b4be Poul Sander 2025-02-03 21:28 3230
                    data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
1193b4be Poul Sander 2025-02-03 21:28 3231
            }
1193b4be Poul Sander 2025-02-03 21:28 3232
1193b4be Poul Sander 2025-02-03 21:28 3233
            const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
1193b4be Poul Sander 2025-02-03 21:28 3234
            const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
1193b4be Poul Sander 2025-02-03 21:28 3235
            data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
1193b4be Poul Sander 2025-02-03 21:28 3236
        }
1193b4be Poul Sander 2025-02-03 21:28 3237
1193b4be Poul Sander 2025-02-03 21:28 3238
        // Convert position ranges to item index ranges
1193b4be Poul Sander 2025-02-03 21:28 3239
        // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
1193b4be Poul Sander 2025-02-03 21:28 3240
        // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
1193b4be Poul Sander 2025-02-03 21:28 3241
        //   which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
1193b4be Poul Sander 2025-02-03 21:28 3242
        for (ImGuiListClipperRange& range : data->Ranges)
1193b4be Poul Sander 2025-02-03 21:28 3243
            if (range.PosToIndexConvert)
1193b4be Poul Sander 2025-02-03 21:28 3244
            {
1193b4be Poul Sander 2025-02-03 21:28 3245
                int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
1193b4be Poul Sander 2025-02-03 21:28 3246
                int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
1193b4be Poul Sander 2025-02-03 21:28 3247
                range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
1193b4be Poul Sander 2025-02-03 21:28 3248
                range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3249
                range.PosToIndexConvert = false;
1193b4be Poul Sander 2025-02-03 21:28 3250
            }
1193b4be Poul Sander 2025-02-03 21:28 3251
        ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
1193b4be Poul Sander 2025-02-03 21:28 3252
    }
1193b4be Poul Sander 2025-02-03 21:28 3253
1193b4be Poul Sander 2025-02-03 21:28 3254
    // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
1193b4be Poul Sander 2025-02-03 21:28 3255
    while (data->StepNo < data->Ranges.Size)
1193b4be Poul Sander 2025-02-03 21:28 3256
    {
1193b4be Poul Sander 2025-02-03 21:28 3257
        clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
1193b4be Poul Sander 2025-02-03 21:28 3258
        clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3259
        if (clipper->DisplayStart > already_submitted) //-V1051
1193b4be Poul Sander 2025-02-03 21:28 3260
            clipper->SeekCursorForItem(clipper->DisplayStart);
1193b4be Poul Sander 2025-02-03 21:28 3261
        data->StepNo++;
1193b4be Poul Sander 2025-02-03 21:28 3262
        if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
1193b4be Poul Sander 2025-02-03 21:28 3263
            continue;
1193b4be Poul Sander 2025-02-03 21:28 3264
        return true;
1193b4be Poul Sander 2025-02-03 21:28 3265
    }
1193b4be Poul Sander 2025-02-03 21:28 3266
1193b4be Poul Sander 2025-02-03 21:28 3267
    // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
1193b4be Poul Sander 2025-02-03 21:28 3268
    // Advance the cursor to the end of the list and then returns 'false' to end the loop.
1193b4be Poul Sander 2025-02-03 21:28 3269
    if (clipper->ItemsCount < INT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 3270
        clipper->SeekCursorForItem(clipper->ItemsCount);
1193b4be Poul Sander 2025-02-03 21:28 3271
1193b4be Poul Sander 2025-02-03 21:28 3272
    return false;
1193b4be Poul Sander 2025-02-03 21:28 3273
}
1193b4be Poul Sander 2025-02-03 21:28 3274
1193b4be Poul Sander 2025-02-03 21:28 3275
bool ImGuiListClipper::Step()
1193b4be Poul Sander 2025-02-03 21:28 3276
{
1193b4be Poul Sander 2025-02-03 21:28 3277
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 3278
    bool need_items_height = (ItemsHeight <= 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 3279
    bool ret = ImGuiListClipper_StepInternal(this);
1193b4be Poul Sander 2025-02-03 21:28 3280
    if (ret && (DisplayStart == DisplayEnd))
1193b4be Poul Sander 2025-02-03 21:28 3281
        ret = false;
1193b4be Poul Sander 2025-02-03 21:28 3282
    if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
1193b4be Poul Sander 2025-02-03 21:28 3283
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
1193b4be Poul Sander 2025-02-03 21:28 3284
    if (need_items_height && ItemsHeight > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 3285
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
1193b4be Poul Sander 2025-02-03 21:28 3286
    if (ret)
1193b4be Poul Sander 2025-02-03 21:28 3287
    {
1193b4be Poul Sander 2025-02-03 21:28 3288
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
1193b4be Poul Sander 2025-02-03 21:28 3289
    }
1193b4be Poul Sander 2025-02-03 21:28 3290
    else
1193b4be Poul Sander 2025-02-03 21:28 3291
    {
1193b4be Poul Sander 2025-02-03 21:28 3292
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
1193b4be Poul Sander 2025-02-03 21:28 3293
        End();
1193b4be Poul Sander 2025-02-03 21:28 3294
    }
1193b4be Poul Sander 2025-02-03 21:28 3295
    return ret;
1193b4be Poul Sander 2025-02-03 21:28 3296
}
1193b4be Poul Sander 2025-02-03 21:28 3297
1193b4be Poul Sander 2025-02-03 21:28 3298
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3299
// [SECTION] STYLING
1193b4be Poul Sander 2025-02-03 21:28 3300
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3301
1193b4be Poul Sander 2025-02-03 21:28 3302
ImGuiStyle& ImGui::GetStyle()
1193b4be Poul Sander 2025-02-03 21:28 3303
{
1193b4be Poul Sander 2025-02-03 21:28 3304
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
1193b4be Poul Sander 2025-02-03 21:28 3305
    return GImGui->Style;
1193b4be Poul Sander 2025-02-03 21:28 3306
}
1193b4be Poul Sander 2025-02-03 21:28 3307
1193b4be Poul Sander 2025-02-03 21:28 3308
ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1193b4be Poul Sander 2025-02-03 21:28 3309
{
1193b4be Poul Sander 2025-02-03 21:28 3310
    ImGuiStyle& style = GImGui->Style;
1193b4be Poul Sander 2025-02-03 21:28 3311
    ImVec4 c = style.Colors[idx];
1193b4be Poul Sander 2025-02-03 21:28 3312
    c.w *= style.Alpha * alpha_mul;
1193b4be Poul Sander 2025-02-03 21:28 3313
    return ColorConvertFloat4ToU32(c);
1193b4be Poul Sander 2025-02-03 21:28 3314
}
1193b4be Poul Sander 2025-02-03 21:28 3315
1193b4be Poul Sander 2025-02-03 21:28 3316
ImU32 ImGui::GetColorU32(const ImVec4& col)
1193b4be Poul Sander 2025-02-03 21:28 3317
{
1193b4be Poul Sander 2025-02-03 21:28 3318
    ImGuiStyle& style = GImGui->Style;
1193b4be Poul Sander 2025-02-03 21:28 3319
    ImVec4 c = col;
1193b4be Poul Sander 2025-02-03 21:28 3320
    c.w *= style.Alpha;
1193b4be Poul Sander 2025-02-03 21:28 3321
    return ColorConvertFloat4ToU32(c);
1193b4be Poul Sander 2025-02-03 21:28 3322
}
1193b4be Poul Sander 2025-02-03 21:28 3323
1193b4be Poul Sander 2025-02-03 21:28 3324
const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1193b4be Poul Sander 2025-02-03 21:28 3325
{
1193b4be Poul Sander 2025-02-03 21:28 3326
    ImGuiStyle& style = GImGui->Style;
1193b4be Poul Sander 2025-02-03 21:28 3327
    return style.Colors[idx];
1193b4be Poul Sander 2025-02-03 21:28 3328
}
1193b4be Poul Sander 2025-02-03 21:28 3329
1193b4be Poul Sander 2025-02-03 21:28 3330
ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
1193b4be Poul Sander 2025-02-03 21:28 3331
{
1193b4be Poul Sander 2025-02-03 21:28 3332
    ImGuiStyle& style = GImGui->Style;
1193b4be Poul Sander 2025-02-03 21:28 3333
    alpha_mul *= style.Alpha;
1193b4be Poul Sander 2025-02-03 21:28 3334
    if (alpha_mul >= 1.0f)
1193b4be Poul Sander 2025-02-03 21:28 3335
        return col;
1193b4be Poul Sander 2025-02-03 21:28 3336
    ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1193b4be Poul Sander 2025-02-03 21:28 3337
    a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
1193b4be Poul Sander 2025-02-03 21:28 3338
    return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1193b4be Poul Sander 2025-02-03 21:28 3339
}
1193b4be Poul Sander 2025-02-03 21:28 3340
1193b4be Poul Sander 2025-02-03 21:28 3341
// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
1193b4be Poul Sander 2025-02-03 21:28 3342
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
1193b4be Poul Sander 2025-02-03 21:28 3343
{
1193b4be Poul Sander 2025-02-03 21:28 3344
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3345
    ImGuiColorMod backup;
1193b4be Poul Sander 2025-02-03 21:28 3346
    backup.Col = idx;
1193b4be Poul Sander 2025-02-03 21:28 3347
    backup.BackupValue = g.Style.Colors[idx];
1193b4be Poul Sander 2025-02-03 21:28 3348
    g.ColorStack.push_back(backup);
1193b4be Poul Sander 2025-02-03 21:28 3349
    if (g.DebugFlashStyleColorIdx != idx)
1193b4be Poul Sander 2025-02-03 21:28 3350
        g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
1193b4be Poul Sander 2025-02-03 21:28 3351
}
1193b4be Poul Sander 2025-02-03 21:28 3352
1193b4be Poul Sander 2025-02-03 21:28 3353
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
1193b4be Poul Sander 2025-02-03 21:28 3354
{
1193b4be Poul Sander 2025-02-03 21:28 3355
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3356
    ImGuiColorMod backup;
1193b4be Poul Sander 2025-02-03 21:28 3357
    backup.Col = idx;
1193b4be Poul Sander 2025-02-03 21:28 3358
    backup.BackupValue = g.Style.Colors[idx];
1193b4be Poul Sander 2025-02-03 21:28 3359
    g.ColorStack.push_back(backup);
1193b4be Poul Sander 2025-02-03 21:28 3360
    if (g.DebugFlashStyleColorIdx != idx)
1193b4be Poul Sander 2025-02-03 21:28 3361
        g.Style.Colors[idx] = col;
1193b4be Poul Sander 2025-02-03 21:28 3362
}
1193b4be Poul Sander 2025-02-03 21:28 3363
1193b4be Poul Sander 2025-02-03 21:28 3364
void ImGui::PopStyleColor(int count)
1193b4be Poul Sander 2025-02-03 21:28 3365
{
1193b4be Poul Sander 2025-02-03 21:28 3366
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3367
    if (g.ColorStack.Size < count)
1193b4be Poul Sander 2025-02-03 21:28 3368
    {
1193b4be Poul Sander 2025-02-03 21:28 3369
        IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 3370
        count = g.ColorStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 3371
    }
1193b4be Poul Sander 2025-02-03 21:28 3372
    while (count > 0)
1193b4be Poul Sander 2025-02-03 21:28 3373
    {
1193b4be Poul Sander 2025-02-03 21:28 3374
        ImGuiColorMod& backup = g.ColorStack.back();
1193b4be Poul Sander 2025-02-03 21:28 3375
        g.Style.Colors[backup.Col] = backup.BackupValue;
1193b4be Poul Sander 2025-02-03 21:28 3376
        g.ColorStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 3377
        count--;
1193b4be Poul Sander 2025-02-03 21:28 3378
    }
1193b4be Poul Sander 2025-02-03 21:28 3379
}
1193b4be Poul Sander 2025-02-03 21:28 3380
1193b4be Poul Sander 2025-02-03 21:28 3381
static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
1193b4be Poul Sander 2025-02-03 21:28 3382
{
1193b4be Poul Sander 2025-02-03 21:28 3383
    ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline,
1193b4be Poul Sander 2025-02-03 21:28 3384
};
1193b4be Poul Sander 2025-02-03 21:28 3385
1193b4be Poul Sander 2025-02-03 21:28 3386
static const ImGuiDataVarInfo GStyleVarInfo[] =
1193b4be Poul Sander 2025-02-03 21:28 3387
{
1193b4be Poul Sander 2025-02-03 21:28 3388
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) },                     // ImGuiStyleVar_Alpha
1193b4be Poul Sander 2025-02-03 21:28 3389
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) },             // ImGuiStyleVar_DisabledAlpha
1193b4be Poul Sander 2025-02-03 21:28 3390
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) },             // ImGuiStyleVar_WindowPadding
1193b4be Poul Sander 2025-02-03 21:28 3391
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) },            // ImGuiStyleVar_WindowRounding
1193b4be Poul Sander 2025-02-03 21:28 3392
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) },          // ImGuiStyleVar_WindowBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3393
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) },             // ImGuiStyleVar_WindowMinSize
1193b4be Poul Sander 2025-02-03 21:28 3394
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) },          // ImGuiStyleVar_WindowTitleAlign
1193b4be Poul Sander 2025-02-03 21:28 3395
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) },             // ImGuiStyleVar_ChildRounding
1193b4be Poul Sander 2025-02-03 21:28 3396
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) },           // ImGuiStyleVar_ChildBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3397
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) },             // ImGuiStyleVar_PopupRounding
1193b4be Poul Sander 2025-02-03 21:28 3398
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) },           // ImGuiStyleVar_PopupBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3399
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) },              // ImGuiStyleVar_FramePadding
1193b4be Poul Sander 2025-02-03 21:28 3400
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) },             // ImGuiStyleVar_FrameRounding
1193b4be Poul Sander 2025-02-03 21:28 3401
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) },           // ImGuiStyleVar_FrameBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3402
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) },               // ImGuiStyleVar_ItemSpacing
1193b4be Poul Sander 2025-02-03 21:28 3403
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) },          // ImGuiStyleVar_ItemInnerSpacing
1193b4be Poul Sander 2025-02-03 21:28 3404
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) },             // ImGuiStyleVar_IndentSpacing
1193b4be Poul Sander 2025-02-03 21:28 3405
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) },               // ImGuiStyleVar_CellPadding
1193b4be Poul Sander 2025-02-03 21:28 3406
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) },             // ImGuiStyleVar_ScrollbarSize
1193b4be Poul Sander 2025-02-03 21:28 3407
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) },         // ImGuiStyleVar_ScrollbarRounding
1193b4be Poul Sander 2025-02-03 21:28 3408
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) },               // ImGuiStyleVar_GrabMinSize
1193b4be Poul Sander 2025-02-03 21:28 3409
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) },              // ImGuiStyleVar_GrabRounding
1193b4be Poul Sander 2025-02-03 21:28 3410
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) },               // ImGuiStyleVar_TabRounding
1193b4be Poul Sander 2025-02-03 21:28 3411
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) },             // ImGuiStyleVar_TabBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3412
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) },          // ImGuiStyleVar_TabBarBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3413
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) },        // ImGuiStyleVar_TabBarOverlineSize
1193b4be Poul Sander 2025-02-03 21:28 3414
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)},    // ImGuiStyleVar_TableAngledHeadersAngle
1193b4be Poul Sander 2025-02-03 21:28 3415
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
1193b4be Poul Sander 2025-02-03 21:28 3416
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) },           // ImGuiStyleVar_ButtonTextAlign
1193b4be Poul Sander 2025-02-03 21:28 3417
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) },       // ImGuiStyleVar_SelectableTextAlign
1193b4be Poul Sander 2025-02-03 21:28 3418
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},    // ImGuiStyleVar_SeparatorTextBorderSize
1193b4be Poul Sander 2025-02-03 21:28 3419
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) },        // ImGuiStyleVar_SeparatorTextAlign
1193b4be Poul Sander 2025-02-03 21:28 3420
    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) },      // ImGuiStyleVar_SeparatorTextPadding
1193b4be Poul Sander 2025-02-03 21:28 3421
    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) },      // ImGuiStyleVar_DockingSeparatorSize
1193b4be Poul Sander 2025-02-03 21:28 3422
};
1193b4be Poul Sander 2025-02-03 21:28 3423
1193b4be Poul Sander 2025-02-03 21:28 3424
const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
1193b4be Poul Sander 2025-02-03 21:28 3425
{
1193b4be Poul Sander 2025-02-03 21:28 3426
    IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 3427
    IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 3428
    return &GStyleVarInfo[idx];
1193b4be Poul Sander 2025-02-03 21:28 3429
}
1193b4be Poul Sander 2025-02-03 21:28 3430
1193b4be Poul Sander 2025-02-03 21:28 3431
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
1193b4be Poul Sander 2025-02-03 21:28 3432
{
1193b4be Poul Sander 2025-02-03 21:28 3433
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3434
    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
1193b4be Poul Sander 2025-02-03 21:28 3435
    if (var_info->Type != ImGuiDataType_Float || var_info->Count != 1)
1193b4be Poul Sander 2025-02-03 21:28 3436
    {
1193b4be Poul Sander 2025-02-03 21:28 3437
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
1193b4be Poul Sander 2025-02-03 21:28 3438
        return;
1193b4be Poul Sander 2025-02-03 21:28 3439
    }
1193b4be Poul Sander 2025-02-03 21:28 3440
    float* pvar = (float*)var_info->GetVarPtr(&g.Style);
1193b4be Poul Sander 2025-02-03 21:28 3441
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
1193b4be Poul Sander 2025-02-03 21:28 3442
    *pvar = val;
1193b4be Poul Sander 2025-02-03 21:28 3443
}
1193b4be Poul Sander 2025-02-03 21:28 3444
1193b4be Poul Sander 2025-02-03 21:28 3445
void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
1193b4be Poul Sander 2025-02-03 21:28 3446
{
1193b4be Poul Sander 2025-02-03 21:28 3447
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3448
    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
1193b4be Poul Sander 2025-02-03 21:28 3449
    if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
1193b4be Poul Sander 2025-02-03 21:28 3450
    {
1193b4be Poul Sander 2025-02-03 21:28 3451
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
1193b4be Poul Sander 2025-02-03 21:28 3452
        return;
1193b4be Poul Sander 2025-02-03 21:28 3453
    }
1193b4be Poul Sander 2025-02-03 21:28 3454
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
1193b4be Poul Sander 2025-02-03 21:28 3455
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
1193b4be Poul Sander 2025-02-03 21:28 3456
    pvar->x = val_x;
1193b4be Poul Sander 2025-02-03 21:28 3457
}
1193b4be Poul Sander 2025-02-03 21:28 3458
1193b4be Poul Sander 2025-02-03 21:28 3459
void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
1193b4be Poul Sander 2025-02-03 21:28 3460
{
1193b4be Poul Sander 2025-02-03 21:28 3461
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3462
    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
1193b4be Poul Sander 2025-02-03 21:28 3463
    if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
1193b4be Poul Sander 2025-02-03 21:28 3464
    {
1193b4be Poul Sander 2025-02-03 21:28 3465
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
1193b4be Poul Sander 2025-02-03 21:28 3466
        return;
1193b4be Poul Sander 2025-02-03 21:28 3467
    }
1193b4be Poul Sander 2025-02-03 21:28 3468
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
1193b4be Poul Sander 2025-02-03 21:28 3469
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
1193b4be Poul Sander 2025-02-03 21:28 3470
    pvar->y = val_y;
1193b4be Poul Sander 2025-02-03 21:28 3471
}
1193b4be Poul Sander 2025-02-03 21:28 3472
1193b4be Poul Sander 2025-02-03 21:28 3473
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
1193b4be Poul Sander 2025-02-03 21:28 3474
{
1193b4be Poul Sander 2025-02-03 21:28 3475
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3476
    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
1193b4be Poul Sander 2025-02-03 21:28 3477
    if (var_info->Type != ImGuiDataType_Float || var_info->Count != 2)
1193b4be Poul Sander 2025-02-03 21:28 3478
    {
1193b4be Poul Sander 2025-02-03 21:28 3479
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
1193b4be Poul Sander 2025-02-03 21:28 3480
        return;
1193b4be Poul Sander 2025-02-03 21:28 3481
    }
1193b4be Poul Sander 2025-02-03 21:28 3482
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
1193b4be Poul Sander 2025-02-03 21:28 3483
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
1193b4be Poul Sander 2025-02-03 21:28 3484
    *pvar = val;
1193b4be Poul Sander 2025-02-03 21:28 3485
}
1193b4be Poul Sander 2025-02-03 21:28 3486
1193b4be Poul Sander 2025-02-03 21:28 3487
void ImGui::PopStyleVar(int count)
1193b4be Poul Sander 2025-02-03 21:28 3488
{
1193b4be Poul Sander 2025-02-03 21:28 3489
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3490
    if (g.StyleVarStack.Size < count)
1193b4be Poul Sander 2025-02-03 21:28 3491
    {
1193b4be Poul Sander 2025-02-03 21:28 3492
        IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 3493
        count = g.StyleVarStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 3494
    }
1193b4be Poul Sander 2025-02-03 21:28 3495
    while (count > 0)
1193b4be Poul Sander 2025-02-03 21:28 3496
    {
1193b4be Poul Sander 2025-02-03 21:28 3497
        // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
1193b4be Poul Sander 2025-02-03 21:28 3498
        ImGuiStyleMod& backup = g.StyleVarStack.back();
1193b4be Poul Sander 2025-02-03 21:28 3499
        const ImGuiDataVarInfo* info = GetStyleVarInfo(backup.VarIdx);
1193b4be Poul Sander 2025-02-03 21:28 3500
        void* data = info->GetVarPtr(&g.Style);
1193b4be Poul Sander 2025-02-03 21:28 3501
        if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
1193b4be Poul Sander 2025-02-03 21:28 3502
        else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
1193b4be Poul Sander 2025-02-03 21:28 3503
        g.StyleVarStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 3504
        count--;
1193b4be Poul Sander 2025-02-03 21:28 3505
    }
1193b4be Poul Sander 2025-02-03 21:28 3506
}
1193b4be Poul Sander 2025-02-03 21:28 3507
1193b4be Poul Sander 2025-02-03 21:28 3508
const char* ImGui::GetStyleColorName(ImGuiCol idx)
1193b4be Poul Sander 2025-02-03 21:28 3509
{
1193b4be Poul Sander 2025-02-03 21:28 3510
    // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
1193b4be Poul Sander 2025-02-03 21:28 3511
    switch (idx)
1193b4be Poul Sander 2025-02-03 21:28 3512
    {
1193b4be Poul Sander 2025-02-03 21:28 3513
    case ImGuiCol_Text: return "Text";
1193b4be Poul Sander 2025-02-03 21:28 3514
    case ImGuiCol_TextDisabled: return "TextDisabled";
1193b4be Poul Sander 2025-02-03 21:28 3515
    case ImGuiCol_WindowBg: return "WindowBg";
1193b4be Poul Sander 2025-02-03 21:28 3516
    case ImGuiCol_ChildBg: return "ChildBg";
1193b4be Poul Sander 2025-02-03 21:28 3517
    case ImGuiCol_PopupBg: return "PopupBg";
1193b4be Poul Sander 2025-02-03 21:28 3518
    case ImGuiCol_Border: return "Border";
1193b4be Poul Sander 2025-02-03 21:28 3519
    case ImGuiCol_BorderShadow: return "BorderShadow";
1193b4be Poul Sander 2025-02-03 21:28 3520
    case ImGuiCol_FrameBg: return "FrameBg";
1193b4be Poul Sander 2025-02-03 21:28 3521
    case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
1193b4be Poul Sander 2025-02-03 21:28 3522
    case ImGuiCol_FrameBgActive: return "FrameBgActive";
1193b4be Poul Sander 2025-02-03 21:28 3523
    case ImGuiCol_TitleBg: return "TitleBg";
1193b4be Poul Sander 2025-02-03 21:28 3524
    case ImGuiCol_TitleBgActive: return "TitleBgActive";
1193b4be Poul Sander 2025-02-03 21:28 3525
    case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
1193b4be Poul Sander 2025-02-03 21:28 3526
    case ImGuiCol_MenuBarBg: return "MenuBarBg";
1193b4be Poul Sander 2025-02-03 21:28 3527
    case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
1193b4be Poul Sander 2025-02-03 21:28 3528
    case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
1193b4be Poul Sander 2025-02-03 21:28 3529
    case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
1193b4be Poul Sander 2025-02-03 21:28 3530
    case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
1193b4be Poul Sander 2025-02-03 21:28 3531
    case ImGuiCol_CheckMark: return "CheckMark";
1193b4be Poul Sander 2025-02-03 21:28 3532
    case ImGuiCol_SliderGrab: return "SliderGrab";
1193b4be Poul Sander 2025-02-03 21:28 3533
    case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
1193b4be Poul Sander 2025-02-03 21:28 3534
    case ImGuiCol_Button: return "Button";
1193b4be Poul Sander 2025-02-03 21:28 3535
    case ImGuiCol_ButtonHovered: return "ButtonHovered";
1193b4be Poul Sander 2025-02-03 21:28 3536
    case ImGuiCol_ButtonActive: return "ButtonActive";
1193b4be Poul Sander 2025-02-03 21:28 3537
    case ImGuiCol_Header: return "Header";
1193b4be Poul Sander 2025-02-03 21:28 3538
    case ImGuiCol_HeaderHovered: return "HeaderHovered";
1193b4be Poul Sander 2025-02-03 21:28 3539
    case ImGuiCol_HeaderActive: return "HeaderActive";
1193b4be Poul Sander 2025-02-03 21:28 3540
    case ImGuiCol_Separator: return "Separator";
1193b4be Poul Sander 2025-02-03 21:28 3541
    case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
1193b4be Poul Sander 2025-02-03 21:28 3542
    case ImGuiCol_SeparatorActive: return "SeparatorActive";
1193b4be Poul Sander 2025-02-03 21:28 3543
    case ImGuiCol_ResizeGrip: return "ResizeGrip";
1193b4be Poul Sander 2025-02-03 21:28 3544
    case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
1193b4be Poul Sander 2025-02-03 21:28 3545
    case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
1193b4be Poul Sander 2025-02-03 21:28 3546
    case ImGuiCol_TabHovered: return "TabHovered";
1193b4be Poul Sander 2025-02-03 21:28 3547
    case ImGuiCol_Tab: return "Tab";
1193b4be Poul Sander 2025-02-03 21:28 3548
    case ImGuiCol_TabSelected: return "TabSelected";
1193b4be Poul Sander 2025-02-03 21:28 3549
    case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
1193b4be Poul Sander 2025-02-03 21:28 3550
    case ImGuiCol_TabDimmed: return "TabDimmed";
1193b4be Poul Sander 2025-02-03 21:28 3551
    case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
1193b4be Poul Sander 2025-02-03 21:28 3552
    case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
1193b4be Poul Sander 2025-02-03 21:28 3553
    case ImGuiCol_DockingPreview: return "DockingPreview";
1193b4be Poul Sander 2025-02-03 21:28 3554
    case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
1193b4be Poul Sander 2025-02-03 21:28 3555
    case ImGuiCol_PlotLines: return "PlotLines";
1193b4be Poul Sander 2025-02-03 21:28 3556
    case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
1193b4be Poul Sander 2025-02-03 21:28 3557
    case ImGuiCol_PlotHistogram: return "PlotHistogram";
1193b4be Poul Sander 2025-02-03 21:28 3558
    case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
1193b4be Poul Sander 2025-02-03 21:28 3559
    case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
1193b4be Poul Sander 2025-02-03 21:28 3560
    case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
1193b4be Poul Sander 2025-02-03 21:28 3561
    case ImGuiCol_TableBorderLight: return "TableBorderLight";
1193b4be Poul Sander 2025-02-03 21:28 3562
    case ImGuiCol_TableRowBg: return "TableRowBg";
1193b4be Poul Sander 2025-02-03 21:28 3563
    case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
1193b4be Poul Sander 2025-02-03 21:28 3564
    case ImGuiCol_TextLink: return "TextLink";
1193b4be Poul Sander 2025-02-03 21:28 3565
    case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
1193b4be Poul Sander 2025-02-03 21:28 3566
    case ImGuiCol_DragDropTarget: return "DragDropTarget";
1193b4be Poul Sander 2025-02-03 21:28 3567
    case ImGuiCol_NavCursor: return "NavCursor";
1193b4be Poul Sander 2025-02-03 21:28 3568
    case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
1193b4be Poul Sander 2025-02-03 21:28 3569
    case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
1193b4be Poul Sander 2025-02-03 21:28 3570
    case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
1193b4be Poul Sander 2025-02-03 21:28 3571
    }
1193b4be Poul Sander 2025-02-03 21:28 3572
    IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 3573
    return "Unknown";
1193b4be Poul Sander 2025-02-03 21:28 3574
}
1193b4be Poul Sander 2025-02-03 21:28 3575
1193b4be Poul Sander 2025-02-03 21:28 3576
1193b4be Poul Sander 2025-02-03 21:28 3577
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3578
// [SECTION] RENDER HELPERS
1193b4be Poul Sander 2025-02-03 21:28 3579
// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
1193b4be Poul Sander 2025-02-03 21:28 3580
// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
1193b4be Poul Sander 2025-02-03 21:28 3581
// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
1193b4be Poul Sander 2025-02-03 21:28 3582
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3583
1193b4be Poul Sander 2025-02-03 21:28 3584
const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
1193b4be Poul Sander 2025-02-03 21:28 3585
{
1193b4be Poul Sander 2025-02-03 21:28 3586
    const char* text_display_end = text;
1193b4be Poul Sander 2025-02-03 21:28 3587
    if (!text_end)
1193b4be Poul Sander 2025-02-03 21:28 3588
        text_end = (const char*)-1;
1193b4be Poul Sander 2025-02-03 21:28 3589
1193b4be Poul Sander 2025-02-03 21:28 3590
    while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
1193b4be Poul Sander 2025-02-03 21:28 3591
        text_display_end++;
1193b4be Poul Sander 2025-02-03 21:28 3592
    return text_display_end;
1193b4be Poul Sander 2025-02-03 21:28 3593
}
1193b4be Poul Sander 2025-02-03 21:28 3594
1193b4be Poul Sander 2025-02-03 21:28 3595
// Internal ImGui functions to render text
1193b4be Poul Sander 2025-02-03 21:28 3596
// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
1193b4be Poul Sander 2025-02-03 21:28 3597
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
1193b4be Poul Sander 2025-02-03 21:28 3598
{
1193b4be Poul Sander 2025-02-03 21:28 3599
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3600
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3601
1193b4be Poul Sander 2025-02-03 21:28 3602
    // Hide anything after a '##' string
1193b4be Poul Sander 2025-02-03 21:28 3603
    const char* text_display_end;
1193b4be Poul Sander 2025-02-03 21:28 3604
    if (hide_text_after_hash)
1193b4be Poul Sander 2025-02-03 21:28 3605
    {
1193b4be Poul Sander 2025-02-03 21:28 3606
        text_display_end = FindRenderedTextEnd(text, text_end);
1193b4be Poul Sander 2025-02-03 21:28 3607
    }
1193b4be Poul Sander 2025-02-03 21:28 3608
    else
1193b4be Poul Sander 2025-02-03 21:28 3609
    {
1193b4be Poul Sander 2025-02-03 21:28 3610
        if (!text_end)
1193b4be Poul Sander 2025-02-03 21:28 3611
            text_end = text + strlen(text); // FIXME-OPT
1193b4be Poul Sander 2025-02-03 21:28 3612
        text_display_end = text_end;
1193b4be Poul Sander 2025-02-03 21:28 3613
    }
1193b4be Poul Sander 2025-02-03 21:28 3614
1193b4be Poul Sander 2025-02-03 21:28 3615
    if (text != text_display_end)
1193b4be Poul Sander 2025-02-03 21:28 3616
    {
1193b4be Poul Sander 2025-02-03 21:28 3617
        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
1193b4be Poul Sander 2025-02-03 21:28 3618
        if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 3619
            LogRenderedText(&pos, text, text_display_end);
1193b4be Poul Sander 2025-02-03 21:28 3620
    }
1193b4be Poul Sander 2025-02-03 21:28 3621
}
1193b4be Poul Sander 2025-02-03 21:28 3622
1193b4be Poul Sander 2025-02-03 21:28 3623
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
1193b4be Poul Sander 2025-02-03 21:28 3624
{
1193b4be Poul Sander 2025-02-03 21:28 3625
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3626
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3627
1193b4be Poul Sander 2025-02-03 21:28 3628
    if (!text_end)
1193b4be Poul Sander 2025-02-03 21:28 3629
        text_end = text + strlen(text); // FIXME-OPT
1193b4be Poul Sander 2025-02-03 21:28 3630
1193b4be Poul Sander 2025-02-03 21:28 3631
    if (text != text_end)
1193b4be Poul Sander 2025-02-03 21:28 3632
    {
1193b4be Poul Sander 2025-02-03 21:28 3633
        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
1193b4be Poul Sander 2025-02-03 21:28 3634
        if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 3635
            LogRenderedText(&pos, text, text_end);
1193b4be Poul Sander 2025-02-03 21:28 3636
    }
1193b4be Poul Sander 2025-02-03 21:28 3637
}
1193b4be Poul Sander 2025-02-03 21:28 3638
1193b4be Poul Sander 2025-02-03 21:28 3639
// Default clip_rect uses (pos_min,pos_max)
1193b4be Poul Sander 2025-02-03 21:28 3640
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
1193b4be Poul Sander 2025-02-03 21:28 3641
// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.
1193b4be Poul Sander 2025-02-03 21:28 3642
// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
1193b4be Poul Sander 2025-02-03 21:28 3643
// better advantage of the render function taking size into account for coarse clipping.
1193b4be Poul Sander 2025-02-03 21:28 3644
void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 3645
{
1193b4be Poul Sander 2025-02-03 21:28 3646
    // Perform CPU side clipping for single clipped element to avoid using scissor state
1193b4be Poul Sander 2025-02-03 21:28 3647
    ImVec2 pos = pos_min;
1193b4be Poul Sander 2025-02-03 21:28 3648
    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 3649
1193b4be Poul Sander 2025-02-03 21:28 3650
    const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
1193b4be Poul Sander 2025-02-03 21:28 3651
    const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
1193b4be Poul Sander 2025-02-03 21:28 3652
    bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
1193b4be Poul Sander 2025-02-03 21:28 3653
    if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
1193b4be Poul Sander 2025-02-03 21:28 3654
        need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
1193b4be Poul Sander 2025-02-03 21:28 3655
1193b4be Poul Sander 2025-02-03 21:28 3656
    // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
1193b4be Poul Sander 2025-02-03 21:28 3657
    if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
1193b4be Poul Sander 2025-02-03 21:28 3658
    if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
1193b4be Poul Sander 2025-02-03 21:28 3659
1193b4be Poul Sander 2025-02-03 21:28 3660
    // Render
1193b4be Poul Sander 2025-02-03 21:28 3661
    if (need_clipping)
1193b4be Poul Sander 2025-02-03 21:28 3662
    {
1193b4be Poul Sander 2025-02-03 21:28 3663
        ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
1193b4be Poul Sander 2025-02-03 21:28 3664
        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
1193b4be Poul Sander 2025-02-03 21:28 3665
    }
1193b4be Poul Sander 2025-02-03 21:28 3666
    else
1193b4be Poul Sander 2025-02-03 21:28 3667
    {
1193b4be Poul Sander 2025-02-03 21:28 3668
        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
1193b4be Poul Sander 2025-02-03 21:28 3669
    }
1193b4be Poul Sander 2025-02-03 21:28 3670
}
1193b4be Poul Sander 2025-02-03 21:28 3671
1193b4be Poul Sander 2025-02-03 21:28 3672
void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 3673
{
1193b4be Poul Sander 2025-02-03 21:28 3674
    // Hide anything after a '##' string
1193b4be Poul Sander 2025-02-03 21:28 3675
    const char* text_display_end = FindRenderedTextEnd(text, text_end);
1193b4be Poul Sander 2025-02-03 21:28 3676
    const int text_len = (int)(text_display_end - text);
1193b4be Poul Sander 2025-02-03 21:28 3677
    if (text_len == 0)
1193b4be Poul Sander 2025-02-03 21:28 3678
        return;
1193b4be Poul Sander 2025-02-03 21:28 3679
1193b4be Poul Sander 2025-02-03 21:28 3680
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3681
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3682
    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
1193b4be Poul Sander 2025-02-03 21:28 3683
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 3684
        LogRenderedText(&pos_min, text, text_display_end);
1193b4be Poul Sander 2025-02-03 21:28 3685
}
1193b4be Poul Sander 2025-02-03 21:28 3686
1193b4be Poul Sander 2025-02-03 21:28 3687
// Another overly complex function until we reorganize everything into a nice all-in-one helper.
1193b4be Poul Sander 2025-02-03 21:28 3688
// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
1193b4be Poul Sander 2025-02-03 21:28 3689
// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
1193b4be Poul Sander 2025-02-03 21:28 3690
void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
1193b4be Poul Sander 2025-02-03 21:28 3691
{
1193b4be Poul Sander 2025-02-03 21:28 3692
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3693
    if (text_end_full == NULL)
1193b4be Poul Sander 2025-02-03 21:28 3694
        text_end_full = FindRenderedTextEnd(text);
1193b4be Poul Sander 2025-02-03 21:28 3695
    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 3696
1193b4be Poul Sander 2025-02-03 21:28 3697
    //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
1193b4be Poul Sander 2025-02-03 21:28 3698
    //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 3699
    //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 3700
    // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
1193b4be Poul Sander 2025-02-03 21:28 3701
    if (text_size.x > pos_max.x - pos_min.x)
1193b4be Poul Sander 2025-02-03 21:28 3702
    {
1193b4be Poul Sander 2025-02-03 21:28 3703
        // Hello wo...
1193b4be Poul Sander 2025-02-03 21:28 3704
        // |       |   |
1193b4be Poul Sander 2025-02-03 21:28 3705
        // min   max   ellipsis_max
1193b4be Poul Sander 2025-02-03 21:28 3706
        //          <-> this is generally some padding value
1193b4be Poul Sander 2025-02-03 21:28 3707
1193b4be Poul Sander 2025-02-03 21:28 3708
        const ImFont* font = draw_list->_Data->Font;
1193b4be Poul Sander 2025-02-03 21:28 3709
        const float font_size = draw_list->_Data->FontSize;
1193b4be Poul Sander 2025-02-03 21:28 3710
        const float font_scale = draw_list->_Data->FontScale;
1193b4be Poul Sander 2025-02-03 21:28 3711
        const char* text_end_ellipsis = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3712
        const float ellipsis_width = font->EllipsisWidth * font_scale;
1193b4be Poul Sander 2025-02-03 21:28 3713
1193b4be Poul Sander 2025-02-03 21:28 3714
        // We can now claim the space between pos_max.x and ellipsis_max.x
1193b4be Poul Sander 2025-02-03 21:28 3715
        const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 3716
        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
1193b4be Poul Sander 2025-02-03 21:28 3717
        if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
1193b4be Poul Sander 2025-02-03 21:28 3718
        {
1193b4be Poul Sander 2025-02-03 21:28 3719
            // Always display at least 1 character if there's no room for character + ellipsis
1193b4be Poul Sander 2025-02-03 21:28 3720
            text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
1193b4be Poul Sander 2025-02-03 21:28 3721
            text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
1193b4be Poul Sander 2025-02-03 21:28 3722
        }
1193b4be Poul Sander 2025-02-03 21:28 3723
        while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
1193b4be Poul Sander 2025-02-03 21:28 3724
        {
1193b4be Poul Sander 2025-02-03 21:28 3725
            // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
1193b4be Poul Sander 2025-02-03 21:28 3726
            text_end_ellipsis--;
1193b4be Poul Sander 2025-02-03 21:28 3727
            text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
1193b4be Poul Sander 2025-02-03 21:28 3728
        }
1193b4be Poul Sander 2025-02-03 21:28 3729
1193b4be Poul Sander 2025-02-03 21:28 3730
        // Render text, render ellipsis
1193b4be Poul Sander 2025-02-03 21:28 3731
        RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 3732
        ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
1193b4be Poul Sander 2025-02-03 21:28 3733
        if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
1193b4be Poul Sander 2025-02-03 21:28 3734
            for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
1193b4be Poul Sander 2025-02-03 21:28 3735
                font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
1193b4be Poul Sander 2025-02-03 21:28 3736
    }
1193b4be Poul Sander 2025-02-03 21:28 3737
    else
1193b4be Poul Sander 2025-02-03 21:28 3738
    {
1193b4be Poul Sander 2025-02-03 21:28 3739
        RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 3740
    }
1193b4be Poul Sander 2025-02-03 21:28 3741
1193b4be Poul Sander 2025-02-03 21:28 3742
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 3743
        LogRenderedText(&pos_min, text, text_end_full);
1193b4be Poul Sander 2025-02-03 21:28 3744
}
1193b4be Poul Sander 2025-02-03 21:28 3745
1193b4be Poul Sander 2025-02-03 21:28 3746
// Render a rectangle shaped with optional rounding and borders
1193b4be Poul Sander 2025-02-03 21:28 3747
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
1193b4be Poul Sander 2025-02-03 21:28 3748
{
1193b4be Poul Sander 2025-02-03 21:28 3749
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3750
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3751
    window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
1193b4be Poul Sander 2025-02-03 21:28 3752
    const float border_size = g.Style.FrameBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 3753
    if (borders && border_size > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 3754
    {
1193b4be Poul Sander 2025-02-03 21:28 3755
        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
1193b4be Poul Sander 2025-02-03 21:28 3756
        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
1193b4be Poul Sander 2025-02-03 21:28 3757
    }
1193b4be Poul Sander 2025-02-03 21:28 3758
}
1193b4be Poul Sander 2025-02-03 21:28 3759
1193b4be Poul Sander 2025-02-03 21:28 3760
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
1193b4be Poul Sander 2025-02-03 21:28 3761
{
1193b4be Poul Sander 2025-02-03 21:28 3762
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3763
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3764
    const float border_size = g.Style.FrameBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 3765
    if (border_size > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 3766
    {
1193b4be Poul Sander 2025-02-03 21:28 3767
        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
1193b4be Poul Sander 2025-02-03 21:28 3768
        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
1193b4be Poul Sander 2025-02-03 21:28 3769
    }
1193b4be Poul Sander 2025-02-03 21:28 3770
}
1193b4be Poul Sander 2025-02-03 21:28 3771
1193b4be Poul Sander 2025-02-03 21:28 3772
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 3773
{
1193b4be Poul Sander 2025-02-03 21:28 3774
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3775
    if (id != g.NavId)
1193b4be Poul Sander 2025-02-03 21:28 3776
        return;
1193b4be Poul Sander 2025-02-03 21:28 3777
    if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
1193b4be Poul Sander 2025-02-03 21:28 3778
        return;
1193b4be Poul Sander 2025-02-03 21:28 3779
    if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
1193b4be Poul Sander 2025-02-03 21:28 3780
        return;
1193b4be Poul Sander 2025-02-03 21:28 3781
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 3782
    if (window->DC.NavHideHighlightOneFrame)
1193b4be Poul Sander 2025-02-03 21:28 3783
        return;
1193b4be Poul Sander 2025-02-03 21:28 3784
1193b4be Poul Sander 2025-02-03 21:28 3785
    float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
1193b4be Poul Sander 2025-02-03 21:28 3786
    ImRect display_rect = bb;
1193b4be Poul Sander 2025-02-03 21:28 3787
    display_rect.ClipWith(window->ClipRect);
1193b4be Poul Sander 2025-02-03 21:28 3788
    const float thickness = 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 3789
    if (flags & ImGuiNavRenderCursorFlags_Compact)
1193b4be Poul Sander 2025-02-03 21:28 3790
    {
1193b4be Poul Sander 2025-02-03 21:28 3791
        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
1193b4be Poul Sander 2025-02-03 21:28 3792
    }
1193b4be Poul Sander 2025-02-03 21:28 3793
    else
1193b4be Poul Sander 2025-02-03 21:28 3794
    {
1193b4be Poul Sander 2025-02-03 21:28 3795
        const float distance = 3.0f + thickness * 0.5f;
1193b4be Poul Sander 2025-02-03 21:28 3796
        display_rect.Expand(ImVec2(distance, distance));
1193b4be Poul Sander 2025-02-03 21:28 3797
        bool fully_visible = window->ClipRect.Contains(display_rect);
1193b4be Poul Sander 2025-02-03 21:28 3798
        if (!fully_visible)
1193b4be Poul Sander 2025-02-03 21:28 3799
            window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
1193b4be Poul Sander 2025-02-03 21:28 3800
        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
1193b4be Poul Sander 2025-02-03 21:28 3801
        if (!fully_visible)
1193b4be Poul Sander 2025-02-03 21:28 3802
            window->DrawList->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 3803
    }
1193b4be Poul Sander 2025-02-03 21:28 3804
}
1193b4be Poul Sander 2025-02-03 21:28 3805
1193b4be Poul Sander 2025-02-03 21:28 3806
void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
1193b4be Poul Sander 2025-02-03 21:28 3807
{
1193b4be Poul Sander 2025-02-03 21:28 3808
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3809
    if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
1193b4be Poul Sander 2025-02-03 21:28 3810
        mouse_cursor = ImGuiMouseCursor_Arrow;
1193b4be Poul Sander 2025-02-03 21:28 3811
    ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
1193b4be Poul Sander 2025-02-03 21:28 3812
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 3813
    {
1193b4be Poul Sander 2025-02-03 21:28 3814
        // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
1193b4be Poul Sander 2025-02-03 21:28 3815
        ImVec2 offset, size, uv[4];
1193b4be Poul Sander 2025-02-03 21:28 3816
        if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
1193b4be Poul Sander 2025-02-03 21:28 3817
            continue;
1193b4be Poul Sander 2025-02-03 21:28 3818
        const ImVec2 pos = base_pos - offset;
1193b4be Poul Sander 2025-02-03 21:28 3819
        const float scale = base_scale * viewport->DpiScale;
1193b4be Poul Sander 2025-02-03 21:28 3820
        if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
1193b4be Poul Sander 2025-02-03 21:28 3821
            continue;
1193b4be Poul Sander 2025-02-03 21:28 3822
        ImDrawList* draw_list = GetForegroundDrawList(viewport);
1193b4be Poul Sander 2025-02-03 21:28 3823
        ImTextureID tex_id = font_atlas->TexID;
1193b4be Poul Sander 2025-02-03 21:28 3824
        draw_list->PushTextureID(tex_id);
1193b4be Poul Sander 2025-02-03 21:28 3825
        draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
1193b4be Poul Sander 2025-02-03 21:28 3826
        draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
1193b4be Poul Sander 2025-02-03 21:28 3827
        draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[2], uv[3], col_border);
1193b4be Poul Sander 2025-02-03 21:28 3828
        draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[0], uv[1], col_fill);
1193b4be Poul Sander 2025-02-03 21:28 3829
        draw_list->PopTextureID();
1193b4be Poul Sander 2025-02-03 21:28 3830
    }
1193b4be Poul Sander 2025-02-03 21:28 3831
}
1193b4be Poul Sander 2025-02-03 21:28 3832
1193b4be Poul Sander 2025-02-03 21:28 3833
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3834
// [SECTION] INITIALIZATION, SHUTDOWN
1193b4be Poul Sander 2025-02-03 21:28 3835
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 3836
1193b4be Poul Sander 2025-02-03 21:28 3837
// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
1193b4be Poul Sander 2025-02-03 21:28 3838
// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
1193b4be Poul Sander 2025-02-03 21:28 3839
ImGuiContext* ImGui::GetCurrentContext()
1193b4be Poul Sander 2025-02-03 21:28 3840
{
1193b4be Poul Sander 2025-02-03 21:28 3841
    return GImGui;
1193b4be Poul Sander 2025-02-03 21:28 3842
}
1193b4be Poul Sander 2025-02-03 21:28 3843
1193b4be Poul Sander 2025-02-03 21:28 3844
void ImGui::SetCurrentContext(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 3845
{
1193b4be Poul Sander 2025-02-03 21:28 3846
#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
1193b4be Poul Sander 2025-02-03 21:28 3847
    IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
1193b4be Poul Sander 2025-02-03 21:28 3848
#else
1193b4be Poul Sander 2025-02-03 21:28 3849
    GImGui = ctx;
1193b4be Poul Sander 2025-02-03 21:28 3850
#endif
1193b4be Poul Sander 2025-02-03 21:28 3851
}
1193b4be Poul Sander 2025-02-03 21:28 3852
1193b4be Poul Sander 2025-02-03 21:28 3853
void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
1193b4be Poul Sander 2025-02-03 21:28 3854
{
1193b4be Poul Sander 2025-02-03 21:28 3855
    GImAllocatorAllocFunc = alloc_func;
1193b4be Poul Sander 2025-02-03 21:28 3856
    GImAllocatorFreeFunc = free_func;
1193b4be Poul Sander 2025-02-03 21:28 3857
    GImAllocatorUserData = user_data;
1193b4be Poul Sander 2025-02-03 21:28 3858
}
1193b4be Poul Sander 2025-02-03 21:28 3859
1193b4be Poul Sander 2025-02-03 21:28 3860
// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
1193b4be Poul Sander 2025-02-03 21:28 3861
void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
1193b4be Poul Sander 2025-02-03 21:28 3862
{
1193b4be Poul Sander 2025-02-03 21:28 3863
    *p_alloc_func = GImAllocatorAllocFunc;
1193b4be Poul Sander 2025-02-03 21:28 3864
    *p_free_func = GImAllocatorFreeFunc;
1193b4be Poul Sander 2025-02-03 21:28 3865
    *p_user_data = GImAllocatorUserData;
1193b4be Poul Sander 2025-02-03 21:28 3866
}
1193b4be Poul Sander 2025-02-03 21:28 3867
1193b4be Poul Sander 2025-02-03 21:28 3868
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
1193b4be Poul Sander 2025-02-03 21:28 3869
{
1193b4be Poul Sander 2025-02-03 21:28 3870
    ImGuiContext* prev_ctx = GetCurrentContext();
1193b4be Poul Sander 2025-02-03 21:28 3871
    ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
1193b4be Poul Sander 2025-02-03 21:28 3872
    SetCurrentContext(ctx);
1193b4be Poul Sander 2025-02-03 21:28 3873
    Initialize();
1193b4be Poul Sander 2025-02-03 21:28 3874
    if (prev_ctx != NULL)
1193b4be Poul Sander 2025-02-03 21:28 3875
        SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
1193b4be Poul Sander 2025-02-03 21:28 3876
    return ctx;
1193b4be Poul Sander 2025-02-03 21:28 3877
}
1193b4be Poul Sander 2025-02-03 21:28 3878
1193b4be Poul Sander 2025-02-03 21:28 3879
void ImGui::DestroyContext(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 3880
{
1193b4be Poul Sander 2025-02-03 21:28 3881
    ImGuiContext* prev_ctx = GetCurrentContext();
1193b4be Poul Sander 2025-02-03 21:28 3882
    if (ctx == NULL) //-V1051
1193b4be Poul Sander 2025-02-03 21:28 3883
        ctx = prev_ctx;
1193b4be Poul Sander 2025-02-03 21:28 3884
    SetCurrentContext(ctx);
1193b4be Poul Sander 2025-02-03 21:28 3885
    Shutdown();
1193b4be Poul Sander 2025-02-03 21:28 3886
    SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
1193b4be Poul Sander 2025-02-03 21:28 3887
    IM_DELETE(ctx);
1193b4be Poul Sander 2025-02-03 21:28 3888
}
1193b4be Poul Sander 2025-02-03 21:28 3889
1193b4be Poul Sander 2025-02-03 21:28 3890
// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
1193b4be Poul Sander 2025-02-03 21:28 3891
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
1193b4be Poul Sander 2025-02-03 21:28 3892
{
1193b4be Poul Sander 2025-02-03 21:28 3893
    { ImGuiLocKey_VersionStr,           "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
1193b4be Poul Sander 2025-02-03 21:28 3894
    { ImGuiLocKey_TableSizeOne,         "Size column to fit###SizeOne"          },
1193b4be Poul Sander 2025-02-03 21:28 3895
    { ImGuiLocKey_TableSizeAllFit,      "Size all columns to fit###SizeAll"     },
1193b4be Poul Sander 2025-02-03 21:28 3896
    { ImGuiLocKey_TableSizeAllDefault,  "Size all columns to default###SizeAll" },
1193b4be Poul Sander 2025-02-03 21:28 3897
    { ImGuiLocKey_TableResetOrder,      "Reset order###ResetOrder"              },
1193b4be Poul Sander 2025-02-03 21:28 3898
    { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)"                       },
1193b4be Poul Sander 2025-02-03 21:28 3899
    { ImGuiLocKey_WindowingPopup,       "(Popup)"                               },
1193b4be Poul Sander 2025-02-03 21:28 3900
    { ImGuiLocKey_WindowingUntitled,    "(Untitled)"                            },
1193b4be Poul Sander 2025-02-03 21:28 3901
    { ImGuiLocKey_OpenLink_s,           "Open '%s'"                             },
1193b4be Poul Sander 2025-02-03 21:28 3902
    { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                  },
1193b4be Poul Sander 2025-02-03 21:28 3903
    { ImGuiLocKey_DockingHideTabBar,            "Hide tab bar###HideTabBar"             },
1193b4be Poul Sander 2025-02-03 21:28 3904
    { ImGuiLocKey_DockingHoldShiftToDock,       "Hold SHIFT to enable Docking window."  },
1193b4be Poul Sander 2025-02-03 21:28 3905
    { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node."    },
1193b4be Poul Sander 2025-02-03 21:28 3906
};
1193b4be Poul Sander 2025-02-03 21:28 3907
1193b4be Poul Sander 2025-02-03 21:28 3908
ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
1193b4be Poul Sander 2025-02-03 21:28 3909
{
1193b4be Poul Sander 2025-02-03 21:28 3910
    IO.Ctx = this;
1193b4be Poul Sander 2025-02-03 21:28 3911
    InputTextState.Ctx = this;
1193b4be Poul Sander 2025-02-03 21:28 3912
1193b4be Poul Sander 2025-02-03 21:28 3913
    Initialized = false;
1193b4be Poul Sander 2025-02-03 21:28 3914
    ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 3915
    FontAtlasOwnedByContext = shared_font_atlas ? false : true;
1193b4be Poul Sander 2025-02-03 21:28 3916
    Font = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3917
    FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3918
    IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
1193b4be Poul Sander 2025-02-03 21:28 3919
    Time = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3920
    FrameCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 3921
    FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1;
1193b4be Poul Sander 2025-02-03 21:28 3922
    WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
1193b4be Poul Sander 2025-02-03 21:28 3923
    GcCompactAll = false;
1193b4be Poul Sander 2025-02-03 21:28 3924
    TestEngineHookItems = false;
1193b4be Poul Sander 2025-02-03 21:28 3925
    TestEngine = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3926
    memset(ContextName, 0, sizeof(ContextName));
1193b4be Poul Sander 2025-02-03 21:28 3927
1193b4be Poul Sander 2025-02-03 21:28 3928
    InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 3929
    InputEventsNextEventId = 1;
1193b4be Poul Sander 2025-02-03 21:28 3930
1193b4be Poul Sander 2025-02-03 21:28 3931
    WindowsActiveCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 3932
    CurrentWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3933
    HoveredWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3934
    HoveredWindowUnderMovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3935
    HoveredWindowBeforeClear = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3936
    MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3937
    WheelingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3938
    WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
1193b4be Poul Sander 2025-02-03 21:28 3939
    WheelingWindowReleaseTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3940
1193b4be Poul Sander 2025-02-03 21:28 3941
    DebugDrawIdConflicts = 0;
1193b4be Poul Sander 2025-02-03 21:28 3942
    DebugHookIdInfo = 0;
1193b4be Poul Sander 2025-02-03 21:28 3943
    HoveredId = HoveredIdPreviousFrame = 0;
1193b4be Poul Sander 2025-02-03 21:28 3944
    HoveredIdPreviousFrameItemCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 3945
    HoveredIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 3946
    HoveredIdIsDisabled = false;
1193b4be Poul Sander 2025-02-03 21:28 3947
    HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3948
    ItemUnclipByLog = false;
1193b4be Poul Sander 2025-02-03 21:28 3949
    ActiveId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3950
    ActiveIdIsAlive = 0;
1193b4be Poul Sander 2025-02-03 21:28 3951
    ActiveIdTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3952
    ActiveIdIsJustActivated = false;
1193b4be Poul Sander 2025-02-03 21:28 3953
    ActiveIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 3954
    ActiveIdNoClearOnFocusLoss = false;
1193b4be Poul Sander 2025-02-03 21:28 3955
    ActiveIdHasBeenPressedBefore = false;
1193b4be Poul Sander 2025-02-03 21:28 3956
    ActiveIdHasBeenEditedBefore = false;
1193b4be Poul Sander 2025-02-03 21:28 3957
    ActiveIdHasBeenEditedThisFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 3958
    ActiveIdFromShortcut = false;
1193b4be Poul Sander 2025-02-03 21:28 3959
    ActiveIdClickOffset = ImVec2(-1, -1);
1193b4be Poul Sander 2025-02-03 21:28 3960
    ActiveIdWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3961
    ActiveIdSource = ImGuiInputSource_None;
1193b4be Poul Sander 2025-02-03 21:28 3962
    ActiveIdMouseButton = -1;
1193b4be Poul Sander 2025-02-03 21:28 3963
    ActiveIdPreviousFrame = 0;
1193b4be Poul Sander 2025-02-03 21:28 3964
    ActiveIdPreviousFrameIsAlive = false;
1193b4be Poul Sander 2025-02-03 21:28 3965
    ActiveIdPreviousFrameHasBeenEditedBefore = false;
1193b4be Poul Sander 2025-02-03 21:28 3966
    ActiveIdPreviousFrameWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3967
    LastActiveId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3968
    LastActiveIdTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3969
1193b4be Poul Sander 2025-02-03 21:28 3970
    LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
1193b4be Poul Sander 2025-02-03 21:28 3971
1193b4be Poul Sander 2025-02-03 21:28 3972
    ActiveIdUsingNavDirMask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 3973
    ActiveIdUsingAllKeyboardKeys = false;
1193b4be Poul Sander 2025-02-03 21:28 3974
1193b4be Poul Sander 2025-02-03 21:28 3975
    CurrentFocusScopeId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3976
    CurrentItemFlags = ImGuiItemFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 3977
    DebugShowGroupRects = false;
1193b4be Poul Sander 2025-02-03 21:28 3978
1193b4be Poul Sander 2025-02-03 21:28 3979
    CurrentViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3980
    MouseViewport = MouseLastHoveredViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3981
    PlatformLastFocusedViewportId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3982
    ViewportCreatedCount = PlatformWindowsCreatedCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 3983
    ViewportFocusedStampCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 3984
1193b4be Poul Sander 2025-02-03 21:28 3985
    NavCursorVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 3986
    NavHighlightItemUnderNav = false;
1193b4be Poul Sander 2025-02-03 21:28 3987
    NavMousePosDirty = false;
1193b4be Poul Sander 2025-02-03 21:28 3988
    NavIdIsAlive = false;
1193b4be Poul Sander 2025-02-03 21:28 3989
    NavId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3990
    NavWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 3991
    NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3992
    NavLayer = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 3993
    NavNextActivateId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3994
    NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 3995
    NavHighlightActivatedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 3996
    NavHighlightActivatedTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 3997
    NavInputSource = ImGuiInputSource_Keyboard;
1193b4be Poul Sander 2025-02-03 21:28 3998
    NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
1193b4be Poul Sander 2025-02-03 21:28 3999
    NavCursorHideFrames = 0;
1193b4be Poul Sander 2025-02-03 21:28 4000
1193b4be Poul Sander 2025-02-03 21:28 4001
    NavAnyRequest = false;
1193b4be Poul Sander 2025-02-03 21:28 4002
    NavInitRequest = false;
1193b4be Poul Sander 2025-02-03 21:28 4003
    NavInitRequestFromMove = false;
1193b4be Poul Sander 2025-02-03 21:28 4004
    NavMoveSubmitted = false;
1193b4be Poul Sander 2025-02-03 21:28 4005
    NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 4006
    NavMoveForwardToNextFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 4007
    NavMoveFlags = ImGuiNavMoveFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 4008
    NavMoveScrollFlags = ImGuiScrollFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 4009
    NavMoveKeyMods = ImGuiMod_None;
1193b4be Poul Sander 2025-02-03 21:28 4010
    NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 4011
    NavScoringDebugCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 4012
    NavTabbingDir = 0;
1193b4be Poul Sander 2025-02-03 21:28 4013
    NavTabbingCounter = 0;
1193b4be Poul Sander 2025-02-03 21:28 4014
1193b4be Poul Sander 2025-02-03 21:28 4015
    NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4016
    NavJustMovedToKeyMods = ImGuiMod_None;
1193b4be Poul Sander 2025-02-03 21:28 4017
    NavJustMovedToIsTabbing = false;
1193b4be Poul Sander 2025-02-03 21:28 4018
    NavJustMovedToHasSelectionData = false;
1193b4be Poul Sander 2025-02-03 21:28 4019
1193b4be Poul Sander 2025-02-03 21:28 4020
    // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
1193b4be Poul Sander 2025-02-03 21:28 4021
    // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
1193b4be Poul Sander 2025-02-03 21:28 4022
    ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
1193b4be Poul Sander 2025-02-03 21:28 4023
    ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
1193b4be Poul Sander 2025-02-03 21:28 4024
    NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4025
    NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4026
    NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 4027
    NavWindowingToggleKey = ImGuiKey_None;
1193b4be Poul Sander 2025-02-03 21:28 4028
1193b4be Poul Sander 2025-02-03 21:28 4029
    DimBgRatio = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4030
1193b4be Poul Sander 2025-02-03 21:28 4031
    DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
1193b4be Poul Sander 2025-02-03 21:28 4032
    DragDropSourceFlags = ImGuiDragDropFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 4033
    DragDropSourceFrameCount = -1;
1193b4be Poul Sander 2025-02-03 21:28 4034
    DragDropMouseButton = -1;
1193b4be Poul Sander 2025-02-03 21:28 4035
    DragDropTargetId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4036
    DragDropAcceptFlags = ImGuiDragDropFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 4037
    DragDropAcceptIdCurrRectSurface = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4038
    DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
1193b4be Poul Sander 2025-02-03 21:28 4039
    DragDropAcceptFrameCount = -1;
1193b4be Poul Sander 2025-02-03 21:28 4040
    DragDropHoldJustPressedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4041
    memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
1193b4be Poul Sander 2025-02-03 21:28 4042
1193b4be Poul Sander 2025-02-03 21:28 4043
    ClipperTempDataStacked = 0;
1193b4be Poul Sander 2025-02-03 21:28 4044
1193b4be Poul Sander 2025-02-03 21:28 4045
    CurrentTable = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4046
    TablesTempDataStacked = 0;
1193b4be Poul Sander 2025-02-03 21:28 4047
    CurrentTabBar = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4048
    CurrentMultiSelect = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4049
    MultiSelectTempDataStacked = 0;
1193b4be Poul Sander 2025-02-03 21:28 4050
1193b4be Poul Sander 2025-02-03 21:28 4051
    HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4052
    HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4053
1193b4be Poul Sander 2025-02-03 21:28 4054
    MouseCursor = ImGuiMouseCursor_Arrow;
1193b4be Poul Sander 2025-02-03 21:28 4055
    MouseStationaryTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4056
1193b4be Poul Sander 2025-02-03 21:28 4057
    TempInputId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4058
    memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
1193b4be Poul Sander 2025-02-03 21:28 4059
    BeginMenuDepth = BeginComboDepth = 0;
1193b4be Poul Sander 2025-02-03 21:28 4060
    ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
1193b4be Poul Sander 2025-02-03 21:28 4061
    ColorEditCurrentID = ColorEditSavedID = 0;
1193b4be Poul Sander 2025-02-03 21:28 4062
    ColorEditSavedHue = ColorEditSavedSat = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4063
    ColorEditSavedColor = 0;
1193b4be Poul Sander 2025-02-03 21:28 4064
    WindowResizeRelativeMode = false;
1193b4be Poul Sander 2025-02-03 21:28 4065
    ScrollbarSeekMode = 0;
1193b4be Poul Sander 2025-02-03 21:28 4066
    ScrollbarClickDeltaToGrabCenter = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4067
    SliderGrabClickOffset = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4068
    SliderCurrentAccum = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4069
    SliderCurrentAccumDirty = false;
1193b4be Poul Sander 2025-02-03 21:28 4070
    DragCurrentAccumDirty = false;
1193b4be Poul Sander 2025-02-03 21:28 4071
    DragCurrentAccum = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4072
    DragSpeedDefaultRatio = 1.0f / 100.0f;
1193b4be Poul Sander 2025-02-03 21:28 4073
    DisabledAlphaBackup = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4074
    DisabledStackSize = 0;
1193b4be Poul Sander 2025-02-03 21:28 4075
    TooltipOverrideCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 4076
    TooltipPreviousWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4077
1193b4be Poul Sander 2025-02-03 21:28 4078
    PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 4079
    PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
1193b4be Poul Sander 2025-02-03 21:28 4080
    PlatformImeViewport = 0;
1193b4be Poul Sander 2025-02-03 21:28 4081
1193b4be Poul Sander 2025-02-03 21:28 4082
    DockNodeWindowMenuHandler = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4083
1193b4be Poul Sander 2025-02-03 21:28 4084
    SettingsLoaded = false;
1193b4be Poul Sander 2025-02-03 21:28 4085
    SettingsDirtyTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4086
    HookIdNext = 0;
1193b4be Poul Sander 2025-02-03 21:28 4087
1193b4be Poul Sander 2025-02-03 21:28 4088
    memset(LocalizationTable, 0, sizeof(LocalizationTable));
1193b4be Poul Sander 2025-02-03 21:28 4089
1193b4be Poul Sander 2025-02-03 21:28 4090
    LogEnabled = false;
1193b4be Poul Sander 2025-02-03 21:28 4091
    LogType = ImGuiLogType_None;
1193b4be Poul Sander 2025-02-03 21:28 4092
    LogNextPrefix = LogNextSuffix = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4093
    LogFile = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4094
    LogLinePosY = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 4095
    LogLineFirstItem = false;
1193b4be Poul Sander 2025-02-03 21:28 4096
    LogDepthRef = 0;
1193b4be Poul Sander 2025-02-03 21:28 4097
    LogDepthToExpand = LogDepthToExpandDefault = 2;
1193b4be Poul Sander 2025-02-03 21:28 4098
1193b4be Poul Sander 2025-02-03 21:28 4099
    ErrorCallback = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4100
    ErrorCallbackUserData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4101
    ErrorFirst = true;
1193b4be Poul Sander 2025-02-03 21:28 4102
    ErrorCountCurrentFrame = 0;
1193b4be Poul Sander 2025-02-03 21:28 4103
    StackSizesInBeginForCurrentWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4104
1193b4be Poul Sander 2025-02-03 21:28 4105
    DebugDrawIdConflictsCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 4106
    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
1193b4be Poul Sander 2025-02-03 21:28 4107
    DebugLocateId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4108
    DebugLogSkippedErrors = 0;
1193b4be Poul Sander 2025-02-03 21:28 4109
    DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 4110
    DebugLogAutoDisableFrames = 0;
1193b4be Poul Sander 2025-02-03 21:28 4111
    DebugLocateFrames = 0;
1193b4be Poul Sander 2025-02-03 21:28 4112
    DebugBeginReturnValueCullDepth = -1;
1193b4be Poul Sander 2025-02-03 21:28 4113
    DebugItemPickerActive = false;
1193b4be Poul Sander 2025-02-03 21:28 4114
    DebugItemPickerMouseButton = ImGuiMouseButton_Left;
1193b4be Poul Sander 2025-02-03 21:28 4115
    DebugItemPickerBreakId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4116
    DebugFlashStyleColorTime = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4117
    DebugFlashStyleColorIdx = ImGuiCol_COUNT;
1193b4be Poul Sander 2025-02-03 21:28 4118
    DebugHoveredDockNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4119
1193b4be Poul Sander 2025-02-03 21:28 4120
    // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
1193b4be Poul Sander 2025-02-03 21:28 4121
    DebugBreakInWindow = 0;
1193b4be Poul Sander 2025-02-03 21:28 4122
    DebugBreakInTable = 0;
1193b4be Poul Sander 2025-02-03 21:28 4123
    DebugBreakInLocateId = false;
1193b4be Poul Sander 2025-02-03 21:28 4124
    DebugBreakKeyChord = ImGuiKey_Pause;
1193b4be Poul Sander 2025-02-03 21:28 4125
    DebugBreakInShortcutRouting = ImGuiKey_None;
1193b4be Poul Sander 2025-02-03 21:28 4126
1193b4be Poul Sander 2025-02-03 21:28 4127
    memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
1193b4be Poul Sander 2025-02-03 21:28 4128
    FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 4129
    FramerateSecPerFrameAccum = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4130
    WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
1193b4be Poul Sander 2025-02-03 21:28 4131
    memset(TempKeychordName, 0, sizeof(TempKeychordName));
1193b4be Poul Sander 2025-02-03 21:28 4132
}
1193b4be Poul Sander 2025-02-03 21:28 4133
1193b4be Poul Sander 2025-02-03 21:28 4134
void ImGui::Initialize()
1193b4be Poul Sander 2025-02-03 21:28 4135
{
1193b4be Poul Sander 2025-02-03 21:28 4136
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4137
    IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
1193b4be Poul Sander 2025-02-03 21:28 4138
1193b4be Poul Sander 2025-02-03 21:28 4139
    // Add .ini handle for ImGuiWindow and ImGuiTable types
1193b4be Poul Sander 2025-02-03 21:28 4140
    {
1193b4be Poul Sander 2025-02-03 21:28 4141
        ImGuiSettingsHandler ini_handler;
1193b4be Poul Sander 2025-02-03 21:28 4142
        ini_handler.TypeName = "Window";
1193b4be Poul Sander 2025-02-03 21:28 4143
        ini_handler.TypeHash = ImHashStr("Window");
1193b4be Poul Sander 2025-02-03 21:28 4144
        ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
1193b4be Poul Sander 2025-02-03 21:28 4145
        ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
1193b4be Poul Sander 2025-02-03 21:28 4146
        ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
1193b4be Poul Sander 2025-02-03 21:28 4147
        ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
1193b4be Poul Sander 2025-02-03 21:28 4148
        ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
1193b4be Poul Sander 2025-02-03 21:28 4149
        AddSettingsHandler(&ini_handler);
1193b4be Poul Sander 2025-02-03 21:28 4150
    }
1193b4be Poul Sander 2025-02-03 21:28 4151
    TableSettingsAddSettingsHandler();
1193b4be Poul Sander 2025-02-03 21:28 4152
1193b4be Poul Sander 2025-02-03 21:28 4153
    // Setup default localization table
1193b4be Poul Sander 2025-02-03 21:28 4154
    LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
1193b4be Poul Sander 2025-02-03 21:28 4155
1193b4be Poul Sander 2025-02-03 21:28 4156
    // Setup default ImGuiPlatformIO clipboard/IME handlers.
1193b4be Poul Sander 2025-02-03 21:28 4157
    g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl;    // Platform dependent default implementations
1193b4be Poul Sander 2025-02-03 21:28 4158
    g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
1193b4be Poul Sander 2025-02-03 21:28 4159
    g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
1193b4be Poul Sander 2025-02-03 21:28 4160
    g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
1193b4be Poul Sander 2025-02-03 21:28 4161
1193b4be Poul Sander 2025-02-03 21:28 4162
    // Create default viewport
1193b4be Poul Sander 2025-02-03 21:28 4163
    ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
1193b4be Poul Sander 2025-02-03 21:28 4164
    viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
1193b4be Poul Sander 2025-02-03 21:28 4165
    viewport->Idx = 0;
1193b4be Poul Sander 2025-02-03 21:28 4166
    viewport->PlatformWindowCreated = true;
1193b4be Poul Sander 2025-02-03 21:28 4167
    viewport->Flags = ImGuiViewportFlags_OwnedByApp;
1193b4be Poul Sander 2025-02-03 21:28 4168
    g.Viewports.push_back(viewport);
1193b4be Poul Sander 2025-02-03 21:28 4169
    g.TempBuffer.resize(1024 * 3 + 1, 0);
1193b4be Poul Sander 2025-02-03 21:28 4170
    g.ViewportCreatedCount++;
1193b4be Poul Sander 2025-02-03 21:28 4171
    g.PlatformIO.Viewports.push_back(g.Viewports[0]);
1193b4be Poul Sander 2025-02-03 21:28 4172
1193b4be Poul Sander 2025-02-03 21:28 4173
    // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
1193b4be Poul Sander 2025-02-03 21:28 4174
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 4175
        if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
1193b4be Poul Sander 2025-02-03 21:28 4176
            || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
1193b4be Poul Sander 2025-02-03 21:28 4177
            || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
1193b4be Poul Sander 2025-02-03 21:28 4178
            || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
1193b4be Poul Sander 2025-02-03 21:28 4179
            g.KeysMayBeCharInput.SetBit(key);
1193b4be Poul Sander 2025-02-03 21:28 4180
1193b4be Poul Sander 2025-02-03 21:28 4181
#ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 4182
    // Initialize Docking
1193b4be Poul Sander 2025-02-03 21:28 4183
    DockContextInitialize(&g);
1193b4be Poul Sander 2025-02-03 21:28 4184
#endif
1193b4be Poul Sander 2025-02-03 21:28 4185
1193b4be Poul Sander 2025-02-03 21:28 4186
    g.Initialized = true;
1193b4be Poul Sander 2025-02-03 21:28 4187
}
1193b4be Poul Sander 2025-02-03 21:28 4188
1193b4be Poul Sander 2025-02-03 21:28 4189
// This function is merely here to free heap allocations.
1193b4be Poul Sander 2025-02-03 21:28 4190
void ImGui::Shutdown()
1193b4be Poul Sander 2025-02-03 21:28 4191
{
1193b4be Poul Sander 2025-02-03 21:28 4192
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4193
    IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
1193b4be Poul Sander 2025-02-03 21:28 4194
    IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
1193b4be Poul Sander 2025-02-03 21:28 4195
1193b4be Poul Sander 2025-02-03 21:28 4196
    // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
1193b4be Poul Sander 2025-02-03 21:28 4197
    if (g.IO.Fonts && g.FontAtlasOwnedByContext)
1193b4be Poul Sander 2025-02-03 21:28 4198
    {
1193b4be Poul Sander 2025-02-03 21:28 4199
        g.IO.Fonts->Locked = false;
1193b4be Poul Sander 2025-02-03 21:28 4200
        IM_DELETE(g.IO.Fonts);
1193b4be Poul Sander 2025-02-03 21:28 4201
    }
1193b4be Poul Sander 2025-02-03 21:28 4202
    g.IO.Fonts = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4203
    g.DrawListSharedData.TempBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 4204
1193b4be Poul Sander 2025-02-03 21:28 4205
    // Cleanup of other data are conditional on actually having initialized Dear ImGui.
1193b4be Poul Sander 2025-02-03 21:28 4206
    if (!g.Initialized)
1193b4be Poul Sander 2025-02-03 21:28 4207
        return;
1193b4be Poul Sander 2025-02-03 21:28 4208
1193b4be Poul Sander 2025-02-03 21:28 4209
    // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
1193b4be Poul Sander 2025-02-03 21:28 4210
    if (g.SettingsLoaded && g.IO.IniFilename != NULL)
1193b4be Poul Sander 2025-02-03 21:28 4211
        SaveIniSettingsToDisk(g.IO.IniFilename);
1193b4be Poul Sander 2025-02-03 21:28 4212
1193b4be Poul Sander 2025-02-03 21:28 4213
    // Destroy platform windows
1193b4be Poul Sander 2025-02-03 21:28 4214
    DestroyPlatformWindows();
1193b4be Poul Sander 2025-02-03 21:28 4215
1193b4be Poul Sander 2025-02-03 21:28 4216
    // Shutdown extensions
1193b4be Poul Sander 2025-02-03 21:28 4217
    DockContextShutdown(&g);
1193b4be Poul Sander 2025-02-03 21:28 4218
1193b4be Poul Sander 2025-02-03 21:28 4219
    CallContextHooks(&g, ImGuiContextHookType_Shutdown);
1193b4be Poul Sander 2025-02-03 21:28 4220
1193b4be Poul Sander 2025-02-03 21:28 4221
    // Clear everything else
1193b4be Poul Sander 2025-02-03 21:28 4222
    g.Windows.clear_delete();
1193b4be Poul Sander 2025-02-03 21:28 4223
    g.WindowsFocusOrder.clear();
1193b4be Poul Sander 2025-02-03 21:28 4224
    g.WindowsTempSortBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 4225
    g.CurrentWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4226
    g.CurrentWindowStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4227
    g.WindowsById.Clear();
1193b4be Poul Sander 2025-02-03 21:28 4228
    g.NavWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4229
    g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4230
    g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4231
    g.MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4232
1193b4be Poul Sander 2025-02-03 21:28 4233
    g.KeysRoutingTable.Clear();
1193b4be Poul Sander 2025-02-03 21:28 4234
1193b4be Poul Sander 2025-02-03 21:28 4235
    g.ColorStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4236
    g.StyleVarStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4237
    g.FontStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4238
    g.OpenPopupStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4239
    g.BeginPopupStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4240
    g.TreeNodeStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4241
1193b4be Poul Sander 2025-02-03 21:28 4242
    g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4243
    g.Viewports.clear_delete();
1193b4be Poul Sander 2025-02-03 21:28 4244
1193b4be Poul Sander 2025-02-03 21:28 4245
    g.TabBars.Clear();
1193b4be Poul Sander 2025-02-03 21:28 4246
    g.CurrentTabBarStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4247
    g.ShrinkWidthBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 4248
1193b4be Poul Sander 2025-02-03 21:28 4249
    g.ClipperTempData.clear_destruct();
1193b4be Poul Sander 2025-02-03 21:28 4250
1193b4be Poul Sander 2025-02-03 21:28 4251
    g.Tables.Clear();
1193b4be Poul Sander 2025-02-03 21:28 4252
    g.TablesTempData.clear_destruct();
1193b4be Poul Sander 2025-02-03 21:28 4253
    g.DrawChannelsTempMergeBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 4254
1193b4be Poul Sander 2025-02-03 21:28 4255
    g.MultiSelectStorage.Clear();
1193b4be Poul Sander 2025-02-03 21:28 4256
    g.MultiSelectTempData.clear_destruct();
1193b4be Poul Sander 2025-02-03 21:28 4257
1193b4be Poul Sander 2025-02-03 21:28 4258
    g.ClipboardHandlerData.clear();
1193b4be Poul Sander 2025-02-03 21:28 4259
    g.MenusIdSubmittedThisFrame.clear();
1193b4be Poul Sander 2025-02-03 21:28 4260
    g.InputTextState.ClearFreeMemory();
1193b4be Poul Sander 2025-02-03 21:28 4261
    g.InputTextDeactivatedState.ClearFreeMemory();
1193b4be Poul Sander 2025-02-03 21:28 4262
1193b4be Poul Sander 2025-02-03 21:28 4263
    g.SettingsWindows.clear();
1193b4be Poul Sander 2025-02-03 21:28 4264
    g.SettingsHandlers.clear();
1193b4be Poul Sander 2025-02-03 21:28 4265
1193b4be Poul Sander 2025-02-03 21:28 4266
    if (g.LogFile)
1193b4be Poul Sander 2025-02-03 21:28 4267
    {
1193b4be Poul Sander 2025-02-03 21:28 4268
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 4269
        if (g.LogFile != stdout)
1193b4be Poul Sander 2025-02-03 21:28 4270
#endif
1193b4be Poul Sander 2025-02-03 21:28 4271
            ImFileClose(g.LogFile);
1193b4be Poul Sander 2025-02-03 21:28 4272
        g.LogFile = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4273
    }
1193b4be Poul Sander 2025-02-03 21:28 4274
    g.LogBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 4275
    g.DebugLogBuf.clear();
1193b4be Poul Sander 2025-02-03 21:28 4276
    g.DebugLogIndex.clear();
1193b4be Poul Sander 2025-02-03 21:28 4277
1193b4be Poul Sander 2025-02-03 21:28 4278
    g.Initialized = false;
1193b4be Poul Sander 2025-02-03 21:28 4279
}
1193b4be Poul Sander 2025-02-03 21:28 4280
1193b4be Poul Sander 2025-02-03 21:28 4281
// No specific ordering/dependency support, will see as needed
1193b4be Poul Sander 2025-02-03 21:28 4282
ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
1193b4be Poul Sander 2025-02-03 21:28 4283
{
1193b4be Poul Sander 2025-02-03 21:28 4284
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 4285
    IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
1193b4be Poul Sander 2025-02-03 21:28 4286
    g.Hooks.push_back(*hook);
1193b4be Poul Sander 2025-02-03 21:28 4287
    g.Hooks.back().HookId = ++g.HookIdNext;
1193b4be Poul Sander 2025-02-03 21:28 4288
    return g.HookIdNext;
1193b4be Poul Sander 2025-02-03 21:28 4289
}
1193b4be Poul Sander 2025-02-03 21:28 4290
1193b4be Poul Sander 2025-02-03 21:28 4291
// Deferred removal, avoiding issue with changing vector while iterating it
1193b4be Poul Sander 2025-02-03 21:28 4292
void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
1193b4be Poul Sander 2025-02-03 21:28 4293
{
1193b4be Poul Sander 2025-02-03 21:28 4294
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 4295
    IM_ASSERT(hook_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 4296
    for (ImGuiContextHook& hook : g.Hooks)
1193b4be Poul Sander 2025-02-03 21:28 4297
        if (hook.HookId == hook_id)
1193b4be Poul Sander 2025-02-03 21:28 4298
            hook.Type = ImGuiContextHookType_PendingRemoval_;
1193b4be Poul Sander 2025-02-03 21:28 4299
}
1193b4be Poul Sander 2025-02-03 21:28 4300
1193b4be Poul Sander 2025-02-03 21:28 4301
// Call context hooks (used by e.g. test engine)
1193b4be Poul Sander 2025-02-03 21:28 4302
// We assume a small number of hooks so all stored in same array
1193b4be Poul Sander 2025-02-03 21:28 4303
void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
1193b4be Poul Sander 2025-02-03 21:28 4304
{
1193b4be Poul Sander 2025-02-03 21:28 4305
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 4306
    for (ImGuiContextHook& hook : g.Hooks)
1193b4be Poul Sander 2025-02-03 21:28 4307
        if (hook.Type == hook_type)
1193b4be Poul Sander 2025-02-03 21:28 4308
            hook.Callback(&g, &hook);
1193b4be Poul Sander 2025-02-03 21:28 4309
}
1193b4be Poul Sander 2025-02-03 21:28 4310
1193b4be Poul Sander 2025-02-03 21:28 4311
1193b4be Poul Sander 2025-02-03 21:28 4312
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 4313
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
1193b4be Poul Sander 2025-02-03 21:28 4314
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 4315
1193b4be Poul Sander 2025-02-03 21:28 4316
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
1193b4be Poul Sander 2025-02-03 21:28 4317
ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
1193b4be Poul Sander 2025-02-03 21:28 4318
{
1193b4be Poul Sander 2025-02-03 21:28 4319
    memset(this, 0, sizeof(*this));
1193b4be Poul Sander 2025-02-03 21:28 4320
    Ctx = ctx;
1193b4be Poul Sander 2025-02-03 21:28 4321
    Name = ImStrdup(name);
1193b4be Poul Sander 2025-02-03 21:28 4322
    NameBufLen = (int)strlen(name) + 1;
1193b4be Poul Sander 2025-02-03 21:28 4323
    ID = ImHashStr(name);
1193b4be Poul Sander 2025-02-03 21:28 4324
    IDStack.push_back(ID);
1193b4be Poul Sander 2025-02-03 21:28 4325
    ViewportAllowPlatformMonitorExtend = -1;
1193b4be Poul Sander 2025-02-03 21:28 4326
    ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 4327
    MoveId = GetID("#MOVE");
1193b4be Poul Sander 2025-02-03 21:28 4328
    TabId = GetID("#TAB");
1193b4be Poul Sander 2025-02-03 21:28 4329
    ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 4330
    ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 4331
    AutoFitFramesX = AutoFitFramesY = -1;
1193b4be Poul Sander 2025-02-03 21:28 4332
    AutoPosLastDirection = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 4333
    SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0;
1193b4be Poul Sander 2025-02-03 21:28 4334
    SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 4335
    LastFrameActive = -1;
1193b4be Poul Sander 2025-02-03 21:28 4336
    LastFrameJustFocused = -1;
1193b4be Poul Sander 2025-02-03 21:28 4337
    LastTimeActive = -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 4338
    FontWindowScale = FontDpiScale = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 4339
    SettingsOffset = -1;
1193b4be Poul Sander 2025-02-03 21:28 4340
    DockOrder = -1;
1193b4be Poul Sander 2025-02-03 21:28 4341
    DrawList = &DrawListInst;
1193b4be Poul Sander 2025-02-03 21:28 4342
    DrawList->_Data = &Ctx->DrawListSharedData;
1193b4be Poul Sander 2025-02-03 21:28 4343
    DrawList->_OwnerName = Name;
1193b4be Poul Sander 2025-02-03 21:28 4344
    NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 4345
    IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();
1193b4be Poul Sander 2025-02-03 21:28 4346
}
1193b4be Poul Sander 2025-02-03 21:28 4347
1193b4be Poul Sander 2025-02-03 21:28 4348
ImGuiWindow::~ImGuiWindow()
1193b4be Poul Sander 2025-02-03 21:28 4349
{
1193b4be Poul Sander 2025-02-03 21:28 4350
    IM_ASSERT(DrawList == &DrawListInst);
1193b4be Poul Sander 2025-02-03 21:28 4351
    IM_DELETE(Name);
1193b4be Poul Sander 2025-02-03 21:28 4352
    ColumnsStorage.clear_destruct();
1193b4be Poul Sander 2025-02-03 21:28 4353
}
1193b4be Poul Sander 2025-02-03 21:28 4354
1193b4be Poul Sander 2025-02-03 21:28 4355
static void SetCurrentWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 4356
{
1193b4be Poul Sander 2025-02-03 21:28 4357
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4358
    g.CurrentWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 4359
    g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
1193b4be Poul Sander 2025-02-03 21:28 4360
    g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 4361
    if (window)
1193b4be Poul Sander 2025-02-03 21:28 4362
    {
1193b4be Poul Sander 2025-02-03 21:28 4363
        g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
1193b4be Poul Sander 2025-02-03 21:28 4364
        g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
1193b4be Poul Sander 2025-02-03 21:28 4365
        ImGui::NavUpdateCurrentWindowIsScrollPushableX();
1193b4be Poul Sander 2025-02-03 21:28 4366
    }
1193b4be Poul Sander 2025-02-03 21:28 4367
}
1193b4be Poul Sander 2025-02-03 21:28 4368
1193b4be Poul Sander 2025-02-03 21:28 4369
void ImGui::GcCompactTransientMiscBuffers()
1193b4be Poul Sander 2025-02-03 21:28 4370
{
1193b4be Poul Sander 2025-02-03 21:28 4371
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4372
    g.ItemFlagsStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4373
    g.GroupStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4374
    g.MultiSelectTempDataStacked = 0;
1193b4be Poul Sander 2025-02-03 21:28 4375
    g.MultiSelectTempData.clear_destruct();
1193b4be Poul Sander 2025-02-03 21:28 4376
    TableGcCompactSettings();
1193b4be Poul Sander 2025-02-03 21:28 4377
}
1193b4be Poul Sander 2025-02-03 21:28 4378
1193b4be Poul Sander 2025-02-03 21:28 4379
// Free up/compact internal window buffers, we can use this when a window becomes unused.
1193b4be Poul Sander 2025-02-03 21:28 4380
// Not freed:
1193b4be Poul Sander 2025-02-03 21:28 4381
// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
1193b4be Poul Sander 2025-02-03 21:28 4382
// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
1193b4be Poul Sander 2025-02-03 21:28 4383
void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 4384
{
1193b4be Poul Sander 2025-02-03 21:28 4385
    window->MemoryCompacted = true;
1193b4be Poul Sander 2025-02-03 21:28 4386
    window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
1193b4be Poul Sander 2025-02-03 21:28 4387
    window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
1193b4be Poul Sander 2025-02-03 21:28 4388
    window->IDStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4389
    window->DrawList->_ClearFreeMemory();
1193b4be Poul Sander 2025-02-03 21:28 4390
    window->DC.ChildWindows.clear();
1193b4be Poul Sander 2025-02-03 21:28 4391
    window->DC.ItemWidthStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4392
    window->DC.TextWrapPosStack.clear();
1193b4be Poul Sander 2025-02-03 21:28 4393
}
1193b4be Poul Sander 2025-02-03 21:28 4394
1193b4be Poul Sander 2025-02-03 21:28 4395
void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 4396
{
1193b4be Poul Sander 2025-02-03 21:28 4397
    // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
1193b4be Poul Sander 2025-02-03 21:28 4398
    // The other buffers tends to amortize much faster.
1193b4be Poul Sander 2025-02-03 21:28 4399
    window->MemoryCompacted = false;
1193b4be Poul Sander 2025-02-03 21:28 4400
    window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
1193b4be Poul Sander 2025-02-03 21:28 4401
    window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
1193b4be Poul Sander 2025-02-03 21:28 4402
    window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
1193b4be Poul Sander 2025-02-03 21:28 4403
}
1193b4be Poul Sander 2025-02-03 21:28 4404
1193b4be Poul Sander 2025-02-03 21:28 4405
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 4406
{
1193b4be Poul Sander 2025-02-03 21:28 4407
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4408
1193b4be Poul Sander 2025-02-03 21:28 4409
    // Clear previous active id
1193b4be Poul Sander 2025-02-03 21:28 4410
    if (g.ActiveId != 0)
1193b4be Poul Sander 2025-02-03 21:28 4411
    {
1193b4be Poul Sander 2025-02-03 21:28 4412
        // While most behaved code would make an effort to not steal active id during window move/drag operations,
1193b4be Poul Sander 2025-02-03 21:28 4413
        // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
1193b4be Poul Sander 2025-02-03 21:28 4414
        // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
1193b4be Poul Sander 2025-02-03 21:28 4415
        if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
1193b4be Poul Sander 2025-02-03 21:28 4416
        {
1193b4be Poul Sander 2025-02-03 21:28 4417
            IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
1193b4be Poul Sander 2025-02-03 21:28 4418
            g.MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 4419
        }
1193b4be Poul Sander 2025-02-03 21:28 4420
1193b4be Poul Sander 2025-02-03 21:28 4421
        // This could be written in a more general way (e.g associate a hook to ActiveId),
1193b4be Poul Sander 2025-02-03 21:28 4422
        // but since this is currently quite an exception we'll leave it as is.
1193b4be Poul Sander 2025-02-03 21:28 4423
        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
1193b4be Poul Sander 2025-02-03 21:28 4424
        if (g.InputTextState.ID == g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 4425
            InputTextDeactivateHook(g.ActiveId);
1193b4be Poul Sander 2025-02-03 21:28 4426
    }
1193b4be Poul Sander 2025-02-03 21:28 4427
1193b4be Poul Sander 2025-02-03 21:28 4428
    // Set active id
1193b4be Poul Sander 2025-02-03 21:28 4429
    g.ActiveIdIsJustActivated = (g.ActiveId != id);
1193b4be Poul Sander 2025-02-03 21:28 4430
    if (g.ActiveIdIsJustActivated)
1193b4be Poul Sander 2025-02-03 21:28 4431
    {
1193b4be Poul Sander 2025-02-03 21:28 4432
        IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : "");
1193b4be Poul Sander 2025-02-03 21:28 4433
        g.ActiveIdTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4434
        g.ActiveIdHasBeenPressedBefore = false;
1193b4be Poul Sander 2025-02-03 21:28 4435
        g.ActiveIdHasBeenEditedBefore = false;
1193b4be Poul Sander 2025-02-03 21:28 4436
        g.ActiveIdMouseButton = -1;
1193b4be Poul Sander 2025-02-03 21:28 4437
        if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 4438
        {
1193b4be Poul Sander 2025-02-03 21:28 4439
            g.LastActiveId = id;
1193b4be Poul Sander 2025-02-03 21:28 4440
            g.LastActiveIdTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4441
        }
1193b4be Poul Sander 2025-02-03 21:28 4442
    }
1193b4be Poul Sander 2025-02-03 21:28 4443
    g.ActiveId = id;
1193b4be Poul Sander 2025-02-03 21:28 4444
    g.ActiveIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 4445
    g.ActiveIdNoClearOnFocusLoss = false;
1193b4be Poul Sander 2025-02-03 21:28 4446
    g.ActiveIdWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 4447
    g.ActiveIdHasBeenEditedThisFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 4448
    g.ActiveIdFromShortcut = false;
1193b4be Poul Sander 2025-02-03 21:28 4449
    if (id)
1193b4be Poul Sander 2025-02-03 21:28 4450
    {
1193b4be Poul Sander 2025-02-03 21:28 4451
        g.ActiveIdIsAlive = id;
1193b4be Poul Sander 2025-02-03 21:28 4452
        g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 4453
        IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
1193b4be Poul Sander 2025-02-03 21:28 4454
    }
1193b4be Poul Sander 2025-02-03 21:28 4455
1193b4be Poul Sander 2025-02-03 21:28 4456
    // Clear declaration of inputs claimed by the widget
1193b4be Poul Sander 2025-02-03 21:28 4457
    // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
1193b4be Poul Sander 2025-02-03 21:28 4458
    g.ActiveIdUsingNavDirMask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 4459
    g.ActiveIdUsingAllKeyboardKeys = false;
1193b4be Poul Sander 2025-02-03 21:28 4460
}
1193b4be Poul Sander 2025-02-03 21:28 4461
1193b4be Poul Sander 2025-02-03 21:28 4462
void ImGui::ClearActiveID()
1193b4be Poul Sander 2025-02-03 21:28 4463
{
1193b4be Poul Sander 2025-02-03 21:28 4464
    SetActiveID(0, NULL); // g.ActiveId = 0;
1193b4be Poul Sander 2025-02-03 21:28 4465
}
1193b4be Poul Sander 2025-02-03 21:28 4466
1193b4be Poul Sander 2025-02-03 21:28 4467
void ImGui::SetHoveredID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 4468
{
1193b4be Poul Sander 2025-02-03 21:28 4469
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4470
    g.HoveredId = id;
1193b4be Poul Sander 2025-02-03 21:28 4471
    g.HoveredIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 4472
    if (id != 0 && g.HoveredIdPreviousFrame != id)
1193b4be Poul Sander 2025-02-03 21:28 4473
        g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4474
}
1193b4be Poul Sander 2025-02-03 21:28 4475
1193b4be Poul Sander 2025-02-03 21:28 4476
ImGuiID ImGui::GetHoveredID()
1193b4be Poul Sander 2025-02-03 21:28 4477
{
1193b4be Poul Sander 2025-02-03 21:28 4478
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4479
    return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
1193b4be Poul Sander 2025-02-03 21:28 4480
}
1193b4be Poul Sander 2025-02-03 21:28 4481
1193b4be Poul Sander 2025-02-03 21:28 4482
void ImGui::MarkItemEdited(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 4483
{
1193b4be Poul Sander 2025-02-03 21:28 4484
    // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
1193b4be Poul Sander 2025-02-03 21:28 4485
    // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
1193b4be Poul Sander 2025-02-03 21:28 4486
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4487
    if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
1193b4be Poul Sander 2025-02-03 21:28 4488
        return;
1193b4be Poul Sander 2025-02-03 21:28 4489
    if (g.ActiveId == id || g.ActiveId == 0)
1193b4be Poul Sander 2025-02-03 21:28 4490
    {
1193b4be Poul Sander 2025-02-03 21:28 4491
        g.ActiveIdHasBeenEditedThisFrame = true;
1193b4be Poul Sander 2025-02-03 21:28 4492
        g.ActiveIdHasBeenEditedBefore = true;
1193b4be Poul Sander 2025-02-03 21:28 4493
    }
1193b4be Poul Sander 2025-02-03 21:28 4494
1193b4be Poul Sander 2025-02-03 21:28 4495
    // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
1193b4be Poul Sander 2025-02-03 21:28 4496
    // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
1193b4be Poul Sander 2025-02-03 21:28 4497
    IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
1193b4be Poul Sander 2025-02-03 21:28 4498
1193b4be Poul Sander 2025-02-03 21:28 4499
    //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
1193b4be Poul Sander 2025-02-03 21:28 4500
    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
1193b4be Poul Sander 2025-02-03 21:28 4501
}
1193b4be Poul Sander 2025-02-03 21:28 4502
1193b4be Poul Sander 2025-02-03 21:28 4503
bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 4504
{
1193b4be Poul Sander 2025-02-03 21:28 4505
    // An active popup disable hovering on other windows (apart from its own children)
1193b4be Poul Sander 2025-02-03 21:28 4506
    // FIXME-OPT: This could be cached/stored within the window.
1193b4be Poul Sander 2025-02-03 21:28 4507
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4508
    if (g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 4509
        if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)
1193b4be Poul Sander 2025-02-03 21:28 4510
            if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)
1193b4be Poul Sander 2025-02-03 21:28 4511
            {
1193b4be Poul Sander 2025-02-03 21:28 4512
                // For the purpose of those flags we differentiate "standard popup" from "modal popup"
1193b4be Poul Sander 2025-02-03 21:28 4513
                // NB: The 'else' is important because Modal windows are also Popups.
1193b4be Poul Sander 2025-02-03 21:28 4514
                bool want_inhibit = false;
1193b4be Poul Sander 2025-02-03 21:28 4515
                if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
1193b4be Poul Sander 2025-02-03 21:28 4516
                    want_inhibit = true;
1193b4be Poul Sander 2025-02-03 21:28 4517
                else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1193b4be Poul Sander 2025-02-03 21:28 4518
                    want_inhibit = true;
1193b4be Poul Sander 2025-02-03 21:28 4519
1193b4be Poul Sander 2025-02-03 21:28 4520
                // Inhibit hover unless the window is within the stack of our modal/popup
1193b4be Poul Sander 2025-02-03 21:28 4521
                if (want_inhibit)
1193b4be Poul Sander 2025-02-03 21:28 4522
                    if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
1193b4be Poul Sander 2025-02-03 21:28 4523
                        return false;
1193b4be Poul Sander 2025-02-03 21:28 4524
            }
1193b4be Poul Sander 2025-02-03 21:28 4525
1193b4be Poul Sander 2025-02-03 21:28 4526
    // Filter by viewport
1193b4be Poul Sander 2025-02-03 21:28 4527
    if (window->Viewport != g.MouseViewport)
1193b4be Poul Sander 2025-02-03 21:28 4528
        if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
1193b4be Poul Sander 2025-02-03 21:28 4529
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4530
1193b4be Poul Sander 2025-02-03 21:28 4531
    return true;
1193b4be Poul Sander 2025-02-03 21:28 4532
}
1193b4be Poul Sander 2025-02-03 21:28 4533
1193b4be Poul Sander 2025-02-03 21:28 4534
static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 4535
{
1193b4be Poul Sander 2025-02-03 21:28 4536
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4537
    if (flags & ImGuiHoveredFlags_DelayNormal)
1193b4be Poul Sander 2025-02-03 21:28 4538
        return g.Style.HoverDelayNormal;
1193b4be Poul Sander 2025-02-03 21:28 4539
    if (flags & ImGuiHoveredFlags_DelayShort)
1193b4be Poul Sander 2025-02-03 21:28 4540
        return g.Style.HoverDelayShort;
1193b4be Poul Sander 2025-02-03 21:28 4541
    return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4542
}
1193b4be Poul Sander 2025-02-03 21:28 4543
1193b4be Poul Sander 2025-02-03 21:28 4544
static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
1193b4be Poul Sander 2025-02-03 21:28 4545
{
1193b4be Poul Sander 2025-02-03 21:28 4546
    // Allow instance flags to override shared flags
1193b4be Poul Sander 2025-02-03 21:28 4547
    if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
1193b4be Poul Sander 2025-02-03 21:28 4548
        shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
1193b4be Poul Sander 2025-02-03 21:28 4549
    return user_flags | shared_flags;
1193b4be Poul Sander 2025-02-03 21:28 4550
}
1193b4be Poul Sander 2025-02-03 21:28 4551
1193b4be Poul Sander 2025-02-03 21:28 4552
// This is roughly matching the behavior of internal-facing ItemHoverable()
1193b4be Poul Sander 2025-02-03 21:28 4553
// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
1193b4be Poul Sander 2025-02-03 21:28 4554
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
1193b4be Poul Sander 2025-02-03 21:28 4555
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 4556
{
1193b4be Poul Sander 2025-02-03 21:28 4557
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4558
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 4559
    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
1193b4be Poul Sander 2025-02-03 21:28 4560
1193b4be Poul Sander 2025-02-03 21:28 4561
    if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
1193b4be Poul Sander 2025-02-03 21:28 4562
    {
1193b4be Poul Sander 2025-02-03 21:28 4563
        if (!IsItemFocused())
1193b4be Poul Sander 2025-02-03 21:28 4564
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4565
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
1193b4be Poul Sander 2025-02-03 21:28 4566
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4567
1193b4be Poul Sander 2025-02-03 21:28 4568
        if (flags & ImGuiHoveredFlags_ForTooltip)
1193b4be Poul Sander 2025-02-03 21:28 4569
            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
1193b4be Poul Sander 2025-02-03 21:28 4570
    }
1193b4be Poul Sander 2025-02-03 21:28 4571
    else
1193b4be Poul Sander 2025-02-03 21:28 4572
    {
1193b4be Poul Sander 2025-02-03 21:28 4573
        // Test for bounding box overlap, as updated as ItemAdd()
1193b4be Poul Sander 2025-02-03 21:28 4574
        ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
1193b4be Poul Sander 2025-02-03 21:28 4575
        if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
1193b4be Poul Sander 2025-02-03 21:28 4576
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4577
1193b4be Poul Sander 2025-02-03 21:28 4578
        if (flags & ImGuiHoveredFlags_ForTooltip)
1193b4be Poul Sander 2025-02-03 21:28 4579
            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
1193b4be Poul Sander 2025-02-03 21:28 4580
1193b4be Poul Sander 2025-02-03 21:28 4581
        // Done with rectangle culling so we can perform heavier checks now
1193b4be Poul Sander 2025-02-03 21:28 4582
        // Test if we are hovering the right window (our window could be behind another window)
1193b4be Poul Sander 2025-02-03 21:28 4583
        // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
1193b4be Poul Sander 2025-02-03 21:28 4584
        // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable
1193b4be Poul Sander 2025-02-03 21:28 4585
        // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
1193b4be Poul Sander 2025-02-03 21:28 4586
        // the test that has been running for a long while.
1193b4be Poul Sander 2025-02-03 21:28 4587
        if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4588
            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4589
                return false;
1193b4be Poul Sander 2025-02-03 21:28 4590
1193b4be Poul Sander 2025-02-03 21:28 4591
        // Test if another item is active (e.g. being dragged)
1193b4be Poul Sander 2025-02-03 21:28 4592
        const ImGuiID id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 4593
        if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4594
            if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
1193b4be Poul Sander 2025-02-03 21:28 4595
                if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId)
1193b4be Poul Sander 2025-02-03 21:28 4596
                    return false;
1193b4be Poul Sander 2025-02-03 21:28 4597
1193b4be Poul Sander 2025-02-03 21:28 4598
        // Test if interactions on this window are blocked by an active popup or modal.
1193b4be Poul Sander 2025-02-03 21:28 4599
        // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
1193b4be Poul Sander 2025-02-03 21:28 4600
        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
1193b4be Poul Sander 2025-02-03 21:28 4601
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4602
1193b4be Poul Sander 2025-02-03 21:28 4603
        // Test if the item is disabled
1193b4be Poul Sander 2025-02-03 21:28 4604
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
1193b4be Poul Sander 2025-02-03 21:28 4605
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4606
1193b4be Poul Sander 2025-02-03 21:28 4607
        // Special handling for calling after Begin() which represent the title bar or tab.
1193b4be Poul Sander 2025-02-03 21:28 4608
        // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
1193b4be Poul Sander 2025-02-03 21:28 4609
        // will never be overwritten so we need to detect the case.
1193b4be Poul Sander 2025-02-03 21:28 4610
        if (id == window->MoveId && window->WriteAccessed)
1193b4be Poul Sander 2025-02-03 21:28 4611
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4612
1193b4be Poul Sander 2025-02-03 21:28 4613
        // Test if using AllowOverlap and overlapped
1193b4be Poul Sander 2025-02-03 21:28 4614
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
1193b4be Poul Sander 2025-02-03 21:28 4615
            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4616
                if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
1193b4be Poul Sander 2025-02-03 21:28 4617
                    return false;
1193b4be Poul Sander 2025-02-03 21:28 4618
    }
1193b4be Poul Sander 2025-02-03 21:28 4619
1193b4be Poul Sander 2025-02-03 21:28 4620
    // Handle hover delay
1193b4be Poul Sander 2025-02-03 21:28 4621
    // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
1193b4be Poul Sander 2025-02-03 21:28 4622
    const float delay = CalcDelayFromHoveredFlags(flags);
1193b4be Poul Sander 2025-02-03 21:28 4623
    if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
1193b4be Poul Sander 2025-02-03 21:28 4624
    {
1193b4be Poul Sander 2025-02-03 21:28 4625
        ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);
1193b4be Poul Sander 2025-02-03 21:28 4626
        if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
1193b4be Poul Sander 2025-02-03 21:28 4627
            g.HoverItemDelayTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4628
        g.HoverItemDelayId = hover_delay_id;
1193b4be Poul Sander 2025-02-03 21:28 4629
1193b4be Poul Sander 2025-02-03 21:28 4630
        // When changing hovered item we requires a bit of stationary delay before activating hover timer,
1193b4be Poul Sander 2025-02-03 21:28 4631
        // but once unlocked on a given item we also moving.
1193b4be Poul Sander 2025-02-03 21:28 4632
        //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
1193b4be Poul Sander 2025-02-03 21:28 4633
        if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
1193b4be Poul Sander 2025-02-03 21:28 4634
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4635
1193b4be Poul Sander 2025-02-03 21:28 4636
        if (g.HoverItemDelayTimer < delay)
1193b4be Poul Sander 2025-02-03 21:28 4637
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4638
    }
1193b4be Poul Sander 2025-02-03 21:28 4639
1193b4be Poul Sander 2025-02-03 21:28 4640
    return true;
1193b4be Poul Sander 2025-02-03 21:28 4641
}
1193b4be Poul Sander 2025-02-03 21:28 4642
1193b4be Poul Sander 2025-02-03 21:28 4643
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
1193b4be Poul Sander 2025-02-03 21:28 4644
// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
1193b4be Poul Sander 2025-02-03 21:28 4645
// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
1193b4be Poul Sander 2025-02-03 21:28 4646
// If you used this in your legacy/custom widgets code:
1193b4be Poul Sander 2025-02-03 21:28 4647
// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
1193b4be Poul Sander 2025-02-03 21:28 4648
// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
1193b4be Poul Sander 2025-02-03 21:28 4649
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
1193b4be Poul Sander 2025-02-03 21:28 4650
{
1193b4be Poul Sander 2025-02-03 21:28 4651
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4652
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 4653
1193b4be Poul Sander 2025-02-03 21:28 4654
    // Detect ID conflicts
1193b4be Poul Sander 2025-02-03 21:28 4655
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 4656
    if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4657
    {
1193b4be Poul Sander 2025-02-03 21:28 4658
        g.HoveredIdPreviousFrameItemCount++;
1193b4be Poul Sander 2025-02-03 21:28 4659
        if (g.DebugDrawIdConflicts == id)
1193b4be Poul Sander 2025-02-03 21:28 4660
            window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 4661
    }
1193b4be Poul Sander 2025-02-03 21:28 4662
#endif
1193b4be Poul Sander 2025-02-03 21:28 4663
1193b4be Poul Sander 2025-02-03 21:28 4664
    if (g.HoveredWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 4665
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4666
    if (!IsMouseHoveringRect(bb.Min, bb.Max))
1193b4be Poul Sander 2025-02-03 21:28 4667
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4668
1193b4be Poul Sander 2025-02-03 21:28 4669
    if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
1193b4be Poul Sander 2025-02-03 21:28 4670
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4671
    if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
1193b4be Poul Sander 2025-02-03 21:28 4672
        if (!g.ActiveIdFromShortcut)
1193b4be Poul Sander 2025-02-03 21:28 4673
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4674
1193b4be Poul Sander 2025-02-03 21:28 4675
    // Done with rectangle culling so we can perform heavier checks now.
1193b4be Poul Sander 2025-02-03 21:28 4676
    if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
1193b4be Poul Sander 2025-02-03 21:28 4677
    {
1193b4be Poul Sander 2025-02-03 21:28 4678
        g.HoveredIdIsDisabled = true;
1193b4be Poul Sander 2025-02-03 21:28 4679
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4680
    }
1193b4be Poul Sander 2025-02-03 21:28 4681
1193b4be Poul Sander 2025-02-03 21:28 4682
    // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
1193b4be Poul Sander 2025-02-03 21:28 4683
    // hover test in widgets code. We could also decide to split this function is two.
1193b4be Poul Sander 2025-02-03 21:28 4684
    if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 4685
    {
1193b4be Poul Sander 2025-02-03 21:28 4686
        // Drag source doesn't report as hovered
1193b4be Poul Sander 2025-02-03 21:28 4687
        if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
1193b4be Poul Sander 2025-02-03 21:28 4688
            return false;
1193b4be Poul Sander 2025-02-03 21:28 4689
1193b4be Poul Sander 2025-02-03 21:28 4690
        SetHoveredID(id);
1193b4be Poul Sander 2025-02-03 21:28 4691
1193b4be Poul Sander 2025-02-03 21:28 4692
        // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
1193b4be Poul Sander 2025-02-03 21:28 4693
        // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
1193b4be Poul Sander 2025-02-03 21:28 4694
        if (item_flags & ImGuiItemFlags_AllowOverlap)
1193b4be Poul Sander 2025-02-03 21:28 4695
        {
1193b4be Poul Sander 2025-02-03 21:28 4696
            g.HoveredIdAllowOverlap = true;
1193b4be Poul Sander 2025-02-03 21:28 4697
            if (g.HoveredIdPreviousFrame != id)
1193b4be Poul Sander 2025-02-03 21:28 4698
                return false;
1193b4be Poul Sander 2025-02-03 21:28 4699
        }
1193b4be Poul Sander 2025-02-03 21:28 4700
1193b4be Poul Sander 2025-02-03 21:28 4701
        // Display shortcut (only works with mouse)
1193b4be Poul Sander 2025-02-03 21:28 4702
        // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
1193b4be Poul Sander 2025-02-03 21:28 4703
        if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
1193b4be Poul Sander 2025-02-03 21:28 4704
            if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
1193b4be Poul Sander 2025-02-03 21:28 4705
                SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
1193b4be Poul Sander 2025-02-03 21:28 4706
    }
1193b4be Poul Sander 2025-02-03 21:28 4707
1193b4be Poul Sander 2025-02-03 21:28 4708
    // When disabled we'll return false but still set HoveredId
1193b4be Poul Sander 2025-02-03 21:28 4709
    if (item_flags & ImGuiItemFlags_Disabled)
1193b4be Poul Sander 2025-02-03 21:28 4710
    {
1193b4be Poul Sander 2025-02-03 21:28 4711
        // Release active id if turning disabled
1193b4be Poul Sander 2025-02-03 21:28 4712
        if (g.ActiveId == id && id != 0)
1193b4be Poul Sander 2025-02-03 21:28 4713
            ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 4714
        g.HoveredIdIsDisabled = true;
1193b4be Poul Sander 2025-02-03 21:28 4715
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4716
    }
1193b4be Poul Sander 2025-02-03 21:28 4717
1193b4be Poul Sander 2025-02-03 21:28 4718
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 4719
    if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 4720
    {
1193b4be Poul Sander 2025-02-03 21:28 4721
        // [DEBUG] Item Picker tool!
1193b4be Poul Sander 2025-02-03 21:28 4722
        // We perform the check here because reaching is path is rare (1~ time a frame),
1193b4be Poul Sander 2025-02-03 21:28 4723
        // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
1193b4be Poul Sander 2025-02-03 21:28 4724
        // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
1193b4be Poul Sander 2025-02-03 21:28 4725
        if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
1193b4be Poul Sander 2025-02-03 21:28 4726
            GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 4727
        if (g.DebugItemPickerBreakId == id)
1193b4be Poul Sander 2025-02-03 21:28 4728
            IM_DEBUG_BREAK();
1193b4be Poul Sander 2025-02-03 21:28 4729
    }
1193b4be Poul Sander 2025-02-03 21:28 4730
#endif
1193b4be Poul Sander 2025-02-03 21:28 4731
1193b4be Poul Sander 2025-02-03 21:28 4732
    if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4733
        return false;
1193b4be Poul Sander 2025-02-03 21:28 4734
1193b4be Poul Sander 2025-02-03 21:28 4735
    return true;
1193b4be Poul Sander 2025-02-03 21:28 4736
}
1193b4be Poul Sander 2025-02-03 21:28 4737
1193b4be Poul Sander 2025-02-03 21:28 4738
// FIXME: This is inlined/duplicated in ItemAdd()
1193b4be Poul Sander 2025-02-03 21:28 4739
// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
1193b4be Poul Sander 2025-02-03 21:28 4740
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 4741
{
1193b4be Poul Sander 2025-02-03 21:28 4742
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4743
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 4744
    if (!bb.Overlaps(window->ClipRect))
1193b4be Poul Sander 2025-02-03 21:28 4745
        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
1193b4be Poul Sander 2025-02-03 21:28 4746
            if (!g.ItemUnclipByLog)
1193b4be Poul Sander 2025-02-03 21:28 4747
                return true;
1193b4be Poul Sander 2025-02-03 21:28 4748
    return false;
1193b4be Poul Sander 2025-02-03 21:28 4749
}
1193b4be Poul Sander 2025-02-03 21:28 4750
1193b4be Poul Sander 2025-02-03 21:28 4751
// This is also inlined in ItemAdd()
1193b4be Poul Sander 2025-02-03 21:28 4752
// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
1193b4be Poul Sander 2025-02-03 21:28 4753
void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
1193b4be Poul Sander 2025-02-03 21:28 4754
{
1193b4be Poul Sander 2025-02-03 21:28 4755
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4756
    g.LastItemData.ID = item_id;
1193b4be Poul Sander 2025-02-03 21:28 4757
    g.LastItemData.ItemFlags = in_flags;
1193b4be Poul Sander 2025-02-03 21:28 4758
    g.LastItemData.StatusFlags = item_flags;
1193b4be Poul Sander 2025-02-03 21:28 4759
    g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
1193b4be Poul Sander 2025-02-03 21:28 4760
}
1193b4be Poul Sander 2025-02-03 21:28 4761
1193b4be Poul Sander 2025-02-03 21:28 4762
float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
1193b4be Poul Sander 2025-02-03 21:28 4763
{
1193b4be Poul Sander 2025-02-03 21:28 4764
    if (wrap_pos_x < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 4765
        return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 4766
1193b4be Poul Sander 2025-02-03 21:28 4767
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4768
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 4769
    if (wrap_pos_x == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 4770
    {
1193b4be Poul Sander 2025-02-03 21:28 4771
        // We could decide to setup a default wrapping max point for auto-resizing windows,
1193b4be Poul Sander 2025-02-03 21:28 4772
        // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
1193b4be Poul Sander 2025-02-03 21:28 4773
        //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
1193b4be Poul Sander 2025-02-03 21:28 4774
        //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 4775
        //else
1193b4be Poul Sander 2025-02-03 21:28 4776
        wrap_pos_x = window->WorkRect.Max.x;
1193b4be Poul Sander 2025-02-03 21:28 4777
    }
1193b4be Poul Sander 2025-02-03 21:28 4778
    else if (wrap_pos_x > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 4779
    {
1193b4be Poul Sander 2025-02-03 21:28 4780
        wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
1193b4be Poul Sander 2025-02-03 21:28 4781
    }
1193b4be Poul Sander 2025-02-03 21:28 4782
1193b4be Poul Sander 2025-02-03 21:28 4783
    return ImMax(wrap_pos_x - pos.x, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 4784
}
1193b4be Poul Sander 2025-02-03 21:28 4785
1193b4be Poul Sander 2025-02-03 21:28 4786
// IM_ALLOC() == ImGui::MemAlloc()
1193b4be Poul Sander 2025-02-03 21:28 4787
void* ImGui::MemAlloc(size_t size)
1193b4be Poul Sander 2025-02-03 21:28 4788
{
1193b4be Poul Sander 2025-02-03 21:28 4789
    void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
1193b4be Poul Sander 2025-02-03 21:28 4790
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 4791
    if (ImGuiContext* ctx = GImGui)
1193b4be Poul Sander 2025-02-03 21:28 4792
        DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
1193b4be Poul Sander 2025-02-03 21:28 4793
#endif
1193b4be Poul Sander 2025-02-03 21:28 4794
    return ptr;
1193b4be Poul Sander 2025-02-03 21:28 4795
}
1193b4be Poul Sander 2025-02-03 21:28 4796
1193b4be Poul Sander 2025-02-03 21:28 4797
// IM_FREE() == ImGui::MemFree()
1193b4be Poul Sander 2025-02-03 21:28 4798
void ImGui::MemFree(void* ptr)
1193b4be Poul Sander 2025-02-03 21:28 4799
{
1193b4be Poul Sander 2025-02-03 21:28 4800
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 4801
    if (ptr != NULL)
1193b4be Poul Sander 2025-02-03 21:28 4802
        if (ImGuiContext* ctx = GImGui)
1193b4be Poul Sander 2025-02-03 21:28 4803
            DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
1193b4be Poul Sander 2025-02-03 21:28 4804
#endif
1193b4be Poul Sander 2025-02-03 21:28 4805
    return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
1193b4be Poul Sander 2025-02-03 21:28 4806
}
1193b4be Poul Sander 2025-02-03 21:28 4807
1193b4be Poul Sander 2025-02-03 21:28 4808
// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
1193b4be Poul Sander 2025-02-03 21:28 4809
void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
1193b4be Poul Sander 2025-02-03 21:28 4810
{
1193b4be Poul Sander 2025-02-03 21:28 4811
    ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
1193b4be Poul Sander 2025-02-03 21:28 4812
    IM_UNUSED(ptr);
1193b4be Poul Sander 2025-02-03 21:28 4813
    if (entry->FrameCount != frame_count)
1193b4be Poul Sander 2025-02-03 21:28 4814
    {
1193b4be Poul Sander 2025-02-03 21:28 4815
        info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
1193b4be Poul Sander 2025-02-03 21:28 4816
        entry = &info->LastEntriesBuf[info->LastEntriesIdx];
1193b4be Poul Sander 2025-02-03 21:28 4817
        entry->FrameCount = frame_count;
1193b4be Poul Sander 2025-02-03 21:28 4818
        entry->AllocCount = entry->FreeCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 4819
    }
1193b4be Poul Sander 2025-02-03 21:28 4820
    if (size != (size_t)-1)
1193b4be Poul Sander 2025-02-03 21:28 4821
    {
1193b4be Poul Sander 2025-02-03 21:28 4822
        entry->AllocCount++;
1193b4be Poul Sander 2025-02-03 21:28 4823
        info->TotalAllocCount++;
1193b4be Poul Sander 2025-02-03 21:28 4824
        //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
1193b4be Poul Sander 2025-02-03 21:28 4825
    }
1193b4be Poul Sander 2025-02-03 21:28 4826
    else
1193b4be Poul Sander 2025-02-03 21:28 4827
    {
1193b4be Poul Sander 2025-02-03 21:28 4828
        entry->FreeCount++;
1193b4be Poul Sander 2025-02-03 21:28 4829
        info->TotalFreeCount++;
1193b4be Poul Sander 2025-02-03 21:28 4830
        //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
1193b4be Poul Sander 2025-02-03 21:28 4831
    }
1193b4be Poul Sander 2025-02-03 21:28 4832
}
1193b4be Poul Sander 2025-02-03 21:28 4833
1193b4be Poul Sander 2025-02-03 21:28 4834
const char* ImGui::GetClipboardText()
1193b4be Poul Sander 2025-02-03 21:28 4835
{
1193b4be Poul Sander 2025-02-03 21:28 4836
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4837
    return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
1193b4be Poul Sander 2025-02-03 21:28 4838
}
1193b4be Poul Sander 2025-02-03 21:28 4839
1193b4be Poul Sander 2025-02-03 21:28 4840
void ImGui::SetClipboardText(const char* text)
1193b4be Poul Sander 2025-02-03 21:28 4841
{
1193b4be Poul Sander 2025-02-03 21:28 4842
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4843
    if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
1193b4be Poul Sander 2025-02-03 21:28 4844
        g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
1193b4be Poul Sander 2025-02-03 21:28 4845
}
1193b4be Poul Sander 2025-02-03 21:28 4846
1193b4be Poul Sander 2025-02-03 21:28 4847
const char* ImGui::GetVersion()
1193b4be Poul Sander 2025-02-03 21:28 4848
{
1193b4be Poul Sander 2025-02-03 21:28 4849
    return IMGUI_VERSION;
1193b4be Poul Sander 2025-02-03 21:28 4850
}
1193b4be Poul Sander 2025-02-03 21:28 4851
1193b4be Poul Sander 2025-02-03 21:28 4852
ImGuiIO& ImGui::GetIO()
1193b4be Poul Sander 2025-02-03 21:28 4853
{
1193b4be Poul Sander 2025-02-03 21:28 4854
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
1193b4be Poul Sander 2025-02-03 21:28 4855
    return GImGui->IO;
1193b4be Poul Sander 2025-02-03 21:28 4856
}
1193b4be Poul Sander 2025-02-03 21:28 4857
1193b4be Poul Sander 2025-02-03 21:28 4858
ImGuiPlatformIO& ImGui::GetPlatformIO()
1193b4be Poul Sander 2025-02-03 21:28 4859
{
1193b4be Poul Sander 2025-02-03 21:28 4860
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
1193b4be Poul Sander 2025-02-03 21:28 4861
    return GImGui->PlatformIO;
1193b4be Poul Sander 2025-02-03 21:28 4862
}
1193b4be Poul Sander 2025-02-03 21:28 4863
1193b4be Poul Sander 2025-02-03 21:28 4864
// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 4865
ImDrawData* ImGui::GetDrawData()
1193b4be Poul Sander 2025-02-03 21:28 4866
{
1193b4be Poul Sander 2025-02-03 21:28 4867
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4868
    ImGuiViewportP* viewport = g.Viewports[0];
1193b4be Poul Sander 2025-02-03 21:28 4869
    return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
1193b4be Poul Sander 2025-02-03 21:28 4870
}
1193b4be Poul Sander 2025-02-03 21:28 4871
1193b4be Poul Sander 2025-02-03 21:28 4872
double ImGui::GetTime()
1193b4be Poul Sander 2025-02-03 21:28 4873
{
1193b4be Poul Sander 2025-02-03 21:28 4874
    return GImGui->Time;
1193b4be Poul Sander 2025-02-03 21:28 4875
}
1193b4be Poul Sander 2025-02-03 21:28 4876
1193b4be Poul Sander 2025-02-03 21:28 4877
int ImGui::GetFrameCount()
1193b4be Poul Sander 2025-02-03 21:28 4878
{
1193b4be Poul Sander 2025-02-03 21:28 4879
    return GImGui->FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 4880
}
1193b4be Poul Sander 2025-02-03 21:28 4881
1193b4be Poul Sander 2025-02-03 21:28 4882
static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
1193b4be Poul Sander 2025-02-03 21:28 4883
{
1193b4be Poul Sander 2025-02-03 21:28 4884
    // Create the draw list on demand, because they are not frequently used for all viewports
1193b4be Poul Sander 2025-02-03 21:28 4885
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4886
    IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
1193b4be Poul Sander 2025-02-03 21:28 4887
    ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
1193b4be Poul Sander 2025-02-03 21:28 4888
    if (draw_list == NULL)
1193b4be Poul Sander 2025-02-03 21:28 4889
    {
1193b4be Poul Sander 2025-02-03 21:28 4890
        draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
1193b4be Poul Sander 2025-02-03 21:28 4891
        draw_list->_OwnerName = drawlist_name;
1193b4be Poul Sander 2025-02-03 21:28 4892
        viewport->BgFgDrawLists[drawlist_no] = draw_list;
1193b4be Poul Sander 2025-02-03 21:28 4893
    }
1193b4be Poul Sander 2025-02-03 21:28 4894
1193b4be Poul Sander 2025-02-03 21:28 4895
    // Our ImDrawList system requires that there is always a command
1193b4be Poul Sander 2025-02-03 21:28 4896
    if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 4897
    {
1193b4be Poul Sander 2025-02-03 21:28 4898
        draw_list->_ResetForNewFrame();
1193b4be Poul Sander 2025-02-03 21:28 4899
        draw_list->PushTextureID(g.IO.Fonts->TexID);
1193b4be Poul Sander 2025-02-03 21:28 4900
        draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
1193b4be Poul Sander 2025-02-03 21:28 4901
        viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 4902
    }
1193b4be Poul Sander 2025-02-03 21:28 4903
    return draw_list;
1193b4be Poul Sander 2025-02-03 21:28 4904
}
1193b4be Poul Sander 2025-02-03 21:28 4905
1193b4be Poul Sander 2025-02-03 21:28 4906
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
1193b4be Poul Sander 2025-02-03 21:28 4907
{
1193b4be Poul Sander 2025-02-03 21:28 4908
    if (viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 4909
        viewport = GImGui->CurrentWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 4910
    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
1193b4be Poul Sander 2025-02-03 21:28 4911
}
1193b4be Poul Sander 2025-02-03 21:28 4912
1193b4be Poul Sander 2025-02-03 21:28 4913
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
1193b4be Poul Sander 2025-02-03 21:28 4914
{
1193b4be Poul Sander 2025-02-03 21:28 4915
    if (viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 4916
        viewport = GImGui->CurrentWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 4917
    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
1193b4be Poul Sander 2025-02-03 21:28 4918
}
1193b4be Poul Sander 2025-02-03 21:28 4919
1193b4be Poul Sander 2025-02-03 21:28 4920
ImDrawListSharedData* ImGui::GetDrawListSharedData()
1193b4be Poul Sander 2025-02-03 21:28 4921
{
1193b4be Poul Sander 2025-02-03 21:28 4922
    return &GImGui->DrawListSharedData;
1193b4be Poul Sander 2025-02-03 21:28 4923
}
1193b4be Poul Sander 2025-02-03 21:28 4924
1193b4be Poul Sander 2025-02-03 21:28 4925
void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 4926
{
1193b4be Poul Sander 2025-02-03 21:28 4927
    // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
1193b4be Poul Sander 2025-02-03 21:28 4928
    // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
1193b4be Poul Sander 2025-02-03 21:28 4929
    // This is because we want ActiveId to be set even when the window is not permitted to move.
1193b4be Poul Sander 2025-02-03 21:28 4930
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4931
    FocusWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 4932
    SetActiveID(window->MoveId, window);
1193b4be Poul Sander 2025-02-03 21:28 4933
    if (g.IO.ConfigNavCursorVisibleAuto)
1193b4be Poul Sander 2025-02-03 21:28 4934
        g.NavCursorVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 4935
    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
1193b4be Poul Sander 2025-02-03 21:28 4936
    g.ActiveIdNoClearOnFocusLoss = true;
1193b4be Poul Sander 2025-02-03 21:28 4937
    SetActiveIdUsingAllKeyboardKeys();
1193b4be Poul Sander 2025-02-03 21:28 4938
1193b4be Poul Sander 2025-02-03 21:28 4939
    bool can_move_window = true;
1193b4be Poul Sander 2025-02-03 21:28 4940
    if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))
1193b4be Poul Sander 2025-02-03 21:28 4941
        can_move_window = false;
1193b4be Poul Sander 2025-02-03 21:28 4942
    if (ImGuiDockNode* node = window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 4943
        if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
1193b4be Poul Sander 2025-02-03 21:28 4944
            can_move_window = false;
1193b4be Poul Sander 2025-02-03 21:28 4945
    if (can_move_window)
1193b4be Poul Sander 2025-02-03 21:28 4946
        g.MovingWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 4947
}
1193b4be Poul Sander 2025-02-03 21:28 4948
1193b4be Poul Sander 2025-02-03 21:28 4949
// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
1193b4be Poul Sander 2025-02-03 21:28 4950
void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock)
1193b4be Poul Sander 2025-02-03 21:28 4951
{
1193b4be Poul Sander 2025-02-03 21:28 4952
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4953
    bool can_undock_node = false;
1193b4be Poul Sander 2025-02-03 21:28 4954
    if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0)
1193b4be Poul Sander 2025-02-03 21:28 4955
    {
1193b4be Poul Sander 2025-02-03 21:28 4956
        // Can undock if:
1193b4be Poul Sander 2025-02-03 21:28 4957
        // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
1193b4be Poul Sander 2025-02-03 21:28 4958
        // - part of a dockspace node hierarchy: so we can undock the last single visible node too (trivia: undocking from a fixed/central node will create a new node and copy windows)
1193b4be Poul Sander 2025-02-03 21:28 4959
        ImGuiDockNode* root_node = DockNodeGetRootNode(node);
1193b4be Poul Sander 2025-02-03 21:28 4960
        if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL)   // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
1193b4be Poul Sander 2025-02-03 21:28 4961
            can_undock_node = true;
1193b4be Poul Sander 2025-02-03 21:28 4962
    }
1193b4be Poul Sander 2025-02-03 21:28 4963
1193b4be Poul Sander 2025-02-03 21:28 4964
    const bool clicked = IsMouseClicked(0);
1193b4be Poul Sander 2025-02-03 21:28 4965
    const bool dragging = IsMouseDragging(0);
1193b4be Poul Sander 2025-02-03 21:28 4966
    if (can_undock_node && dragging)
1193b4be Poul Sander 2025-02-03 21:28 4967
        DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
1193b4be Poul Sander 2025-02-03 21:28 4968
    else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 4969
        StartMouseMovingWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 4970
}
1193b4be Poul Sander 2025-02-03 21:28 4971
1193b4be Poul Sander 2025-02-03 21:28 4972
// Handle mouse moving window
1193b4be Poul Sander 2025-02-03 21:28 4973
// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
1193b4be Poul Sander 2025-02-03 21:28 4974
// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
1193b4be Poul Sander 2025-02-03 21:28 4975
// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
1193b4be Poul Sander 2025-02-03 21:28 4976
// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
1193b4be Poul Sander 2025-02-03 21:28 4977
void ImGui::UpdateMouseMovingWindowNewFrame()
1193b4be Poul Sander 2025-02-03 21:28 4978
{
1193b4be Poul Sander 2025-02-03 21:28 4979
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 4980
    if (g.MovingWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 4981
    {
1193b4be Poul Sander 2025-02-03 21:28 4982
        // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
1193b4be Poul Sander 2025-02-03 21:28 4983
        // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
1193b4be Poul Sander 2025-02-03 21:28 4984
        KeepAliveID(g.ActiveId);
1193b4be Poul Sander 2025-02-03 21:28 4985
        IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
1193b4be Poul Sander 2025-02-03 21:28 4986
        ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
1193b4be Poul Sander 2025-02-03 21:28 4987
1193b4be Poul Sander 2025-02-03 21:28 4988
        // When a window stop being submitted while being dragged, it may will its viewport until next Begin()
1193b4be Poul Sander 2025-02-03 21:28 4989
        const bool window_disappared = (!moving_window->WasActive && !moving_window->Active);
1193b4be Poul Sander 2025-02-03 21:28 4990
        if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared)
1193b4be Poul Sander 2025-02-03 21:28 4991
        {
1193b4be Poul Sander 2025-02-03 21:28 4992
            ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
1193b4be Poul Sander 2025-02-03 21:28 4993
            if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
1193b4be Poul Sander 2025-02-03 21:28 4994
            {
1193b4be Poul Sander 2025-02-03 21:28 4995
                SetWindowPos(moving_window, pos, ImGuiCond_Always);
1193b4be Poul Sander 2025-02-03 21:28 4996
                if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
1193b4be Poul Sander 2025-02-03 21:28 4997
                {
1193b4be Poul Sander 2025-02-03 21:28 4998
                    moving_window->Viewport->Pos = pos;
1193b4be Poul Sander 2025-02-03 21:28 4999
                    moving_window->Viewport->UpdateWorkRect();
1193b4be Poul Sander 2025-02-03 21:28 5000
                }
1193b4be Poul Sander 2025-02-03 21:28 5001
            }
1193b4be Poul Sander 2025-02-03 21:28 5002
            FocusWindow(g.MovingWindow);
1193b4be Poul Sander 2025-02-03 21:28 5003
        }
1193b4be Poul Sander 2025-02-03 21:28 5004
        else
1193b4be Poul Sander 2025-02-03 21:28 5005
        {
1193b4be Poul Sander 2025-02-03 21:28 5006
            if (!window_disappared)
1193b4be Poul Sander 2025-02-03 21:28 5007
            {
1193b4be Poul Sander 2025-02-03 21:28 5008
                // Try to merge the window back into the main viewport.
1193b4be Poul Sander 2025-02-03 21:28 5009
                // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
1193b4be Poul Sander 2025-02-03 21:28 5010
                if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
1193b4be Poul Sander 2025-02-03 21:28 5011
                    UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
1193b4be Poul Sander 2025-02-03 21:28 5012
1193b4be Poul Sander 2025-02-03 21:28 5013
                // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
1193b4be Poul Sander 2025-02-03 21:28 5014
                if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted())
1193b4be Poul Sander 2025-02-03 21:28 5015
                    g.MouseViewport = moving_window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5016
1193b4be Poul Sander 2025-02-03 21:28 5017
                // Clear the NoInput window flag set by the Viewport system
1193b4be Poul Sander 2025-02-03 21:28 5018
                if (moving_window->Viewport)
1193b4be Poul Sander 2025-02-03 21:28 5019
                    moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;
1193b4be Poul Sander 2025-02-03 21:28 5020
            }
1193b4be Poul Sander 2025-02-03 21:28 5021
1193b4be Poul Sander 2025-02-03 21:28 5022
            g.MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5023
            ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 5024
        }
1193b4be Poul Sander 2025-02-03 21:28 5025
    }
1193b4be Poul Sander 2025-02-03 21:28 5026
    else
1193b4be Poul Sander 2025-02-03 21:28 5027
    {
1193b4be Poul Sander 2025-02-03 21:28 5028
        // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
1193b4be Poul Sander 2025-02-03 21:28 5029
        if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 5030
        {
1193b4be Poul Sander 2025-02-03 21:28 5031
            KeepAliveID(g.ActiveId);
1193b4be Poul Sander 2025-02-03 21:28 5032
            if (!g.IO.MouseDown[0])
1193b4be Poul Sander 2025-02-03 21:28 5033
                ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 5034
        }
1193b4be Poul Sander 2025-02-03 21:28 5035
    }
1193b4be Poul Sander 2025-02-03 21:28 5036
}
1193b4be Poul Sander 2025-02-03 21:28 5037
1193b4be Poul Sander 2025-02-03 21:28 5038
// Initiate focusing and moving window when clicking on empty space or title bar.
1193b4be Poul Sander 2025-02-03 21:28 5039
// Initiate focusing window when clicking on a disabled item.
1193b4be Poul Sander 2025-02-03 21:28 5040
// Handle left-click and right-click focus.
1193b4be Poul Sander 2025-02-03 21:28 5041
void ImGui::UpdateMouseMovingWindowEndFrame()
1193b4be Poul Sander 2025-02-03 21:28 5042
{
1193b4be Poul Sander 2025-02-03 21:28 5043
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5044
    if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
1193b4be Poul Sander 2025-02-03 21:28 5045
        return;
1193b4be Poul Sander 2025-02-03 21:28 5046
1193b4be Poul Sander 2025-02-03 21:28 5047
    // Unless we just made a window/popup appear
1193b4be Poul Sander 2025-02-03 21:28 5048
    if (g.NavWindow && g.NavWindow->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 5049
        return;
1193b4be Poul Sander 2025-02-03 21:28 5050
1193b4be Poul Sander 2025-02-03 21:28 5051
    // Click on empty space to focus window and start moving
1193b4be Poul Sander 2025-02-03 21:28 5052
    // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!)
1193b4be Poul Sander 2025-02-03 21:28 5053
    if (g.IO.MouseClicked[0])
1193b4be Poul Sander 2025-02-03 21:28 5054
    {
1193b4be Poul Sander 2025-02-03 21:28 5055
        // Handle the edge case of a popup being closed while clicking in its empty space.
1193b4be Poul Sander 2025-02-03 21:28 5056
        // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
1193b4be Poul Sander 2025-02-03 21:28 5057
        ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
1193b4be Poul Sander 2025-02-03 21:28 5058
        const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
1193b4be Poul Sander 2025-02-03 21:28 5059
1193b4be Poul Sander 2025-02-03 21:28 5060
        if (root_window != NULL && !is_closed_popup)
1193b4be Poul Sander 2025-02-03 21:28 5061
        {
1193b4be Poul Sander 2025-02-03 21:28 5062
            StartMouseMovingWindow(g.HoveredWindow); //-V595
1193b4be Poul Sander 2025-02-03 21:28 5063
1193b4be Poul Sander 2025-02-03 21:28 5064
            // Cancel moving if clicked outside of title bar
1193b4be Poul Sander 2025-02-03 21:28 5065
            if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
1193b4be Poul Sander 2025-02-03 21:28 5066
                if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 5067
                    if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
1193b4be Poul Sander 2025-02-03 21:28 5068
                        g.MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5069
1193b4be Poul Sander 2025-02-03 21:28 5070
            // Cancel moving if clicked over an item which was disabled or inhibited by popups
1193b4be Poul Sander 2025-02-03 21:28 5071
            // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already)
1193b4be Poul Sander 2025-02-03 21:28 5072
            if (g.HoveredIdIsDisabled)
1193b4be Poul Sander 2025-02-03 21:28 5073
                g.MovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5074
        }
1193b4be Poul Sander 2025-02-03 21:28 5075
        else if (root_window == NULL && g.NavWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 5076
        {
1193b4be Poul Sander 2025-02-03 21:28 5077
            // Clicking on void disable focus
1193b4be Poul Sander 2025-02-03 21:28 5078
            FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
1193b4be Poul Sander 2025-02-03 21:28 5079
        }
1193b4be Poul Sander 2025-02-03 21:28 5080
    }
1193b4be Poul Sander 2025-02-03 21:28 5081
1193b4be Poul Sander 2025-02-03 21:28 5082
    // With right mouse button we close popups without changing focus based on where the mouse is aimed
1193b4be Poul Sander 2025-02-03 21:28 5083
    // Instead, focus will be restored to the window under the bottom-most closed popup.
1193b4be Poul Sander 2025-02-03 21:28 5084
    // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
1193b4be Poul Sander 2025-02-03 21:28 5085
    if (g.IO.MouseClicked[1] && g.HoveredId == 0)
1193b4be Poul Sander 2025-02-03 21:28 5086
    {
1193b4be Poul Sander 2025-02-03 21:28 5087
        // Find the top-most window between HoveredWindow and the top-most Modal Window.
1193b4be Poul Sander 2025-02-03 21:28 5088
        // This is where we can trim the popup stack.
1193b4be Poul Sander 2025-02-03 21:28 5089
        ImGuiWindow* modal = GetTopMostPopupModal();
1193b4be Poul Sander 2025-02-03 21:28 5090
        bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
1193b4be Poul Sander 2025-02-03 21:28 5091
        ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
1193b4be Poul Sander 2025-02-03 21:28 5092
    }
1193b4be Poul Sander 2025-02-03 21:28 5093
}
1193b4be Poul Sander 2025-02-03 21:28 5094
1193b4be Poul Sander 2025-02-03 21:28 5095
// This is called during NewFrame()->UpdateViewportsNewFrame() only.
1193b4be Poul Sander 2025-02-03 21:28 5096
// Need to keep in sync with SetWindowPos()
1193b4be Poul Sander 2025-02-03 21:28 5097
static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
1193b4be Poul Sander 2025-02-03 21:28 5098
{
1193b4be Poul Sander 2025-02-03 21:28 5099
    window->Pos += delta;
1193b4be Poul Sander 2025-02-03 21:28 5100
    window->ClipRect.Translate(delta);
1193b4be Poul Sander 2025-02-03 21:28 5101
    window->OuterRectClipped.Translate(delta);
1193b4be Poul Sander 2025-02-03 21:28 5102
    window->InnerRect.Translate(delta);
1193b4be Poul Sander 2025-02-03 21:28 5103
    window->DC.CursorPos += delta;
1193b4be Poul Sander 2025-02-03 21:28 5104
    window->DC.CursorStartPos += delta;
1193b4be Poul Sander 2025-02-03 21:28 5105
    window->DC.CursorMaxPos += delta;
1193b4be Poul Sander 2025-02-03 21:28 5106
    window->DC.IdealMaxPos += delta;
1193b4be Poul Sander 2025-02-03 21:28 5107
}
1193b4be Poul Sander 2025-02-03 21:28 5108
1193b4be Poul Sander 2025-02-03 21:28 5109
static void ScaleWindow(ImGuiWindow* window, float scale)
1193b4be Poul Sander 2025-02-03 21:28 5110
{
1193b4be Poul Sander 2025-02-03 21:28 5111
    ImVec2 origin = window->Viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 5112
    window->Pos = ImFloor((window->Pos - origin) * scale + origin);
1193b4be Poul Sander 2025-02-03 21:28 5113
    window->Size = ImTrunc(window->Size * scale);
1193b4be Poul Sander 2025-02-03 21:28 5114
    window->SizeFull = ImTrunc(window->SizeFull * scale);
1193b4be Poul Sander 2025-02-03 21:28 5115
    window->ContentSize = ImTrunc(window->ContentSize * scale);
1193b4be Poul Sander 2025-02-03 21:28 5116
}
1193b4be Poul Sander 2025-02-03 21:28 5117
1193b4be Poul Sander 2025-02-03 21:28 5118
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 5119
{
1193b4be Poul Sander 2025-02-03 21:28 5120
    return (window->Active) && (!window->Hidden);
1193b4be Poul Sander 2025-02-03 21:28 5121
}
1193b4be Poul Sander 2025-02-03 21:28 5122
1193b4be Poul Sander 2025-02-03 21:28 5123
// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
1193b4be Poul Sander 2025-02-03 21:28 5124
void ImGui::UpdateHoveredWindowAndCaptureFlags()
1193b4be Poul Sander 2025-02-03 21:28 5125
{
1193b4be Poul Sander 2025-02-03 21:28 5126
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5127
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 5128
1193b4be Poul Sander 2025-02-03 21:28 5129
    // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
1193b4be Poul Sander 2025-02-03 21:28 5130
    // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
1193b4be Poul Sander 2025-02-03 21:28 5131
    g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
1193b4be Poul Sander 2025-02-03 21:28 5132
1193b4be Poul Sander 2025-02-03 21:28 5133
    // Find the window hovered by mouse:
1193b4be Poul Sander 2025-02-03 21:28 5134
    // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
1193b4be Poul Sander 2025-02-03 21:28 5135
    // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
1193b4be Poul Sander 2025-02-03 21:28 5136
    // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
1193b4be Poul Sander 2025-02-03 21:28 5137
    bool clear_hovered_windows = false;
1193b4be Poul Sander 2025-02-03 21:28 5138
    FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
1193b4be Poul Sander 2025-02-03 21:28 5139
    IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
1193b4be Poul Sander 2025-02-03 21:28 5140
    g.HoveredWindowBeforeClear = g.HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 5141
1193b4be Poul Sander 2025-02-03 21:28 5142
    // Modal windows prevents mouse from hovering behind them.
1193b4be Poul Sander 2025-02-03 21:28 5143
    ImGuiWindow* modal_window = GetTopMostPopupModal();
1193b4be Poul Sander 2025-02-03 21:28 5144
    if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ?
1193b4be Poul Sander 2025-02-03 21:28 5145
        clear_hovered_windows = true;
1193b4be Poul Sander 2025-02-03 21:28 5146
1193b4be Poul Sander 2025-02-03 21:28 5147
    // Disabled mouse hovering (we don't currently clear MousePos, we could)
1193b4be Poul Sander 2025-02-03 21:28 5148
    if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
1193b4be Poul Sander 2025-02-03 21:28 5149
        clear_hovered_windows = true;
1193b4be Poul Sander 2025-02-03 21:28 5150
1193b4be Poul Sander 2025-02-03 21:28 5151
    // We track click ownership. When clicked outside of a window the click is owned by the application and
1193b4be Poul Sander 2025-02-03 21:28 5152
    // won't report hovering nor request capture even while dragging over our windows afterward.
1193b4be Poul Sander 2025-02-03 21:28 5153
    const bool has_open_popup = (g.OpenPopupStack.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 5154
    const bool has_open_modal = (modal_window != NULL);
1193b4be Poul Sander 2025-02-03 21:28 5155
    int mouse_earliest_down = -1;
1193b4be Poul Sander 2025-02-03 21:28 5156
    bool mouse_any_down = false;
1193b4be Poul Sander 2025-02-03 21:28 5157
    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
1193b4be Poul Sander 2025-02-03 21:28 5158
    {
1193b4be Poul Sander 2025-02-03 21:28 5159
        if (io.MouseClicked[i])
1193b4be Poul Sander 2025-02-03 21:28 5160
        {
1193b4be Poul Sander 2025-02-03 21:28 5161
            io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
1193b4be Poul Sander 2025-02-03 21:28 5162
            io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
1193b4be Poul Sander 2025-02-03 21:28 5163
        }
1193b4be Poul Sander 2025-02-03 21:28 5164
        mouse_any_down |= io.MouseDown[i];
1193b4be Poul Sander 2025-02-03 21:28 5165
        if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
1193b4be Poul Sander 2025-02-03 21:28 5166
            if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
1193b4be Poul Sander 2025-02-03 21:28 5167
                mouse_earliest_down = i;
1193b4be Poul Sander 2025-02-03 21:28 5168
    }
1193b4be Poul Sander 2025-02-03 21:28 5169
    const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
1193b4be Poul Sander 2025-02-03 21:28 5170
    const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
1193b4be Poul Sander 2025-02-03 21:28 5171
1193b4be Poul Sander 2025-02-03 21:28 5172
    // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
1193b4be Poul Sander 2025-02-03 21:28 5173
    // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
1193b4be Poul Sander 2025-02-03 21:28 5174
    const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
1193b4be Poul Sander 2025-02-03 21:28 5175
    if (!mouse_avail && !mouse_dragging_extern_payload)
1193b4be Poul Sander 2025-02-03 21:28 5176
        clear_hovered_windows = true;
1193b4be Poul Sander 2025-02-03 21:28 5177
1193b4be Poul Sander 2025-02-03 21:28 5178
    if (clear_hovered_windows)
1193b4be Poul Sander 2025-02-03 21:28 5179
        g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5180
1193b4be Poul Sander 2025-02-03 21:28 5181
    // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
1193b4be Poul Sander 2025-02-03 21:28 5182
    // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
1193b4be Poul Sander 2025-02-03 21:28 5183
    if (g.WantCaptureMouseNextFrame != -1)
1193b4be Poul Sander 2025-02-03 21:28 5184
    {
1193b4be Poul Sander 2025-02-03 21:28 5185
        io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
1193b4be Poul Sander 2025-02-03 21:28 5186
    }
1193b4be Poul Sander 2025-02-03 21:28 5187
    else
1193b4be Poul Sander 2025-02-03 21:28 5188
    {
1193b4be Poul Sander 2025-02-03 21:28 5189
        io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
1193b4be Poul Sander 2025-02-03 21:28 5190
        io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
1193b4be Poul Sander 2025-02-03 21:28 5191
    }
1193b4be Poul Sander 2025-02-03 21:28 5192
1193b4be Poul Sander 2025-02-03 21:28 5193
    // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
1193b4be Poul Sander 2025-02-03 21:28 5194
    io.WantCaptureKeyboard = false;
1193b4be Poul Sander 2025-02-03 21:28 5195
    if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
1193b4be Poul Sander 2025-02-03 21:28 5196
    {
1193b4be Poul Sander 2025-02-03 21:28 5197
        if ((g.ActiveId != 0) || (modal_window != NULL))
1193b4be Poul Sander 2025-02-03 21:28 5198
            io.WantCaptureKeyboard = true;
1193b4be Poul Sander 2025-02-03 21:28 5199
        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
1193b4be Poul Sander 2025-02-03 21:28 5200
            io.WantCaptureKeyboard = true;
1193b4be Poul Sander 2025-02-03 21:28 5201
    }
1193b4be Poul Sander 2025-02-03 21:28 5202
    if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
1193b4be Poul Sander 2025-02-03 21:28 5203
        io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
1193b4be Poul Sander 2025-02-03 21:28 5204
1193b4be Poul Sander 2025-02-03 21:28 5205
    // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
1193b4be Poul Sander 2025-02-03 21:28 5206
    io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
1193b4be Poul Sander 2025-02-03 21:28 5207
}
1193b4be Poul Sander 2025-02-03 21:28 5208
1193b4be Poul Sander 2025-02-03 21:28 5209
// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
1193b4be Poul Sander 2025-02-03 21:28 5210
static void SetupDrawListSharedData()
1193b4be Poul Sander 2025-02-03 21:28 5211
{
1193b4be Poul Sander 2025-02-03 21:28 5212
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5213
    ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 5214
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 5215
        virtual_space.Add(viewport->GetMainRect());
1193b4be Poul Sander 2025-02-03 21:28 5216
    g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
1193b4be Poul Sander 2025-02-03 21:28 5217
    g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
1193b4be Poul Sander 2025-02-03 21:28 5218
    g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
1193b4be Poul Sander 2025-02-03 21:28 5219
    g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 5220
    if (g.Style.AntiAliasedLines)
1193b4be Poul Sander 2025-02-03 21:28 5221
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
1193b4be Poul Sander 2025-02-03 21:28 5222
    if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
1193b4be Poul Sander 2025-02-03 21:28 5223
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
1193b4be Poul Sander 2025-02-03 21:28 5224
    if (g.Style.AntiAliasedFill)
1193b4be Poul Sander 2025-02-03 21:28 5225
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
1193b4be Poul Sander 2025-02-03 21:28 5226
    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
1193b4be Poul Sander 2025-02-03 21:28 5227
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
1193b4be Poul Sander 2025-02-03 21:28 5228
}
1193b4be Poul Sander 2025-02-03 21:28 5229
1193b4be Poul Sander 2025-02-03 21:28 5230
void ImGui::NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 5231
{
1193b4be Poul Sander 2025-02-03 21:28 5232
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
1193b4be Poul Sander 2025-02-03 21:28 5233
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5234
1193b4be Poul Sander 2025-02-03 21:28 5235
    // Remove pending delete hooks before frame start.
1193b4be Poul Sander 2025-02-03 21:28 5236
    // This deferred removal avoid issues of removal while iterating the hook vector
1193b4be Poul Sander 2025-02-03 21:28 5237
    for (int n = g.Hooks.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 5238
        if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
1193b4be Poul Sander 2025-02-03 21:28 5239
            g.Hooks.erase(&g.Hooks[n]);
1193b4be Poul Sander 2025-02-03 21:28 5240
1193b4be Poul Sander 2025-02-03 21:28 5241
    CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
1193b4be Poul Sander 2025-02-03 21:28 5242
1193b4be Poul Sander 2025-02-03 21:28 5243
    // Check and assert for various common IO and Configuration mistakes
1193b4be Poul Sander 2025-02-03 21:28 5244
    g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
1193b4be Poul Sander 2025-02-03 21:28 5245
    ErrorCheckNewFrameSanityChecks();
1193b4be Poul Sander 2025-02-03 21:28 5246
    g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
1193b4be Poul Sander 2025-02-03 21:28 5247
1193b4be Poul Sander 2025-02-03 21:28 5248
    // Load settings on first frame, save settings when modified (after a delay)
1193b4be Poul Sander 2025-02-03 21:28 5249
    UpdateSettings();
1193b4be Poul Sander 2025-02-03 21:28 5250
1193b4be Poul Sander 2025-02-03 21:28 5251
    g.Time += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5252
    g.WithinFrameScope = true;
1193b4be Poul Sander 2025-02-03 21:28 5253
    g.FrameCount += 1;
1193b4be Poul Sander 2025-02-03 21:28 5254
    g.TooltipOverrideCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5255
    g.WindowsActiveCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5256
    g.MenusIdSubmittedThisFrame.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5257
1193b4be Poul Sander 2025-02-03 21:28 5258
    // Calculate frame-rate for the user, as a purely luxurious feature
1193b4be Poul Sander 2025-02-03 21:28 5259
    g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
1193b4be Poul Sander 2025-02-03 21:28 5260
    g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5261
    g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
1193b4be Poul Sander 2025-02-03 21:28 5262
    g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
1193b4be Poul Sander 2025-02-03 21:28 5263
    g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 5264
1193b4be Poul Sander 2025-02-03 21:28 5265
    // Process input queue (trickle as many events as possible), turn events into writes to IO structure
1193b4be Poul Sander 2025-02-03 21:28 5266
    g.InputEventsTrail.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5267
    UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
1193b4be Poul Sander 2025-02-03 21:28 5268
1193b4be Poul Sander 2025-02-03 21:28 5269
    // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
1193b4be Poul Sander 2025-02-03 21:28 5270
    UpdateViewportsNewFrame();
1193b4be Poul Sander 2025-02-03 21:28 5271
1193b4be Poul Sander 2025-02-03 21:28 5272
    // Setup current font and draw list shared data
1193b4be Poul Sander 2025-02-03 21:28 5273
    // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
1193b4be Poul Sander 2025-02-03 21:28 5274
    g.IO.Fonts->Locked = true;
1193b4be Poul Sander 2025-02-03 21:28 5275
    SetupDrawListSharedData();
1193b4be Poul Sander 2025-02-03 21:28 5276
    SetCurrentFont(GetDefaultFont());
1193b4be Poul Sander 2025-02-03 21:28 5277
    IM_ASSERT(g.Font->IsLoaded());
1193b4be Poul Sander 2025-02-03 21:28 5278
1193b4be Poul Sander 2025-02-03 21:28 5279
    // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
1193b4be Poul Sander 2025-02-03 21:28 5280
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 5281
    {
1193b4be Poul Sander 2025-02-03 21:28 5282
        viewport->DrawData = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5283
        viewport->DrawDataP.Valid = false;
1193b4be Poul Sander 2025-02-03 21:28 5284
    }
1193b4be Poul Sander 2025-02-03 21:28 5285
1193b4be Poul Sander 2025-02-03 21:28 5286
    // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
1193b4be Poul Sander 2025-02-03 21:28 5287
    if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 5288
        KeepAliveID(g.DragDropPayload.SourceId);
1193b4be Poul Sander 2025-02-03 21:28 5289
1193b4be Poul Sander 2025-02-03 21:28 5290
    // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 5291
    if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
1193b4be Poul Sander 2025-02-03 21:28 5292
        g.DebugDrawIdConflicts = 0;
1193b4be Poul Sander 2025-02-03 21:28 5293
    if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
1193b4be Poul Sander 2025-02-03 21:28 5294
        g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
1193b4be Poul Sander 2025-02-03 21:28 5295
1193b4be Poul Sander 2025-02-03 21:28 5296
    // Update HoveredId data
1193b4be Poul Sander 2025-02-03 21:28 5297
    if (!g.HoveredIdPreviousFrame)
1193b4be Poul Sander 2025-02-03 21:28 5298
        g.HoveredIdTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 5299
    if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
1193b4be Poul Sander 2025-02-03 21:28 5300
        g.HoveredIdNotActiveTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 5301
    if (g.HoveredId)
1193b4be Poul Sander 2025-02-03 21:28 5302
        g.HoveredIdTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5303
    if (g.HoveredId && g.ActiveId != g.HoveredId)
1193b4be Poul Sander 2025-02-03 21:28 5304
        g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5305
    g.HoveredIdPreviousFrame = g.HoveredId;
1193b4be Poul Sander 2025-02-03 21:28 5306
    g.HoveredIdPreviousFrameItemCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5307
    g.HoveredId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5308
    g.HoveredIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 5309
    g.HoveredIdIsDisabled = false;
1193b4be Poul Sander 2025-02-03 21:28 5310
1193b4be Poul Sander 2025-02-03 21:28 5311
    // Clear ActiveID if the item is not alive anymore.
1193b4be Poul Sander 2025-02-03 21:28 5312
    // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
1193b4be Poul Sander 2025-02-03 21:28 5313
    // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
1193b4be Poul Sander 2025-02-03 21:28 5314
    if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 5315
    {
1193b4be Poul Sander 2025-02-03 21:28 5316
        IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
1193b4be Poul Sander 2025-02-03 21:28 5317
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 5318
    }
1193b4be Poul Sander 2025-02-03 21:28 5319
1193b4be Poul Sander 2025-02-03 21:28 5320
    // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
1193b4be Poul Sander 2025-02-03 21:28 5321
    if (g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 5322
        g.ActiveIdTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5323
    g.LastActiveIdTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5324
    g.ActiveIdPreviousFrame = g.ActiveId;
1193b4be Poul Sander 2025-02-03 21:28 5325
    g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
1193b4be Poul Sander 2025-02-03 21:28 5326
    g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
1193b4be Poul Sander 2025-02-03 21:28 5327
    g.ActiveIdIsAlive = 0;
1193b4be Poul Sander 2025-02-03 21:28 5328
    g.ActiveIdHasBeenEditedThisFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 5329
    g.ActiveIdPreviousFrameIsAlive = false;
1193b4be Poul Sander 2025-02-03 21:28 5330
    g.ActiveIdIsJustActivated = false;
1193b4be Poul Sander 2025-02-03 21:28 5331
    if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
1193b4be Poul Sander 2025-02-03 21:28 5332
        g.TempInputId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5333
    if (g.ActiveId == 0)
1193b4be Poul Sander 2025-02-03 21:28 5334
    {
1193b4be Poul Sander 2025-02-03 21:28 5335
        g.ActiveIdUsingNavDirMask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 5336
        g.ActiveIdUsingAllKeyboardKeys = false;
1193b4be Poul Sander 2025-02-03 21:28 5337
    }
1193b4be Poul Sander 2025-02-03 21:28 5338
1193b4be Poul Sander 2025-02-03 21:28 5339
    // Record when we have been stationary as this state is preserved while over same item.
1193b4be Poul Sander 2025-02-03 21:28 5340
    // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
1193b4be Poul Sander 2025-02-03 21:28 5341
    // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
1193b4be Poul Sander 2025-02-03 21:28 5342
    if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
1193b4be Poul Sander 2025-02-03 21:28 5343
        g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
1193b4be Poul Sander 2025-02-03 21:28 5344
    else if (g.HoverItemDelayId == 0)
1193b4be Poul Sander 2025-02-03 21:28 5345
        g.HoverItemUnlockedStationaryId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5346
    if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
1193b4be Poul Sander 2025-02-03 21:28 5347
        g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
1193b4be Poul Sander 2025-02-03 21:28 5348
    else if (g.HoveredWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 5349
        g.HoverWindowUnlockedStationaryId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5350
1193b4be Poul Sander 2025-02-03 21:28 5351
    // Update hover delay for IsItemHovered() with delays and tooltips
1193b4be Poul Sander 2025-02-03 21:28 5352
    g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
1193b4be Poul Sander 2025-02-03 21:28 5353
    if (g.HoverItemDelayId != 0)
1193b4be Poul Sander 2025-02-03 21:28 5354
    {
1193b4be Poul Sander 2025-02-03 21:28 5355
        g.HoverItemDelayTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5356
        g.HoverItemDelayClearTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 5357
        g.HoverItemDelayId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5358
    }
1193b4be Poul Sander 2025-02-03 21:28 5359
    else if (g.HoverItemDelayTimer > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 5360
    {
1193b4be Poul Sander 2025-02-03 21:28 5361
        // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
1193b4be Poul Sander 2025-02-03 21:28 5362
        // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
1193b4be Poul Sander 2025-02-03 21:28 5363
        g.HoverItemDelayClearTimer += g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 5364
        if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
1193b4be Poul Sander 2025-02-03 21:28 5365
            g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
1193b4be Poul Sander 2025-02-03 21:28 5366
    }
1193b4be Poul Sander 2025-02-03 21:28 5367
1193b4be Poul Sander 2025-02-03 21:28 5368
    // Drag and drop
1193b4be Poul Sander 2025-02-03 21:28 5369
    g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
1193b4be Poul Sander 2025-02-03 21:28 5370
    g.DragDropAcceptIdCurr = 0;
1193b4be Poul Sander 2025-02-03 21:28 5371
    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 5372
    g.DragDropWithinSource = false;
1193b4be Poul Sander 2025-02-03 21:28 5373
    g.DragDropWithinTarget = false;
1193b4be Poul Sander 2025-02-03 21:28 5374
    g.DragDropHoldJustPressedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5375
    g.TooltipPreviousWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5376
1193b4be Poul Sander 2025-02-03 21:28 5377
    // Close popups on focus lost (currently wip/opt-in)
1193b4be Poul Sander 2025-02-03 21:28 5378
    //if (g.IO.AppFocusLost)
1193b4be Poul Sander 2025-02-03 21:28 5379
    //    ClosePopupsExceptModals();
1193b4be Poul Sander 2025-02-03 21:28 5380
1193b4be Poul Sander 2025-02-03 21:28 5381
    // Update keyboard input state
1193b4be Poul Sander 2025-02-03 21:28 5382
    UpdateKeyboardInputs();
1193b4be Poul Sander 2025-02-03 21:28 5383
1193b4be Poul Sander 2025-02-03 21:28 5384
    //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
1193b4be Poul Sander 2025-02-03 21:28 5385
    //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
1193b4be Poul Sander 2025-02-03 21:28 5386
    //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
1193b4be Poul Sander 2025-02-03 21:28 5387
    //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
1193b4be Poul Sander 2025-02-03 21:28 5388
1193b4be Poul Sander 2025-02-03 21:28 5389
    // Update keyboard/gamepad navigation
1193b4be Poul Sander 2025-02-03 21:28 5390
    NavUpdate();
1193b4be Poul Sander 2025-02-03 21:28 5391
1193b4be Poul Sander 2025-02-03 21:28 5392
    // Update mouse input state
1193b4be Poul Sander 2025-02-03 21:28 5393
    UpdateMouseInputs();
1193b4be Poul Sander 2025-02-03 21:28 5394
1193b4be Poul Sander 2025-02-03 21:28 5395
    // Undocking
1193b4be Poul Sander 2025-02-03 21:28 5396
    // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
1193b4be Poul Sander 2025-02-03 21:28 5397
    DockContextNewFrameUpdateUndocking(&g);
1193b4be Poul Sander 2025-02-03 21:28 5398
1193b4be Poul Sander 2025-02-03 21:28 5399
    // Mark all windows as not visible and compact unused memory.
1193b4be Poul Sander 2025-02-03 21:28 5400
    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
1193b4be Poul Sander 2025-02-03 21:28 5401
    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
1193b4be Poul Sander 2025-02-03 21:28 5402
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 5403
    {
1193b4be Poul Sander 2025-02-03 21:28 5404
        window->WasActive = window->Active;
1193b4be Poul Sander 2025-02-03 21:28 5405
        window->Active = false;
1193b4be Poul Sander 2025-02-03 21:28 5406
        window->WriteAccessed = false;
1193b4be Poul Sander 2025-02-03 21:28 5407
        window->BeginCountPreviousFrame = window->BeginCount;
1193b4be Poul Sander 2025-02-03 21:28 5408
        window->BeginCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5409
1193b4be Poul Sander 2025-02-03 21:28 5410
        // Garbage collect transient buffers of recently unused windows
1193b4be Poul Sander 2025-02-03 21:28 5411
        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
1193b4be Poul Sander 2025-02-03 21:28 5412
            GcCompactTransientWindowBuffers(window);
1193b4be Poul Sander 2025-02-03 21:28 5413
    }
1193b4be Poul Sander 2025-02-03 21:28 5414
1193b4be Poul Sander 2025-02-03 21:28 5415
    // Find hovered window
1193b4be Poul Sander 2025-02-03 21:28 5416
    // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
1193b4be Poul Sander 2025-02-03 21:28 5417
    // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
1193b4be Poul Sander 2025-02-03 21:28 5418
    UpdateHoveredWindowAndCaptureFlags();
1193b4be Poul Sander 2025-02-03 21:28 5419
1193b4be Poul Sander 2025-02-03 21:28 5420
    // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
1193b4be Poul Sander 2025-02-03 21:28 5421
    UpdateMouseMovingWindowNewFrame();
1193b4be Poul Sander 2025-02-03 21:28 5422
1193b4be Poul Sander 2025-02-03 21:28 5423
    // Background darkening/whitening
1193b4be Poul Sander 2025-02-03 21:28 5424
    if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
1193b4be Poul Sander 2025-02-03 21:28 5425
        g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 5426
    else
1193b4be Poul Sander 2025-02-03 21:28 5427
        g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 5428
1193b4be Poul Sander 2025-02-03 21:28 5429
    g.MouseCursor = ImGuiMouseCursor_Arrow;
1193b4be Poul Sander 2025-02-03 21:28 5430
    g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
1193b4be Poul Sander 2025-02-03 21:28 5431
1193b4be Poul Sander 2025-02-03 21:28 5432
    // Platform IME data: reset for the frame
1193b4be Poul Sander 2025-02-03 21:28 5433
    g.PlatformImeDataPrev = g.PlatformImeData;
1193b4be Poul Sander 2025-02-03 21:28 5434
    g.PlatformImeData.WantVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 5435
1193b4be Poul Sander 2025-02-03 21:28 5436
    // Mouse wheel scrolling, scale
1193b4be Poul Sander 2025-02-03 21:28 5437
    UpdateMouseWheel();
1193b4be Poul Sander 2025-02-03 21:28 5438
1193b4be Poul Sander 2025-02-03 21:28 5439
    // Garbage collect transient buffers of recently unused tables
1193b4be Poul Sander 2025-02-03 21:28 5440
    for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 5441
        if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
1193b4be Poul Sander 2025-02-03 21:28 5442
            TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
1193b4be Poul Sander 2025-02-03 21:28 5443
    for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
1193b4be Poul Sander 2025-02-03 21:28 5444
        if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
1193b4be Poul Sander 2025-02-03 21:28 5445
            TableGcCompactTransientBuffers(&table_temp_data);
1193b4be Poul Sander 2025-02-03 21:28 5446
    if (g.GcCompactAll)
1193b4be Poul Sander 2025-02-03 21:28 5447
        GcCompactTransientMiscBuffers();
1193b4be Poul Sander 2025-02-03 21:28 5448
    g.GcCompactAll = false;
1193b4be Poul Sander 2025-02-03 21:28 5449
1193b4be Poul Sander 2025-02-03 21:28 5450
    // Closing the focused window restore focus to the first active root window in descending z-order
1193b4be Poul Sander 2025-02-03 21:28 5451
    if (g.NavWindow && !g.NavWindow->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 5452
        FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
1193b4be Poul Sander 2025-02-03 21:28 5453
1193b4be Poul Sander 2025-02-03 21:28 5454
    // No window should be open at the beginning of the frame.
1193b4be Poul Sander 2025-02-03 21:28 5455
    // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
1193b4be Poul Sander 2025-02-03 21:28 5456
    g.CurrentWindowStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5457
    g.BeginPopupStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5458
    g.ItemFlagsStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5459
    g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
1193b4be Poul Sander 2025-02-03 21:28 5460
    g.CurrentItemFlags = g.ItemFlagsStack.back();
1193b4be Poul Sander 2025-02-03 21:28 5461
    g.GroupStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5462
1193b4be Poul Sander 2025-02-03 21:28 5463
    // Docking
1193b4be Poul Sander 2025-02-03 21:28 5464
    DockContextNewFrameUpdateDocking(&g);
1193b4be Poul Sander 2025-02-03 21:28 5465
1193b4be Poul Sander 2025-02-03 21:28 5466
    // [DEBUG] Update debug features
1193b4be Poul Sander 2025-02-03 21:28 5467
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 5468
    UpdateDebugToolItemPicker();
1193b4be Poul Sander 2025-02-03 21:28 5469
    UpdateDebugToolStackQueries();
1193b4be Poul Sander 2025-02-03 21:28 5470
    UpdateDebugToolFlashStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 5471
    if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
1193b4be Poul Sander 2025-02-03 21:28 5472
    {
1193b4be Poul Sander 2025-02-03 21:28 5473
        g.DebugLocateId = 0;
1193b4be Poul Sander 2025-02-03 21:28 5474
        g.DebugBreakInLocateId = false;
1193b4be Poul Sander 2025-02-03 21:28 5475
    }
1193b4be Poul Sander 2025-02-03 21:28 5476
    if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
1193b4be Poul Sander 2025-02-03 21:28 5477
    {
1193b4be Poul Sander 2025-02-03 21:28 5478
        DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
1193b4be Poul Sander 2025-02-03 21:28 5479
        g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
1193b4be Poul Sander 2025-02-03 21:28 5480
        g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 5481
    }
1193b4be Poul Sander 2025-02-03 21:28 5482
#endif
1193b4be Poul Sander 2025-02-03 21:28 5483
1193b4be Poul Sander 2025-02-03 21:28 5484
    // Create implicit/fallback window - which we will only render it if the user has added something to it.
1193b4be Poul Sander 2025-02-03 21:28 5485
    // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
1193b4be Poul Sander 2025-02-03 21:28 5486
    // This fallback is particularly important as it prevents ImGui:: calls from crashing.
1193b4be Poul Sander 2025-02-03 21:28 5487
    g.WithinFrameScopeWithImplicitWindow = true;
1193b4be Poul Sander 2025-02-03 21:28 5488
    SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
1193b4be Poul Sander 2025-02-03 21:28 5489
    Begin("Debug##Default");
1193b4be Poul Sander 2025-02-03 21:28 5490
    IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
1193b4be Poul Sander 2025-02-03 21:28 5491
1193b4be Poul Sander 2025-02-03 21:28 5492
    // Store stack sizes
1193b4be Poul Sander 2025-02-03 21:28 5493
    g.ErrorCountCurrentFrame = 0;
1193b4be Poul Sander 2025-02-03 21:28 5494
    ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
1193b4be Poul Sander 2025-02-03 21:28 5495
1193b4be Poul Sander 2025-02-03 21:28 5496
    // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
1193b4be Poul Sander 2025-02-03 21:28 5497
    // allowing to validate correct Begin/End behavior in user code.
1193b4be Poul Sander 2025-02-03 21:28 5498
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 5499
    if (g.IO.ConfigDebugBeginReturnValueLoop)
1193b4be Poul Sander 2025-02-03 21:28 5500
        g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
1193b4be Poul Sander 2025-02-03 21:28 5501
    else
1193b4be Poul Sander 2025-02-03 21:28 5502
        g.DebugBeginReturnValueCullDepth = -1;
1193b4be Poul Sander 2025-02-03 21:28 5503
#endif
1193b4be Poul Sander 2025-02-03 21:28 5504
1193b4be Poul Sander 2025-02-03 21:28 5505
    CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
1193b4be Poul Sander 2025-02-03 21:28 5506
}
1193b4be Poul Sander 2025-02-03 21:28 5507
1193b4be Poul Sander 2025-02-03 21:28 5508
// FIXME: Add a more explicit sort order in the window structure.
1193b4be Poul Sander 2025-02-03 21:28 5509
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
1193b4be Poul Sander 2025-02-03 21:28 5510
{
1193b4be Poul Sander 2025-02-03 21:28 5511
    const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
1193b4be Poul Sander 2025-02-03 21:28 5512
    const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
1193b4be Poul Sander 2025-02-03 21:28 5513
    if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
1193b4be Poul Sander 2025-02-03 21:28 5514
        return d;
1193b4be Poul Sander 2025-02-03 21:28 5515
    if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
1193b4be Poul Sander 2025-02-03 21:28 5516
        return d;
1193b4be Poul Sander 2025-02-03 21:28 5517
    return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
1193b4be Poul Sander 2025-02-03 21:28 5518
}
1193b4be Poul Sander 2025-02-03 21:28 5519
1193b4be Poul Sander 2025-02-03 21:28 5520
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 5521
{
1193b4be Poul Sander 2025-02-03 21:28 5522
    out_sorted_windows->push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 5523
    if (window->Active)
1193b4be Poul Sander 2025-02-03 21:28 5524
    {
1193b4be Poul Sander 2025-02-03 21:28 5525
        int count = window->DC.ChildWindows.Size;
1193b4be Poul Sander 2025-02-03 21:28 5526
        ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
1193b4be Poul Sander 2025-02-03 21:28 5527
        for (int i = 0; i < count; i++)
1193b4be Poul Sander 2025-02-03 21:28 5528
        {
1193b4be Poul Sander 2025-02-03 21:28 5529
            ImGuiWindow* child = window->DC.ChildWindows[i];
1193b4be Poul Sander 2025-02-03 21:28 5530
            if (child->Active)
1193b4be Poul Sander 2025-02-03 21:28 5531
                AddWindowToSortBuffer(out_sorted_windows, child);
1193b4be Poul Sander 2025-02-03 21:28 5532
        }
1193b4be Poul Sander 2025-02-03 21:28 5533
    }
1193b4be Poul Sander 2025-02-03 21:28 5534
}
1193b4be Poul Sander 2025-02-03 21:28 5535
1193b4be Poul Sander 2025-02-03 21:28 5536
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
1193b4be Poul Sander 2025-02-03 21:28 5537
{
1193b4be Poul Sander 2025-02-03 21:28 5538
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5539
    ImGuiViewportP* viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5540
    IM_ASSERT(viewport != NULL);
1193b4be Poul Sander 2025-02-03 21:28 5541
    g.IO.MetricsRenderWindows++;
1193b4be Poul Sander 2025-02-03 21:28 5542
    if (window->DrawList->_Splitter._Count > 1)
1193b4be Poul Sander 2025-02-03 21:28 5543
        window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
1193b4be Poul Sander 2025-02-03 21:28 5544
    ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
1193b4be Poul Sander 2025-02-03 21:28 5545
    for (ImGuiWindow* child : window->DC.ChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 5546
        if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
1193b4be Poul Sander 2025-02-03 21:28 5547
            AddWindowToDrawData(child, layer);
1193b4be Poul Sander 2025-02-03 21:28 5548
}
1193b4be Poul Sander 2025-02-03 21:28 5549
1193b4be Poul Sander 2025-02-03 21:28 5550
static inline int GetWindowDisplayLayer(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 5551
{
1193b4be Poul Sander 2025-02-03 21:28 5552
    return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
1193b4be Poul Sander 2025-02-03 21:28 5553
}
1193b4be Poul Sander 2025-02-03 21:28 5554
1193b4be Poul Sander 2025-02-03 21:28 5555
// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
1193b4be Poul Sander 2025-02-03 21:28 5556
static inline void AddRootWindowToDrawData(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 5557
{
1193b4be Poul Sander 2025-02-03 21:28 5558
    AddWindowToDrawData(window, GetWindowDisplayLayer(window));
1193b4be Poul Sander 2025-02-03 21:28 5559
}
1193b4be Poul Sander 2025-02-03 21:28 5560
1193b4be Poul Sander 2025-02-03 21:28 5561
static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
1193b4be Poul Sander 2025-02-03 21:28 5562
{
1193b4be Poul Sander 2025-02-03 21:28 5563
    int n = builder->Layers[0]->Size;
1193b4be Poul Sander 2025-02-03 21:28 5564
    int full_size = n;
1193b4be Poul Sander 2025-02-03 21:28 5565
    for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
1193b4be Poul Sander 2025-02-03 21:28 5566
        full_size += builder->Layers[i]->Size;
1193b4be Poul Sander 2025-02-03 21:28 5567
    builder->Layers[0]->resize(full_size);
1193b4be Poul Sander 2025-02-03 21:28 5568
    for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
1193b4be Poul Sander 2025-02-03 21:28 5569
    {
1193b4be Poul Sander 2025-02-03 21:28 5570
        ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
1193b4be Poul Sander 2025-02-03 21:28 5571
        if (layer->empty())
1193b4be Poul Sander 2025-02-03 21:28 5572
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5573
        memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
1193b4be Poul Sander 2025-02-03 21:28 5574
        n += layer->Size;
1193b4be Poul Sander 2025-02-03 21:28 5575
        layer->resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5576
    }
1193b4be Poul Sander 2025-02-03 21:28 5577
}
1193b4be Poul Sander 2025-02-03 21:28 5578
1193b4be Poul Sander 2025-02-03 21:28 5579
static void InitViewportDrawData(ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 5580
{
1193b4be Poul Sander 2025-02-03 21:28 5581
    ImGuiIO& io = ImGui::GetIO();
1193b4be Poul Sander 2025-02-03 21:28 5582
    ImDrawData* draw_data = &viewport->DrawDataP;
1193b4be Poul Sander 2025-02-03 21:28 5583
1193b4be Poul Sander 2025-02-03 21:28 5584
    viewport->DrawData = draw_data; // Make publicly accessible
1193b4be Poul Sander 2025-02-03 21:28 5585
    viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
1193b4be Poul Sander 2025-02-03 21:28 5586
    viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
1193b4be Poul Sander 2025-02-03 21:28 5587
    viewport->DrawDataBuilder.Layers[0]->resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5588
    viewport->DrawDataBuilder.Layers[1]->resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5589
1193b4be Poul Sander 2025-02-03 21:28 5590
    // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
1193b4be Poul Sander 2025-02-03 21:28 5591
    // and to allow applications/backends to easily skip rendering.
1193b4be Poul Sander 2025-02-03 21:28 5592
    // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
1193b4be Poul Sander 2025-02-03 21:28 5593
    // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
1193b4be Poul Sander 2025-02-03 21:28 5594
    // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
1193b4be Poul Sander 2025-02-03 21:28 5595
    const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0;
1193b4be Poul Sander 2025-02-03 21:28 5596
1193b4be Poul Sander 2025-02-03 21:28 5597
    draw_data->Valid = true;
1193b4be Poul Sander 2025-02-03 21:28 5598
    draw_data->CmdListsCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5599
    draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 5600
    draw_data->DisplayPos = viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 5601
    draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 5602
    draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
1193b4be Poul Sander 2025-02-03 21:28 5603
    draw_data->OwnerViewport = viewport;
1193b4be Poul Sander 2025-02-03 21:28 5604
}
1193b4be Poul Sander 2025-02-03 21:28 5605
1193b4be Poul Sander 2025-02-03 21:28 5606
// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
1193b4be Poul Sander 2025-02-03 21:28 5607
// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
1193b4be Poul Sander 2025-02-03 21:28 5608
//   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
1193b4be Poul Sander 2025-02-03 21:28 5609
// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
1193b4be Poul Sander 2025-02-03 21:28 5610
//   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
1193b4be Poul Sander 2025-02-03 21:28 5611
//   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
1193b4be Poul Sander 2025-02-03 21:28 5612
// - This is analoguous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
1193b4be Poul Sander 2025-02-03 21:28 5613
//   which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
1193b4be Poul Sander 2025-02-03 21:28 5614
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 5615
{
1193b4be Poul Sander 2025-02-03 21:28 5616
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 5617
    window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
1193b4be Poul Sander 2025-02-03 21:28 5618
    window->ClipRect = window->DrawList->_ClipRectStack.back();
1193b4be Poul Sander 2025-02-03 21:28 5619
}
1193b4be Poul Sander 2025-02-03 21:28 5620
1193b4be Poul Sander 2025-02-03 21:28 5621
void ImGui::PopClipRect()
1193b4be Poul Sander 2025-02-03 21:28 5622
{
1193b4be Poul Sander 2025-02-03 21:28 5623
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 5624
    window->DrawList->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 5625
    window->ClipRect = window->DrawList->_ClipRectStack.back();
1193b4be Poul Sander 2025-02-03 21:28 5626
}
1193b4be Poul Sander 2025-02-03 21:28 5627
1193b4be Poul Sander 2025-02-03 21:28 5628
static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 5629
{
1193b4be Poul Sander 2025-02-03 21:28 5630
    for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 5631
        if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
1193b4be Poul Sander 2025-02-03 21:28 5632
            return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
1193b4be Poul Sander 2025-02-03 21:28 5633
    return window;
1193b4be Poul Sander 2025-02-03 21:28 5634
}
1193b4be Poul Sander 2025-02-03 21:28 5635
1193b4be Poul Sander 2025-02-03 21:28 5636
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
1193b4be Poul Sander 2025-02-03 21:28 5637
{
1193b4be Poul Sander 2025-02-03 21:28 5638
    if ((col & IM_COL32_A_MASK) == 0)
1193b4be Poul Sander 2025-02-03 21:28 5639
        return;
1193b4be Poul Sander 2025-02-03 21:28 5640
1193b4be Poul Sander 2025-02-03 21:28 5641
    ImGuiViewportP* viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5642
    ImRect viewport_rect = viewport->GetMainRect();
1193b4be Poul Sander 2025-02-03 21:28 5643
1193b4be Poul Sander 2025-02-03 21:28 5644
    // Draw behind window by moving the draw command at the FRONT of the draw list
1193b4be Poul Sander 2025-02-03 21:28 5645
    {
1193b4be Poul Sander 2025-02-03 21:28 5646
        // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
1193b4be Poul Sander 2025-02-03 21:28 5647
        // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.
1193b4be Poul Sander 2025-02-03 21:28 5648
        ImDrawList* draw_list = window->RootWindowDockTree->DrawList;
1193b4be Poul Sander 2025-02-03 21:28 5649
        draw_list->ChannelsMerge();
1193b4be Poul Sander 2025-02-03 21:28 5650
        if (draw_list->CmdBuffer.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 5651
            draw_list->AddDrawCmd();
1193b4be Poul Sander 2025-02-03 21:28 5652
        draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
1193b4be Poul Sander 2025-02-03 21:28 5653
        draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
1193b4be Poul Sander 2025-02-03 21:28 5654
        ImDrawCmd cmd = draw_list->CmdBuffer.back();
1193b4be Poul Sander 2025-02-03 21:28 5655
        IM_ASSERT(cmd.ElemCount == 6);
1193b4be Poul Sander 2025-02-03 21:28 5656
        draw_list->CmdBuffer.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 5657
        draw_list->CmdBuffer.push_front(cmd);
1193b4be Poul Sander 2025-02-03 21:28 5658
        draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
1193b4be Poul Sander 2025-02-03 21:28 5659
        draw_list->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 5660
    }
1193b4be Poul Sander 2025-02-03 21:28 5661
1193b4be Poul Sander 2025-02-03 21:28 5662
    // Draw over sibling docking nodes in a same docking tree
1193b4be Poul Sander 2025-02-03 21:28 5663
    if (window->RootWindow->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 5664
    {
1193b4be Poul Sander 2025-02-03 21:28 5665
        ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;
1193b4be Poul Sander 2025-02-03 21:28 5666
        draw_list->ChannelsMerge();
1193b4be Poul Sander 2025-02-03 21:28 5667
        if (draw_list->CmdBuffer.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 5668
            draw_list->AddDrawCmd();
1193b4be Poul Sander 2025-02-03 21:28 5669
        draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false);
1193b4be Poul Sander 2025-02-03 21:28 5670
        RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding);
1193b4be Poul Sander 2025-02-03 21:28 5671
        draw_list->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 5672
    }
1193b4be Poul Sander 2025-02-03 21:28 5673
}
1193b4be Poul Sander 2025-02-03 21:28 5674
1193b4be Poul Sander 2025-02-03 21:28 5675
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
1193b4be Poul Sander 2025-02-03 21:28 5676
{
1193b4be Poul Sander 2025-02-03 21:28 5677
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5678
    ImGuiWindow* bottom_most_visible_window = parent_window;
1193b4be Poul Sander 2025-02-03 21:28 5679
    for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 5680
    {
1193b4be Poul Sander 2025-02-03 21:28 5681
        ImGuiWindow* window = g.Windows[i];
1193b4be Poul Sander 2025-02-03 21:28 5682
        if (window->Flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 5683
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5684
        if (!IsWindowWithinBeginStackOf(window, parent_window))
1193b4be Poul Sander 2025-02-03 21:28 5685
            break;
1193b4be Poul Sander 2025-02-03 21:28 5686
        if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
1193b4be Poul Sander 2025-02-03 21:28 5687
            bottom_most_visible_window = window;
1193b4be Poul Sander 2025-02-03 21:28 5688
    }
1193b4be Poul Sander 2025-02-03 21:28 5689
    return bottom_most_visible_window;
1193b4be Poul Sander 2025-02-03 21:28 5690
}
1193b4be Poul Sander 2025-02-03 21:28 5691
1193b4be Poul Sander 2025-02-03 21:28 5692
// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage.
1193b4be Poul Sander 2025-02-03 21:28 5693
// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().
1193b4be Poul Sander 2025-02-03 21:28 5694
static void ImGui::RenderDimmedBackgrounds()
1193b4be Poul Sander 2025-02-03 21:28 5695
{
1193b4be Poul Sander 2025-02-03 21:28 5696
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5697
    ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
1193b4be Poul Sander 2025-02-03 21:28 5698
    if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 5699
        return;
1193b4be Poul Sander 2025-02-03 21:28 5700
    const bool dim_bg_for_modal = (modal_window != NULL);
1193b4be Poul Sander 2025-02-03 21:28 5701
    const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
1193b4be Poul Sander 2025-02-03 21:28 5702
    if (!dim_bg_for_modal && !dim_bg_for_window_list)
1193b4be Poul Sander 2025-02-03 21:28 5703
        return;
1193b4be Poul Sander 2025-02-03 21:28 5704
1193b4be Poul Sander 2025-02-03 21:28 5705
    ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };
1193b4be Poul Sander 2025-02-03 21:28 5706
    if (dim_bg_for_modal)
1193b4be Poul Sander 2025-02-03 21:28 5707
    {
1193b4be Poul Sander 2025-02-03 21:28 5708
        // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
1193b4be Poul Sander 2025-02-03 21:28 5709
        ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
1193b4be Poul Sander 2025-02-03 21:28 5710
        RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
1193b4be Poul Sander 2025-02-03 21:28 5711
        viewports_already_dimmed[0] = modal_window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5712
    }
1193b4be Poul Sander 2025-02-03 21:28 5713
    else if (dim_bg_for_window_list)
1193b4be Poul Sander 2025-02-03 21:28 5714
    {
1193b4be Poul Sander 2025-02-03 21:28 5715
        // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
1193b4be Poul Sander 2025-02-03 21:28 5716
        RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
1193b4be Poul Sander 2025-02-03 21:28 5717
        if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)
1193b4be Poul Sander 2025-02-03 21:28 5718
            RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
1193b4be Poul Sander 2025-02-03 21:28 5719
        viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5720
        viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL;
1193b4be Poul Sander 2025-02-03 21:28 5721
1193b4be Poul Sander 2025-02-03 21:28 5722
        // Draw border around CTRL+Tab target window
1193b4be Poul Sander 2025-02-03 21:28 5723
        ImGuiWindow* window = g.NavWindowingTargetAnim;
1193b4be Poul Sander 2025-02-03 21:28 5724
        ImGuiViewport* viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5725
        float distance = g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 5726
        ImRect bb = window->Rect();
1193b4be Poul Sander 2025-02-03 21:28 5727
        bb.Expand(distance);
1193b4be Poul Sander 2025-02-03 21:28 5728
        if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
1193b4be Poul Sander 2025-02-03 21:28 5729
            bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
1193b4be Poul Sander 2025-02-03 21:28 5730
        window->DrawList->ChannelsMerge();
1193b4be Poul Sander 2025-02-03 21:28 5731
        if (window->DrawList->CmdBuffer.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 5732
            window->DrawList->AddDrawCmd();
1193b4be Poul Sander 2025-02-03 21:28 5733
        window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
1193b4be Poul Sander 2025-02-03 21:28 5734
        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);
1193b4be Poul Sander 2025-02-03 21:28 5735
        window->DrawList->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 5736
    }
1193b4be Poul Sander 2025-02-03 21:28 5737
1193b4be Poul Sander 2025-02-03 21:28 5738
    // Draw dimming background on _other_ viewports than the ones our windows are in
1193b4be Poul Sander 2025-02-03 21:28 5739
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 5740
    {
1193b4be Poul Sander 2025-02-03 21:28 5741
        if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])
1193b4be Poul Sander 2025-02-03 21:28 5742
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5743
        if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window))
1193b4be Poul Sander 2025-02-03 21:28 5744
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5745
        ImDrawList* draw_list = GetForegroundDrawList(viewport);
1193b4be Poul Sander 2025-02-03 21:28 5746
        const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
1193b4be Poul Sander 2025-02-03 21:28 5747
        draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
1193b4be Poul Sander 2025-02-03 21:28 5748
    }
1193b4be Poul Sander 2025-02-03 21:28 5749
}
1193b4be Poul Sander 2025-02-03 21:28 5750
1193b4be Poul Sander 2025-02-03 21:28 5751
// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
1193b4be Poul Sander 2025-02-03 21:28 5752
void ImGui::EndFrame()
1193b4be Poul Sander 2025-02-03 21:28 5753
{
1193b4be Poul Sander 2025-02-03 21:28 5754
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5755
    IM_ASSERT(g.Initialized);
1193b4be Poul Sander 2025-02-03 21:28 5756
1193b4be Poul Sander 2025-02-03 21:28 5757
    // Don't process EndFrame() multiple times.
1193b4be Poul Sander 2025-02-03 21:28 5758
    if (g.FrameCountEnded == g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 5759
        return;
1193b4be Poul Sander 2025-02-03 21:28 5760
    IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
1193b4be Poul Sander 2025-02-03 21:28 5761
1193b4be Poul Sander 2025-02-03 21:28 5762
    CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
1193b4be Poul Sander 2025-02-03 21:28 5763
1193b4be Poul Sander 2025-02-03 21:28 5764
    // [EXPERIMENTAL] Recover from errors
1193b4be Poul Sander 2025-02-03 21:28 5765
    if (g.IO.ConfigErrorRecovery)
1193b4be Poul Sander 2025-02-03 21:28 5766
        ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
1193b4be Poul Sander 2025-02-03 21:28 5767
    ErrorCheckEndFrameSanityChecks();
1193b4be Poul Sander 2025-02-03 21:28 5768
    ErrorCheckEndFrameFinalizeErrorTooltip();
1193b4be Poul Sander 2025-02-03 21:28 5769
1193b4be Poul Sander 2025-02-03 21:28 5770
    // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
1193b4be Poul Sander 2025-02-03 21:28 5771
    ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
1193b4be Poul Sander 2025-02-03 21:28 5772
    if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 5773
    {
1193b4be Poul Sander 2025-02-03 21:28 5774
        ImGuiViewport* viewport = FindViewportByID(g.PlatformImeViewport);
1193b4be Poul Sander 2025-02-03 21:28 5775
        IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
1193b4be Poul Sander 2025-02-03 21:28 5776
        if (viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 5777
            viewport = GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 5778
        g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
1193b4be Poul Sander 2025-02-03 21:28 5779
    }
1193b4be Poul Sander 2025-02-03 21:28 5780
1193b4be Poul Sander 2025-02-03 21:28 5781
    // Hide implicit/fallback "Debug" window if it hasn't been used
1193b4be Poul Sander 2025-02-03 21:28 5782
    g.WithinFrameScopeWithImplicitWindow = false;
1193b4be Poul Sander 2025-02-03 21:28 5783
    if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
1193b4be Poul Sander 2025-02-03 21:28 5784
        g.CurrentWindow->Active = false;
1193b4be Poul Sander 2025-02-03 21:28 5785
    End();
1193b4be Poul Sander 2025-02-03 21:28 5786
1193b4be Poul Sander 2025-02-03 21:28 5787
    // Update navigation: CTRL+Tab, wrap-around requests
1193b4be Poul Sander 2025-02-03 21:28 5788
    NavEndFrame();
1193b4be Poul Sander 2025-02-03 21:28 5789
1193b4be Poul Sander 2025-02-03 21:28 5790
    // Update docking
1193b4be Poul Sander 2025-02-03 21:28 5791
    DockContextEndFrame(&g);
1193b4be Poul Sander 2025-02-03 21:28 5792
1193b4be Poul Sander 2025-02-03 21:28 5793
    SetCurrentViewport(NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 5794
1193b4be Poul Sander 2025-02-03 21:28 5795
    // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
1193b4be Poul Sander 2025-02-03 21:28 5796
    if (g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 5797
    {
1193b4be Poul Sander 2025-02-03 21:28 5798
        bool is_delivered = g.DragDropPayload.Delivery;
1193b4be Poul Sander 2025-02-03 21:28 5799
        bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
1193b4be Poul Sander 2025-02-03 21:28 5800
        if (is_delivered || is_elapsed)
1193b4be Poul Sander 2025-02-03 21:28 5801
            ClearDragDrop();
1193b4be Poul Sander 2025-02-03 21:28 5802
    }
1193b4be Poul Sander 2025-02-03 21:28 5803
1193b4be Poul Sander 2025-02-03 21:28 5804
    // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
1193b4be Poul Sander 2025-02-03 21:28 5805
    // If you want to handle source item disappearing: instead of submitting your description tooltip
1193b4be Poul Sander 2025-02-03 21:28 5806
    // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
1193b4be Poul Sander 2025-02-03 21:28 5807
    // (e.g. end of your item loop, or before EndFrame) by reading payload data.
1193b4be Poul Sander 2025-02-03 21:28 5808
    // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
1193b4be Poul Sander 2025-02-03 21:28 5809
    if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
1193b4be Poul Sander 2025-02-03 21:28 5810
    {
1193b4be Poul Sander 2025-02-03 21:28 5811
        g.DragDropWithinSource = true;
1193b4be Poul Sander 2025-02-03 21:28 5812
        SetTooltip("...");
1193b4be Poul Sander 2025-02-03 21:28 5813
        g.DragDropWithinSource = false;
1193b4be Poul Sander 2025-02-03 21:28 5814
    }
1193b4be Poul Sander 2025-02-03 21:28 5815
1193b4be Poul Sander 2025-02-03 21:28 5816
    // End frame
1193b4be Poul Sander 2025-02-03 21:28 5817
    g.WithinFrameScope = false;
1193b4be Poul Sander 2025-02-03 21:28 5818
    g.FrameCountEnded = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 5819
1193b4be Poul Sander 2025-02-03 21:28 5820
    // Initiate moving window + handle left-click and right-click focus
1193b4be Poul Sander 2025-02-03 21:28 5821
    UpdateMouseMovingWindowEndFrame();
1193b4be Poul Sander 2025-02-03 21:28 5822
1193b4be Poul Sander 2025-02-03 21:28 5823
    // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
1193b4be Poul Sander 2025-02-03 21:28 5824
    UpdateViewportsEndFrame();
1193b4be Poul Sander 2025-02-03 21:28 5825
1193b4be Poul Sander 2025-02-03 21:28 5826
    // Sort the window list so that all child windows are after their parent
1193b4be Poul Sander 2025-02-03 21:28 5827
    // We cannot do that on FocusWindow() because children may not exist yet
1193b4be Poul Sander 2025-02-03 21:28 5828
    g.WindowsTempSortBuffer.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5829
    g.WindowsTempSortBuffer.reserve(g.Windows.Size);
1193b4be Poul Sander 2025-02-03 21:28 5830
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 5831
    {
1193b4be Poul Sander 2025-02-03 21:28 5832
        if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
1193b4be Poul Sander 2025-02-03 21:28 5833
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5834
        AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
1193b4be Poul Sander 2025-02-03 21:28 5835
    }
1193b4be Poul Sander 2025-02-03 21:28 5836
1193b4be Poul Sander 2025-02-03 21:28 5837
    // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
1193b4be Poul Sander 2025-02-03 21:28 5838
    IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
1193b4be Poul Sander 2025-02-03 21:28 5839
    g.Windows.swap(g.WindowsTempSortBuffer);
1193b4be Poul Sander 2025-02-03 21:28 5840
    g.IO.MetricsActiveWindows = g.WindowsActiveCount;
1193b4be Poul Sander 2025-02-03 21:28 5841
1193b4be Poul Sander 2025-02-03 21:28 5842
    // Unlock font atlas
1193b4be Poul Sander 2025-02-03 21:28 5843
    g.IO.Fonts->Locked = false;
1193b4be Poul Sander 2025-02-03 21:28 5844
1193b4be Poul Sander 2025-02-03 21:28 5845
    // Clear Input data for next frame
1193b4be Poul Sander 2025-02-03 21:28 5846
    g.IO.MousePosPrev = g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 5847
    g.IO.AppFocusLost = false;
1193b4be Poul Sander 2025-02-03 21:28 5848
    g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 5849
    g.IO.InputQueueCharacters.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 5850
1193b4be Poul Sander 2025-02-03 21:28 5851
    CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
1193b4be Poul Sander 2025-02-03 21:28 5852
}
1193b4be Poul Sander 2025-02-03 21:28 5853
1193b4be Poul Sander 2025-02-03 21:28 5854
// Prepare the data for rendering so you can call GetDrawData()
1193b4be Poul Sander 2025-02-03 21:28 5855
// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
1193b4be Poul Sander 2025-02-03 21:28 5856
// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
1193b4be Poul Sander 2025-02-03 21:28 5857
void ImGui::Render()
1193b4be Poul Sander 2025-02-03 21:28 5858
{
1193b4be Poul Sander 2025-02-03 21:28 5859
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5860
    IM_ASSERT(g.Initialized);
1193b4be Poul Sander 2025-02-03 21:28 5861
1193b4be Poul Sander 2025-02-03 21:28 5862
    if (g.FrameCountEnded != g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 5863
        EndFrame();
1193b4be Poul Sander 2025-02-03 21:28 5864
    if (g.FrameCountRendered == g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 5865
        return;
1193b4be Poul Sander 2025-02-03 21:28 5866
    g.FrameCountRendered = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 5867
1193b4be Poul Sander 2025-02-03 21:28 5868
    g.IO.MetricsRenderWindows = 0;
1193b4be Poul Sander 2025-02-03 21:28 5869
    CallContextHooks(&g, ImGuiContextHookType_RenderPre);
1193b4be Poul Sander 2025-02-03 21:28 5870
1193b4be Poul Sander 2025-02-03 21:28 5871
    // Add background ImDrawList (for each active viewport)
1193b4be Poul Sander 2025-02-03 21:28 5872
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 5873
    {
1193b4be Poul Sander 2025-02-03 21:28 5874
        InitViewportDrawData(viewport);
1193b4be Poul Sander 2025-02-03 21:28 5875
        if (viewport->BgFgDrawLists[0] != NULL)
1193b4be Poul Sander 2025-02-03 21:28 5876
            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
1193b4be Poul Sander 2025-02-03 21:28 5877
    }
1193b4be Poul Sander 2025-02-03 21:28 5878
1193b4be Poul Sander 2025-02-03 21:28 5879
    // Draw modal/window whitening backgrounds
1193b4be Poul Sander 2025-02-03 21:28 5880
    RenderDimmedBackgrounds();
1193b4be Poul Sander 2025-02-03 21:28 5881
1193b4be Poul Sander 2025-02-03 21:28 5882
    // Add ImDrawList to render
1193b4be Poul Sander 2025-02-03 21:28 5883
    ImGuiWindow* windows_to_render_top_most[2];
1193b4be Poul Sander 2025-02-03 21:28 5884
    windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;
1193b4be Poul Sander 2025-02-03 21:28 5885
    windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
1193b4be Poul Sander 2025-02-03 21:28 5886
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 5887
    {
1193b4be Poul Sander 2025-02-03 21:28 5888
        IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
1193b4be Poul Sander 2025-02-03 21:28 5889
        if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
1193b4be Poul Sander 2025-02-03 21:28 5890
            AddRootWindowToDrawData(window);
1193b4be Poul Sander 2025-02-03 21:28 5891
    }
1193b4be Poul Sander 2025-02-03 21:28 5892
    for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
1193b4be Poul Sander 2025-02-03 21:28 5893
        if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
1193b4be Poul Sander 2025-02-03 21:28 5894
            AddRootWindowToDrawData(windows_to_render_top_most[n]);
1193b4be Poul Sander 2025-02-03 21:28 5895
1193b4be Poul Sander 2025-02-03 21:28 5896
    // Draw software mouse cursor if requested by io.MouseDrawCursor flag
1193b4be Poul Sander 2025-02-03 21:28 5897
    if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
1193b4be Poul Sander 2025-02-03 21:28 5898
        RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
1193b4be Poul Sander 2025-02-03 21:28 5899
1193b4be Poul Sander 2025-02-03 21:28 5900
    // Setup ImDrawData structures for end-user
1193b4be Poul Sander 2025-02-03 21:28 5901
    g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
1193b4be Poul Sander 2025-02-03 21:28 5902
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 5903
    {
1193b4be Poul Sander 2025-02-03 21:28 5904
        FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
1193b4be Poul Sander 2025-02-03 21:28 5905
1193b4be Poul Sander 2025-02-03 21:28 5906
        // Add foreground ImDrawList (for each active viewport)
1193b4be Poul Sander 2025-02-03 21:28 5907
        if (viewport->BgFgDrawLists[1] != NULL)
1193b4be Poul Sander 2025-02-03 21:28 5908
            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
1193b4be Poul Sander 2025-02-03 21:28 5909
1193b4be Poul Sander 2025-02-03 21:28 5910
        // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
1193b4be Poul Sander 2025-02-03 21:28 5911
        ImDrawData* draw_data = &viewport->DrawDataP;
1193b4be Poul Sander 2025-02-03 21:28 5912
        IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
1193b4be Poul Sander 2025-02-03 21:28 5913
        for (ImDrawList* draw_list : draw_data->CmdLists)
1193b4be Poul Sander 2025-02-03 21:28 5914
            draw_list->_PopUnusedDrawCmd();
1193b4be Poul Sander 2025-02-03 21:28 5915
1193b4be Poul Sander 2025-02-03 21:28 5916
        g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
1193b4be Poul Sander 2025-02-03 21:28 5917
        g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
1193b4be Poul Sander 2025-02-03 21:28 5918
    }
1193b4be Poul Sander 2025-02-03 21:28 5919
1193b4be Poul Sander 2025-02-03 21:28 5920
    CallContextHooks(&g, ImGuiContextHookType_RenderPost);
1193b4be Poul Sander 2025-02-03 21:28 5921
}
1193b4be Poul Sander 2025-02-03 21:28 5922
1193b4be Poul Sander 2025-02-03 21:28 5923
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
1193b4be Poul Sander 2025-02-03 21:28 5924
// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
1193b4be Poul Sander 2025-02-03 21:28 5925
ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
1193b4be Poul Sander 2025-02-03 21:28 5926
{
1193b4be Poul Sander 2025-02-03 21:28 5927
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5928
1193b4be Poul Sander 2025-02-03 21:28 5929
    const char* text_display_end;
1193b4be Poul Sander 2025-02-03 21:28 5930
    if (hide_text_after_double_hash)
1193b4be Poul Sander 2025-02-03 21:28 5931
        text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
1193b4be Poul Sander 2025-02-03 21:28 5932
    else
1193b4be Poul Sander 2025-02-03 21:28 5933
        text_display_end = text_end;
1193b4be Poul Sander 2025-02-03 21:28 5934
1193b4be Poul Sander 2025-02-03 21:28 5935
    ImFont* font = g.Font;
1193b4be Poul Sander 2025-02-03 21:28 5936
    const float font_size = g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 5937
    if (text == text_display_end)
1193b4be Poul Sander 2025-02-03 21:28 5938
        return ImVec2(0.0f, font_size);
1193b4be Poul Sander 2025-02-03 21:28 5939
    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
1193b4be Poul Sander 2025-02-03 21:28 5940
1193b4be Poul Sander 2025-02-03 21:28 5941
    // Round
1193b4be Poul Sander 2025-02-03 21:28 5942
    // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
1193b4be Poul Sander 2025-02-03 21:28 5943
    // FIXME: Investigate using ceilf or e.g.
1193b4be Poul Sander 2025-02-03 21:28 5944
    // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
1193b4be Poul Sander 2025-02-03 21:28 5945
    // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
1193b4be Poul Sander 2025-02-03 21:28 5946
    text_size.x = IM_TRUNC(text_size.x + 0.99999f);
1193b4be Poul Sander 2025-02-03 21:28 5947
1193b4be Poul Sander 2025-02-03 21:28 5948
    return text_size;
1193b4be Poul Sander 2025-02-03 21:28 5949
}
1193b4be Poul Sander 2025-02-03 21:28 5950
1193b4be Poul Sander 2025-02-03 21:28 5951
// Find window given position, search front-to-back
1193b4be Poul Sander 2025-02-03 21:28 5952
// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
1193b4be Poul Sander 2025-02-03 21:28 5953
// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
1193b4be Poul Sander 2025-02-03 21:28 5954
//   with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
1193b4be Poul Sander 2025-02-03 21:28 5955
//   called, aka before the next Begin(). Moving window isn't affected.
1193b4be Poul Sander 2025-02-03 21:28 5956
// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
1193b4be Poul Sander 2025-02-03 21:28 5957
void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
1193b4be Poul Sander 2025-02-03 21:28 5958
{
1193b4be Poul Sander 2025-02-03 21:28 5959
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 5960
    ImGuiWindow* hovered_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5961
    ImGuiWindow* hovered_window_under_moving_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5962
1193b4be Poul Sander 2025-02-03 21:28 5963
    // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
1193b4be Poul Sander 2025-02-03 21:28 5964
    ImGuiViewportP* backup_moving_window_viewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 5965
    if (find_first_and_in_any_viewport == false && g.MovingWindow)
1193b4be Poul Sander 2025-02-03 21:28 5966
    {
1193b4be Poul Sander 2025-02-03 21:28 5967
        backup_moving_window_viewport = g.MovingWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 5968
        g.MovingWindow->Viewport = g.MouseViewport;
1193b4be Poul Sander 2025-02-03 21:28 5969
        if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
1193b4be Poul Sander 2025-02-03 21:28 5970
            hovered_window = g.MovingWindow;
1193b4be Poul Sander 2025-02-03 21:28 5971
    }
1193b4be Poul Sander 2025-02-03 21:28 5972
1193b4be Poul Sander 2025-02-03 21:28 5973
    ImVec2 padding_regular = g.Style.TouchExtraPadding;
1193b4be Poul Sander 2025-02-03 21:28 5974
    ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
1193b4be Poul Sander 2025-02-03 21:28 5975
    for (int i = g.Windows.Size - 1; i >= 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 5976
    {
1193b4be Poul Sander 2025-02-03 21:28 5977
        ImGuiWindow* window = g.Windows[i];
1193b4be Poul Sander 2025-02-03 21:28 5978
        IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
1193b4be Poul Sander 2025-02-03 21:28 5979
        if (!window->WasActive || window->Hidden)
1193b4be Poul Sander 2025-02-03 21:28 5980
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5981
        if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
1193b4be Poul Sander 2025-02-03 21:28 5982
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5983
        IM_ASSERT(window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 5984
        if (window->Viewport != g.MouseViewport)
1193b4be Poul Sander 2025-02-03 21:28 5985
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5986
1193b4be Poul Sander 2025-02-03 21:28 5987
        // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
1193b4be Poul Sander 2025-02-03 21:28 5988
        ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
1193b4be Poul Sander 2025-02-03 21:28 5989
        if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
1193b4be Poul Sander 2025-02-03 21:28 5990
            continue;
1193b4be Poul Sander 2025-02-03 21:28 5991
1193b4be Poul Sander 2025-02-03 21:28 5992
        // Support for one rectangular hole in any given window
1193b4be Poul Sander 2025-02-03 21:28 5993
        // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
1193b4be Poul Sander 2025-02-03 21:28 5994
        if (window->HitTestHoleSize.x != 0)
1193b4be Poul Sander 2025-02-03 21:28 5995
        {
1193b4be Poul Sander 2025-02-03 21:28 5996
            ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
1193b4be Poul Sander 2025-02-03 21:28 5997
            ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
1193b4be Poul Sander 2025-02-03 21:28 5998
            if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
1193b4be Poul Sander 2025-02-03 21:28 5999
                continue;
1193b4be Poul Sander 2025-02-03 21:28 6000
        }
1193b4be Poul Sander 2025-02-03 21:28 6001
1193b4be Poul Sander 2025-02-03 21:28 6002
        if (find_first_and_in_any_viewport)
1193b4be Poul Sander 2025-02-03 21:28 6003
        {
1193b4be Poul Sander 2025-02-03 21:28 6004
            hovered_window = window;
1193b4be Poul Sander 2025-02-03 21:28 6005
            break;
1193b4be Poul Sander 2025-02-03 21:28 6006
        }
1193b4be Poul Sander 2025-02-03 21:28 6007
        else
1193b4be Poul Sander 2025-02-03 21:28 6008
        {
1193b4be Poul Sander 2025-02-03 21:28 6009
            if (hovered_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 6010
                hovered_window = window;
1193b4be Poul Sander 2025-02-03 21:28 6011
            IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
1193b4be Poul Sander 2025-02-03 21:28 6012
            if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))
1193b4be Poul Sander 2025-02-03 21:28 6013
                hovered_window_under_moving_window = window;
1193b4be Poul Sander 2025-02-03 21:28 6014
            if (hovered_window && hovered_window_under_moving_window)
1193b4be Poul Sander 2025-02-03 21:28 6015
                break;
1193b4be Poul Sander 2025-02-03 21:28 6016
        }
1193b4be Poul Sander 2025-02-03 21:28 6017
    }
1193b4be Poul Sander 2025-02-03 21:28 6018
1193b4be Poul Sander 2025-02-03 21:28 6019
    *out_hovered_window = hovered_window;
1193b4be Poul Sander 2025-02-03 21:28 6020
    if (out_hovered_window_under_moving_window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 6021
        *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
1193b4be Poul Sander 2025-02-03 21:28 6022
    if (find_first_and_in_any_viewport == false && g.MovingWindow)
1193b4be Poul Sander 2025-02-03 21:28 6023
        g.MovingWindow->Viewport = backup_moving_window_viewport;
1193b4be Poul Sander 2025-02-03 21:28 6024
}
1193b4be Poul Sander 2025-02-03 21:28 6025
1193b4be Poul Sander 2025-02-03 21:28 6026
bool ImGui::IsItemActive()
1193b4be Poul Sander 2025-02-03 21:28 6027
{
1193b4be Poul Sander 2025-02-03 21:28 6028
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6029
    if (g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 6030
        return g.ActiveId == g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 6031
    return false;
1193b4be Poul Sander 2025-02-03 21:28 6032
}
1193b4be Poul Sander 2025-02-03 21:28 6033
1193b4be Poul Sander 2025-02-03 21:28 6034
bool ImGui::IsItemActivated()
1193b4be Poul Sander 2025-02-03 21:28 6035
{
1193b4be Poul Sander 2025-02-03 21:28 6036
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6037
    if (g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 6038
        if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
1193b4be Poul Sander 2025-02-03 21:28 6039
            return true;
1193b4be Poul Sander 2025-02-03 21:28 6040
    return false;
1193b4be Poul Sander 2025-02-03 21:28 6041
}
1193b4be Poul Sander 2025-02-03 21:28 6042
1193b4be Poul Sander 2025-02-03 21:28 6043
bool ImGui::IsItemDeactivated()
1193b4be Poul Sander 2025-02-03 21:28 6044
{
1193b4be Poul Sander 2025-02-03 21:28 6045
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6046
    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
1193b4be Poul Sander 2025-02-03 21:28 6047
        return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
1193b4be Poul Sander 2025-02-03 21:28 6048
    return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
1193b4be Poul Sander 2025-02-03 21:28 6049
}
1193b4be Poul Sander 2025-02-03 21:28 6050
1193b4be Poul Sander 2025-02-03 21:28 6051
bool ImGui::IsItemDeactivatedAfterEdit()
1193b4be Poul Sander 2025-02-03 21:28 6052
{
1193b4be Poul Sander 2025-02-03 21:28 6053
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6054
    return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
1193b4be Poul Sander 2025-02-03 21:28 6055
}
1193b4be Poul Sander 2025-02-03 21:28 6056
1193b4be Poul Sander 2025-02-03 21:28 6057
// == GetItemID() == GetFocusID()
1193b4be Poul Sander 2025-02-03 21:28 6058
bool ImGui::IsItemFocused()
1193b4be Poul Sander 2025-02-03 21:28 6059
{
1193b4be Poul Sander 2025-02-03 21:28 6060
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6061
    if (g.NavId != g.LastItemData.ID || g.NavId == 0)
1193b4be Poul Sander 2025-02-03 21:28 6062
        return false;
1193b4be Poul Sander 2025-02-03 21:28 6063
1193b4be Poul Sander 2025-02-03 21:28 6064
    // Special handling for the dummy item after Begin() which represent the title bar or tab.
1193b4be Poul Sander 2025-02-03 21:28 6065
    // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
1193b4be Poul Sander 2025-02-03 21:28 6066
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6067
    if (g.LastItemData.ID == window->ID && window->WriteAccessed)
1193b4be Poul Sander 2025-02-03 21:28 6068
        return false;
1193b4be Poul Sander 2025-02-03 21:28 6069
1193b4be Poul Sander 2025-02-03 21:28 6070
    return true;
1193b4be Poul Sander 2025-02-03 21:28 6071
}
1193b4be Poul Sander 2025-02-03 21:28 6072
1193b4be Poul Sander 2025-02-03 21:28 6073
// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
1193b4be Poul Sander 2025-02-03 21:28 6074
// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
1193b4be Poul Sander 2025-02-03 21:28 6075
bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
1193b4be Poul Sander 2025-02-03 21:28 6076
{
1193b4be Poul Sander 2025-02-03 21:28 6077
    return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 6078
}
1193b4be Poul Sander 2025-02-03 21:28 6079
1193b4be Poul Sander 2025-02-03 21:28 6080
bool ImGui::IsItemToggledOpen()
1193b4be Poul Sander 2025-02-03 21:28 6081
{
1193b4be Poul Sander 2025-02-03 21:28 6082
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6083
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
1193b4be Poul Sander 2025-02-03 21:28 6084
}
1193b4be Poul Sander 2025-02-03 21:28 6085
1193b4be Poul Sander 2025-02-03 21:28 6086
// Call after a Selectable() or TreeNode() involved in multi-selection.
1193b4be Poul Sander 2025-02-03 21:28 6087
// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
1193b4be Poul Sander 2025-02-03 21:28 6088
// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
1193b4be Poul Sander 2025-02-03 21:28 6089
// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
1193b4be Poul Sander 2025-02-03 21:28 6090
// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
1193b4be Poul Sander 2025-02-03 21:28 6091
bool ImGui::IsItemToggledSelection()
1193b4be Poul Sander 2025-02-03 21:28 6092
{
1193b4be Poul Sander 2025-02-03 21:28 6093
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6094
    IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
1193b4be Poul Sander 2025-02-03 21:28 6095
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
1193b4be Poul Sander 2025-02-03 21:28 6096
}
1193b4be Poul Sander 2025-02-03 21:28 6097
1193b4be Poul Sander 2025-02-03 21:28 6098
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
1193b4be Poul Sander 2025-02-03 21:28 6099
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
1193b4be Poul Sander 2025-02-03 21:28 6100
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
1193b4be Poul Sander 2025-02-03 21:28 6101
bool ImGui::IsAnyItemHovered()
1193b4be Poul Sander 2025-02-03 21:28 6102
{
1193b4be Poul Sander 2025-02-03 21:28 6103
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6104
    return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
1193b4be Poul Sander 2025-02-03 21:28 6105
}
1193b4be Poul Sander 2025-02-03 21:28 6106
1193b4be Poul Sander 2025-02-03 21:28 6107
bool ImGui::IsAnyItemActive()
1193b4be Poul Sander 2025-02-03 21:28 6108
{
1193b4be Poul Sander 2025-02-03 21:28 6109
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6110
    return g.ActiveId != 0;
1193b4be Poul Sander 2025-02-03 21:28 6111
}
1193b4be Poul Sander 2025-02-03 21:28 6112
1193b4be Poul Sander 2025-02-03 21:28 6113
bool ImGui::IsAnyItemFocused()
1193b4be Poul Sander 2025-02-03 21:28 6114
{
1193b4be Poul Sander 2025-02-03 21:28 6115
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6116
    return g.NavId != 0 && g.NavCursorVisible;
1193b4be Poul Sander 2025-02-03 21:28 6117
}
1193b4be Poul Sander 2025-02-03 21:28 6118
1193b4be Poul Sander 2025-02-03 21:28 6119
bool ImGui::IsItemVisible()
1193b4be Poul Sander 2025-02-03 21:28 6120
{
1193b4be Poul Sander 2025-02-03 21:28 6121
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6122
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
1193b4be Poul Sander 2025-02-03 21:28 6123
}
1193b4be Poul Sander 2025-02-03 21:28 6124
1193b4be Poul Sander 2025-02-03 21:28 6125
bool ImGui::IsItemEdited()
1193b4be Poul Sander 2025-02-03 21:28 6126
{
1193b4be Poul Sander 2025-02-03 21:28 6127
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6128
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
1193b4be Poul Sander 2025-02-03 21:28 6129
}
1193b4be Poul Sander 2025-02-03 21:28 6130
1193b4be Poul Sander 2025-02-03 21:28 6131
// Allow next item to be overlapped by subsequent items.
1193b4be Poul Sander 2025-02-03 21:28 6132
// This works by requiring HoveredId to match for two subsequent frames,
1193b4be Poul Sander 2025-02-03 21:28 6133
// so if a following items overwrite it our interactions will naturally be disabled.
1193b4be Poul Sander 2025-02-03 21:28 6134
void ImGui::SetNextItemAllowOverlap()
1193b4be Poul Sander 2025-02-03 21:28 6135
{
1193b4be Poul Sander 2025-02-03 21:28 6136
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6137
    g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
1193b4be Poul Sander 2025-02-03 21:28 6138
}
1193b4be Poul Sander 2025-02-03 21:28 6139
1193b4be Poul Sander 2025-02-03 21:28 6140
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 6141
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
1193b4be Poul Sander 2025-02-03 21:28 6142
// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
1193b4be Poul Sander 2025-02-03 21:28 6143
void ImGui::SetItemAllowOverlap()
1193b4be Poul Sander 2025-02-03 21:28 6144
{
1193b4be Poul Sander 2025-02-03 21:28 6145
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6146
    ImGuiID id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 6147
    if (g.HoveredId == id)
1193b4be Poul Sander 2025-02-03 21:28 6148
        g.HoveredIdAllowOverlap = true;
1193b4be Poul Sander 2025-02-03 21:28 6149
    if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
1193b4be Poul Sander 2025-02-03 21:28 6150
        g.ActiveIdAllowOverlap = true;
1193b4be Poul Sander 2025-02-03 21:28 6151
}
1193b4be Poul Sander 2025-02-03 21:28 6152
#endif
1193b4be Poul Sander 2025-02-03 21:28 6153
1193b4be Poul Sander 2025-02-03 21:28 6154
// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
1193b4be Poul Sander 2025-02-03 21:28 6155
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
1193b4be Poul Sander 2025-02-03 21:28 6156
void ImGui::SetActiveIdUsingAllKeyboardKeys()
1193b4be Poul Sander 2025-02-03 21:28 6157
{
1193b4be Poul Sander 2025-02-03 21:28 6158
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6159
    IM_ASSERT(g.ActiveId != 0);
1193b4be Poul Sander 2025-02-03 21:28 6160
    g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
1193b4be Poul Sander 2025-02-03 21:28 6161
    g.ActiveIdUsingAllKeyboardKeys = true;
1193b4be Poul Sander 2025-02-03 21:28 6162
    NavMoveRequestCancel();
1193b4be Poul Sander 2025-02-03 21:28 6163
}
1193b4be Poul Sander 2025-02-03 21:28 6164
1193b4be Poul Sander 2025-02-03 21:28 6165
ImGuiID ImGui::GetItemID()
1193b4be Poul Sander 2025-02-03 21:28 6166
{
1193b4be Poul Sander 2025-02-03 21:28 6167
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6168
    return g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 6169
}
1193b4be Poul Sander 2025-02-03 21:28 6170
1193b4be Poul Sander 2025-02-03 21:28 6171
ImVec2 ImGui::GetItemRectMin()
1193b4be Poul Sander 2025-02-03 21:28 6172
{
1193b4be Poul Sander 2025-02-03 21:28 6173
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6174
    return g.LastItemData.Rect.Min;
1193b4be Poul Sander 2025-02-03 21:28 6175
}
1193b4be Poul Sander 2025-02-03 21:28 6176
1193b4be Poul Sander 2025-02-03 21:28 6177
ImVec2 ImGui::GetItemRectMax()
1193b4be Poul Sander 2025-02-03 21:28 6178
{
1193b4be Poul Sander 2025-02-03 21:28 6179
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6180
    return g.LastItemData.Rect.Max;
1193b4be Poul Sander 2025-02-03 21:28 6181
}
1193b4be Poul Sander 2025-02-03 21:28 6182
1193b4be Poul Sander 2025-02-03 21:28 6183
ImVec2 ImGui::GetItemRectSize()
1193b4be Poul Sander 2025-02-03 21:28 6184
{
1193b4be Poul Sander 2025-02-03 21:28 6185
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6186
    return g.LastItemData.Rect.GetSize();
1193b4be Poul Sander 2025-02-03 21:28 6187
}
1193b4be Poul Sander 2025-02-03 21:28 6188
1193b4be Poul Sander 2025-02-03 21:28 6189
// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
1193b4be Poul Sander 2025-02-03 21:28 6190
// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
1193b4be Poul Sander 2025-02-03 21:28 6191
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
1193b4be Poul Sander 2025-02-03 21:28 6192
{
1193b4be Poul Sander 2025-02-03 21:28 6193
    ImGuiID id = GetCurrentWindow()->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 6194
    return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
1193b4be Poul Sander 2025-02-03 21:28 6195
}
1193b4be Poul Sander 2025-02-03 21:28 6196
1193b4be Poul Sander 2025-02-03 21:28 6197
bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
1193b4be Poul Sander 2025-02-03 21:28 6198
{
1193b4be Poul Sander 2025-02-03 21:28 6199
    return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
1193b4be Poul Sander 2025-02-03 21:28 6200
}
1193b4be Poul Sander 2025-02-03 21:28 6201
1193b4be Poul Sander 2025-02-03 21:28 6202
bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
1193b4be Poul Sander 2025-02-03 21:28 6203
{
1193b4be Poul Sander 2025-02-03 21:28 6204
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6205
    ImGuiWindow* parent_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6206
    IM_ASSERT(id != 0);
1193b4be Poul Sander 2025-02-03 21:28 6207
1193b4be Poul Sander 2025-02-03 21:28 6208
    // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
1193b4be Poul Sander 2025-02-03 21:28 6209
    const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
1193b4be Poul Sander 2025-02-03 21:28 6210
    IM_UNUSED(ImGuiChildFlags_SupportedMask_);
1193b4be Poul Sander 2025-02-03 21:28 6211
    IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
1193b4be Poul Sander 2025-02-03 21:28 6212
    IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
1193b4be Poul Sander 2025-02-03 21:28 6213
    if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
1193b4be Poul Sander 2025-02-03 21:28 6214
    {
1193b4be Poul Sander 2025-02-03 21:28 6215
        IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
1193b4be Poul Sander 2025-02-03 21:28 6216
        IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
1193b4be Poul Sander 2025-02-03 21:28 6217
    }
1193b4be Poul Sander 2025-02-03 21:28 6218
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 6219
    if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
1193b4be Poul Sander 2025-02-03 21:28 6220
        child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 6221
    if (window_flags & ImGuiWindowFlags_NavFlattened)
1193b4be Poul Sander 2025-02-03 21:28 6222
        child_flags |= ImGuiChildFlags_NavFlattened;
1193b4be Poul Sander 2025-02-03 21:28 6223
#endif
1193b4be Poul Sander 2025-02-03 21:28 6224
    if (child_flags & ImGuiChildFlags_AutoResizeX)
1193b4be Poul Sander 2025-02-03 21:28 6225
        child_flags &= ~ImGuiChildFlags_ResizeX;
1193b4be Poul Sander 2025-02-03 21:28 6226
    if (child_flags & ImGuiChildFlags_AutoResizeY)
1193b4be Poul Sander 2025-02-03 21:28 6227
        child_flags &= ~ImGuiChildFlags_ResizeY;
1193b4be Poul Sander 2025-02-03 21:28 6228
1193b4be Poul Sander 2025-02-03 21:28 6229
    // Set window flags
1193b4be Poul Sander 2025-02-03 21:28 6230
    window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking;
1193b4be Poul Sander 2025-02-03 21:28 6231
    window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
1193b4be Poul Sander 2025-02-03 21:28 6232
    if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
1193b4be Poul Sander 2025-02-03 21:28 6233
        window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
1193b4be Poul Sander 2025-02-03 21:28 6234
    if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 6235
        window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
1193b4be Poul Sander 2025-02-03 21:28 6236
1193b4be Poul Sander 2025-02-03 21:28 6237
    // Special framed style
1193b4be Poul Sander 2025-02-03 21:28 6238
    if (child_flags & ImGuiChildFlags_FrameStyle)
1193b4be Poul Sander 2025-02-03 21:28 6239
    {
1193b4be Poul Sander 2025-02-03 21:28 6240
        PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
1193b4be Poul Sander 2025-02-03 21:28 6241
        PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
1193b4be Poul Sander 2025-02-03 21:28 6242
        PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
1193b4be Poul Sander 2025-02-03 21:28 6243
        PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
1193b4be Poul Sander 2025-02-03 21:28 6244
        child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 6245
        window_flags |= ImGuiWindowFlags_NoMove;
1193b4be Poul Sander 2025-02-03 21:28 6246
    }
1193b4be Poul Sander 2025-02-03 21:28 6247
1193b4be Poul Sander 2025-02-03 21:28 6248
    // Forward size
1193b4be Poul Sander 2025-02-03 21:28 6249
    // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
1193b4be Poul Sander 2025-02-03 21:28 6250
    // (the alternative would to store conditional flags per axis, which is possible but more code)
1193b4be Poul Sander 2025-02-03 21:28 6251
    const ImVec2 size_avail = GetContentRegionAvail();
1193b4be Poul Sander 2025-02-03 21:28 6252
    const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
1193b4be Poul Sander 2025-02-03 21:28 6253
    ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
1193b4be Poul Sander 2025-02-03 21:28 6254
1193b4be Poul Sander 2025-02-03 21:28 6255
    // A SetNextWindowSize() call always has priority (#8020)
1193b4be Poul Sander 2025-02-03 21:28 6256
    // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
1193b4be Poul Sander 2025-02-03 21:28 6257
    // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
1193b4be Poul Sander 2025-02-03 21:28 6258
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
1193b4be Poul Sander 2025-02-03 21:28 6259
    {
1193b4be Poul Sander 2025-02-03 21:28 6260
        if (g.NextWindowData.SizeVal.x > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6261
        {
1193b4be Poul Sander 2025-02-03 21:28 6262
            size.x = g.NextWindowData.SizeVal.x;
1193b4be Poul Sander 2025-02-03 21:28 6263
            child_flags &= ~ImGuiChildFlags_ResizeX;
1193b4be Poul Sander 2025-02-03 21:28 6264
        }
1193b4be Poul Sander 2025-02-03 21:28 6265
        if (g.NextWindowData.SizeVal.y > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6266
        {
1193b4be Poul Sander 2025-02-03 21:28 6267
            size.y = g.NextWindowData.SizeVal.y;
1193b4be Poul Sander 2025-02-03 21:28 6268
            child_flags &= ~ImGuiChildFlags_ResizeY;
1193b4be Poul Sander 2025-02-03 21:28 6269
        }
1193b4be Poul Sander 2025-02-03 21:28 6270
    }
1193b4be Poul Sander 2025-02-03 21:28 6271
    SetNextWindowSize(size);
1193b4be Poul Sander 2025-02-03 21:28 6272
1193b4be Poul Sander 2025-02-03 21:28 6273
    // Forward child flags
1193b4be Poul Sander 2025-02-03 21:28 6274
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
1193b4be Poul Sander 2025-02-03 21:28 6275
    g.NextWindowData.ChildFlags = child_flags;
1193b4be Poul Sander 2025-02-03 21:28 6276
1193b4be Poul Sander 2025-02-03 21:28 6277
    // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
1193b4be Poul Sander 2025-02-03 21:28 6278
    // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
1193b4be Poul Sander 2025-02-03 21:28 6279
    // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
1193b4be Poul Sander 2025-02-03 21:28 6280
    const char* temp_window_name;
1193b4be Poul Sander 2025-02-03 21:28 6281
    /*if (name && parent_window->IDStack.back() == parent_window->ID)
1193b4be Poul Sander 2025-02-03 21:28 6282
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
1193b4be Poul Sander 2025-02-03 21:28 6283
    else*/
1193b4be Poul Sander 2025-02-03 21:28 6284
    if (name)
1193b4be Poul Sander 2025-02-03 21:28 6285
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
1193b4be Poul Sander 2025-02-03 21:28 6286
    else
1193b4be Poul Sander 2025-02-03 21:28 6287
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
1193b4be Poul Sander 2025-02-03 21:28 6288
1193b4be Poul Sander 2025-02-03 21:28 6289
    // Set style
1193b4be Poul Sander 2025-02-03 21:28 6290
    const float backup_border_size = g.Style.ChildBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 6291
    if ((child_flags & ImGuiChildFlags_Borders) == 0)
1193b4be Poul Sander 2025-02-03 21:28 6292
        g.Style.ChildBorderSize = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 6293
1193b4be Poul Sander 2025-02-03 21:28 6294
    // Begin into window
1193b4be Poul Sander 2025-02-03 21:28 6295
    const bool ret = Begin(temp_window_name, NULL, window_flags);
1193b4be Poul Sander 2025-02-03 21:28 6296
1193b4be Poul Sander 2025-02-03 21:28 6297
    // Restore style
1193b4be Poul Sander 2025-02-03 21:28 6298
    g.Style.ChildBorderSize = backup_border_size;
1193b4be Poul Sander 2025-02-03 21:28 6299
    if (child_flags & ImGuiChildFlags_FrameStyle)
1193b4be Poul Sander 2025-02-03 21:28 6300
    {
1193b4be Poul Sander 2025-02-03 21:28 6301
        PopStyleVar(3);
1193b4be Poul Sander 2025-02-03 21:28 6302
        PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 6303
    }
1193b4be Poul Sander 2025-02-03 21:28 6304
1193b4be Poul Sander 2025-02-03 21:28 6305
    ImGuiWindow* child_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6306
    child_window->ChildId = id;
1193b4be Poul Sander 2025-02-03 21:28 6307
1193b4be Poul Sander 2025-02-03 21:28 6308
    // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
1193b4be Poul Sander 2025-02-03 21:28 6309
    // While this is not really documented/defined, it seems that the expected thing to do.
1193b4be Poul Sander 2025-02-03 21:28 6310
    if (child_window->BeginCount == 1)
1193b4be Poul Sander 2025-02-03 21:28 6311
        parent_window->DC.CursorPos = child_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6312
1193b4be Poul Sander 2025-02-03 21:28 6313
    // Process navigation-in immediately so NavInit can run on first frame
1193b4be Poul Sander 2025-02-03 21:28 6314
    // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
1193b4be Poul Sander 2025-02-03 21:28 6315
    const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
1193b4be Poul Sander 2025-02-03 21:28 6316
    if (g.ActiveId == temp_id_for_activation)
1193b4be Poul Sander 2025-02-03 21:28 6317
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 6318
    if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
1193b4be Poul Sander 2025-02-03 21:28 6319
    {
1193b4be Poul Sander 2025-02-03 21:28 6320
        FocusWindow(child_window);
1193b4be Poul Sander 2025-02-03 21:28 6321
        NavInitWindow(child_window, false);
1193b4be Poul Sander 2025-02-03 21:28 6322
        SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
1193b4be Poul Sander 2025-02-03 21:28 6323
        g.ActiveIdSource = g.NavInputSource;
1193b4be Poul Sander 2025-02-03 21:28 6324
    }
1193b4be Poul Sander 2025-02-03 21:28 6325
    return ret;
1193b4be Poul Sander 2025-02-03 21:28 6326
}
1193b4be Poul Sander 2025-02-03 21:28 6327
1193b4be Poul Sander 2025-02-03 21:28 6328
void ImGui::EndChild()
1193b4be Poul Sander 2025-02-03 21:28 6329
{
1193b4be Poul Sander 2025-02-03 21:28 6330
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6331
    ImGuiWindow* child_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6332
1193b4be Poul Sander 2025-02-03 21:28 6333
    IM_ASSERT(g.WithinEndChild == false);
1193b4be Poul Sander 2025-02-03 21:28 6334
    IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
1193b4be Poul Sander 2025-02-03 21:28 6335
1193b4be Poul Sander 2025-02-03 21:28 6336
    g.WithinEndChild = true;
1193b4be Poul Sander 2025-02-03 21:28 6337
    ImVec2 child_size = child_window->Size;
1193b4be Poul Sander 2025-02-03 21:28 6338
    End();
1193b4be Poul Sander 2025-02-03 21:28 6339
    if (child_window->BeginCount == 1)
1193b4be Poul Sander 2025-02-03 21:28 6340
    {
1193b4be Poul Sander 2025-02-03 21:28 6341
        ImGuiWindow* parent_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6342
        ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
1193b4be Poul Sander 2025-02-03 21:28 6343
        ItemSize(child_size);
1193b4be Poul Sander 2025-02-03 21:28 6344
        const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
1193b4be Poul Sander 2025-02-03 21:28 6345
        if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
1193b4be Poul Sander 2025-02-03 21:28 6346
        {
1193b4be Poul Sander 2025-02-03 21:28 6347
            ItemAdd(bb, child_window->ChildId);
1193b4be Poul Sander 2025-02-03 21:28 6348
            RenderNavCursor(bb, child_window->ChildId);
1193b4be Poul Sander 2025-02-03 21:28 6349
1193b4be Poul Sander 2025-02-03 21:28 6350
            // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
1193b4be Poul Sander 2025-02-03 21:28 6351
            if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 6352
                RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
1193b4be Poul Sander 2025-02-03 21:28 6353
        }
1193b4be Poul Sander 2025-02-03 21:28 6354
        else
1193b4be Poul Sander 2025-02-03 21:28 6355
        {
1193b4be Poul Sander 2025-02-03 21:28 6356
            // Not navigable into
1193b4be Poul Sander 2025-02-03 21:28 6357
            // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
1193b4be Poul Sander 2025-02-03 21:28 6358
            // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
1193b4be Poul Sander 2025-02-03 21:28 6359
            ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
1193b4be Poul Sander 2025-02-03 21:28 6360
1193b4be Poul Sander 2025-02-03 21:28 6361
            // But when flattened we directly reach items, adjust active layer mask accordingly
1193b4be Poul Sander 2025-02-03 21:28 6362
            if (nav_flattened)
1193b4be Poul Sander 2025-02-03 21:28 6363
                parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
1193b4be Poul Sander 2025-02-03 21:28 6364
        }
1193b4be Poul Sander 2025-02-03 21:28 6365
        if (g.HoveredWindow == child_window)
1193b4be Poul Sander 2025-02-03 21:28 6366
            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 6367
    }
1193b4be Poul Sander 2025-02-03 21:28 6368
    g.WithinEndChild = false;
1193b4be Poul Sander 2025-02-03 21:28 6369
    g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
1193b4be Poul Sander 2025-02-03 21:28 6370
}
1193b4be Poul Sander 2025-02-03 21:28 6371
1193b4be Poul Sander 2025-02-03 21:28 6372
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
1193b4be Poul Sander 2025-02-03 21:28 6373
{
1193b4be Poul Sander 2025-02-03 21:28 6374
    window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
1193b4be Poul Sander 2025-02-03 21:28 6375
    window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
1193b4be Poul Sander 2025-02-03 21:28 6376
    window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
1193b4be Poul Sander 2025-02-03 21:28 6377
    window->SetWindowDockAllowFlags      = enabled ? (window->SetWindowDockAllowFlags      | flags) : (window->SetWindowDockAllowFlags      & ~flags);
1193b4be Poul Sander 2025-02-03 21:28 6378
}
1193b4be Poul Sander 2025-02-03 21:28 6379
1193b4be Poul Sander 2025-02-03 21:28 6380
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 6381
{
1193b4be Poul Sander 2025-02-03 21:28 6382
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6383
    return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
1193b4be Poul Sander 2025-02-03 21:28 6384
}
1193b4be Poul Sander 2025-02-03 21:28 6385
1193b4be Poul Sander 2025-02-03 21:28 6386
ImGuiWindow* ImGui::FindWindowByName(const char* name)
1193b4be Poul Sander 2025-02-03 21:28 6387
{
1193b4be Poul Sander 2025-02-03 21:28 6388
    ImGuiID id = ImHashStr(name);
1193b4be Poul Sander 2025-02-03 21:28 6389
    return FindWindowByID(id);
1193b4be Poul Sander 2025-02-03 21:28 6390
}
1193b4be Poul Sander 2025-02-03 21:28 6391
1193b4be Poul Sander 2025-02-03 21:28 6392
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
1193b4be Poul Sander 2025-02-03 21:28 6393
{
1193b4be Poul Sander 2025-02-03 21:28 6394
    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 6395
    window->ViewportPos = main_viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6396
    if (settings->ViewportId)
1193b4be Poul Sander 2025-02-03 21:28 6397
    {
1193b4be Poul Sander 2025-02-03 21:28 6398
        window->ViewportId = settings->ViewportId;
1193b4be Poul Sander 2025-02-03 21:28 6399
        window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
1193b4be Poul Sander 2025-02-03 21:28 6400
    }
1193b4be Poul Sander 2025-02-03 21:28 6401
    window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
1193b4be Poul Sander 2025-02-03 21:28 6402
    if (settings->Size.x > 0 && settings->Size.y > 0)
1193b4be Poul Sander 2025-02-03 21:28 6403
        window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
1193b4be Poul Sander 2025-02-03 21:28 6404
    window->Collapsed = settings->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 6405
    window->DockId = settings->DockId;
1193b4be Poul Sander 2025-02-03 21:28 6406
    window->DockOrder = settings->DockOrder;
1193b4be Poul Sander 2025-02-03 21:28 6407
}
1193b4be Poul Sander 2025-02-03 21:28 6408
1193b4be Poul Sander 2025-02-03 21:28 6409
static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
1193b4be Poul Sander 2025-02-03 21:28 6410
{
1193b4be Poul Sander 2025-02-03 21:28 6411
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6412
1193b4be Poul Sander 2025-02-03 21:28 6413
    const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
1193b4be Poul Sander 2025-02-03 21:28 6414
    const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
1193b4be Poul Sander 2025-02-03 21:28 6415
    if ((just_created || child_flag_changed) && !new_is_explicit_child)
1193b4be Poul Sander 2025-02-03 21:28 6416
    {
1193b4be Poul Sander 2025-02-03 21:28 6417
        IM_ASSERT(!g.WindowsFocusOrder.contains(window));
1193b4be Poul Sander 2025-02-03 21:28 6418
        g.WindowsFocusOrder.push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 6419
        window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
1193b4be Poul Sander 2025-02-03 21:28 6420
    }
1193b4be Poul Sander 2025-02-03 21:28 6421
    else if (!just_created && child_flag_changed && new_is_explicit_child)
1193b4be Poul Sander 2025-02-03 21:28 6422
    {
1193b4be Poul Sander 2025-02-03 21:28 6423
        IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
1193b4be Poul Sander 2025-02-03 21:28 6424
        for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 6425
            g.WindowsFocusOrder[n]->FocusOrder--;
1193b4be Poul Sander 2025-02-03 21:28 6426
        g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
1193b4be Poul Sander 2025-02-03 21:28 6427
        window->FocusOrder = -1;
1193b4be Poul Sander 2025-02-03 21:28 6428
    }
1193b4be Poul Sander 2025-02-03 21:28 6429
    window->IsExplicitChild = new_is_explicit_child;
1193b4be Poul Sander 2025-02-03 21:28 6430
}
1193b4be Poul Sander 2025-02-03 21:28 6431
1193b4be Poul Sander 2025-02-03 21:28 6432
static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
1193b4be Poul Sander 2025-02-03 21:28 6433
{
1193b4be Poul Sander 2025-02-03 21:28 6434
    // Initial window state with e.g. default/arbitrary window position
1193b4be Poul Sander 2025-02-03 21:28 6435
    // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
1193b4be Poul Sander 2025-02-03 21:28 6436
    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 6437
    window->Pos = main_viewport->Pos + ImVec2(60, 60);
1193b4be Poul Sander 2025-02-03 21:28 6438
    window->Size = window->SizeFull = ImVec2(0, 0);
1193b4be Poul Sander 2025-02-03 21:28 6439
    window->ViewportPos = main_viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6440
    window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
1193b4be Poul Sander 2025-02-03 21:28 6441
1193b4be Poul Sander 2025-02-03 21:28 6442
    if (settings != NULL)
1193b4be Poul Sander 2025-02-03 21:28 6443
    {
1193b4be Poul Sander 2025-02-03 21:28 6444
        SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
1193b4be Poul Sander 2025-02-03 21:28 6445
        ApplyWindowSettings(window, settings);
1193b4be Poul Sander 2025-02-03 21:28 6446
    }
1193b4be Poul Sander 2025-02-03 21:28 6447
    window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
1193b4be Poul Sander 2025-02-03 21:28 6448
1193b4be Poul Sander 2025-02-03 21:28 6449
    if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
1193b4be Poul Sander 2025-02-03 21:28 6450
    {
1193b4be Poul Sander 2025-02-03 21:28 6451
        window->AutoFitFramesX = window->AutoFitFramesY = 2;
1193b4be Poul Sander 2025-02-03 21:28 6452
        window->AutoFitOnlyGrows = false;
1193b4be Poul Sander 2025-02-03 21:28 6453
    }
1193b4be Poul Sander 2025-02-03 21:28 6454
    else
1193b4be Poul Sander 2025-02-03 21:28 6455
    {
1193b4be Poul Sander 2025-02-03 21:28 6456
        if (window->Size.x <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6457
            window->AutoFitFramesX = 2;
1193b4be Poul Sander 2025-02-03 21:28 6458
        if (window->Size.y <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6459
            window->AutoFitFramesY = 2;
1193b4be Poul Sander 2025-02-03 21:28 6460
        window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
1193b4be Poul Sander 2025-02-03 21:28 6461
    }
1193b4be Poul Sander 2025-02-03 21:28 6462
}
1193b4be Poul Sander 2025-02-03 21:28 6463
1193b4be Poul Sander 2025-02-03 21:28 6464
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 6465
{
1193b4be Poul Sander 2025-02-03 21:28 6466
    // Create window the first time
1193b4be Poul Sander 2025-02-03 21:28 6467
    //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
1193b4be Poul Sander 2025-02-03 21:28 6468
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6469
    ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
1193b4be Poul Sander 2025-02-03 21:28 6470
    window->Flags = flags;
1193b4be Poul Sander 2025-02-03 21:28 6471
    g.WindowsById.SetVoidPtr(window->ID, window);
1193b4be Poul Sander 2025-02-03 21:28 6472
1193b4be Poul Sander 2025-02-03 21:28 6473
    ImGuiWindowSettings* settings = NULL;
1193b4be Poul Sander 2025-02-03 21:28 6474
    if (!(flags & ImGuiWindowFlags_NoSavedSettings))
1193b4be Poul Sander 2025-02-03 21:28 6475
        if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 6476
            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
1193b4be Poul Sander 2025-02-03 21:28 6477
1193b4be Poul Sander 2025-02-03 21:28 6478
    InitOrLoadWindowSettings(window, settings);
1193b4be Poul Sander 2025-02-03 21:28 6479
1193b4be Poul Sander 2025-02-03 21:28 6480
    if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
1193b4be Poul Sander 2025-02-03 21:28 6481
        g.Windows.push_front(window); // Quite slow but rare and only once
1193b4be Poul Sander 2025-02-03 21:28 6482
    else
1193b4be Poul Sander 2025-02-03 21:28 6483
        g.Windows.push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 6484
1193b4be Poul Sander 2025-02-03 21:28 6485
    return window;
1193b4be Poul Sander 2025-02-03 21:28 6486
}
1193b4be Poul Sander 2025-02-03 21:28 6487
1193b4be Poul Sander 2025-02-03 21:28 6488
static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6489
{
1193b4be Poul Sander 2025-02-03 21:28 6490
    return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
1193b4be Poul Sander 2025-02-03 21:28 6491
}
1193b4be Poul Sander 2025-02-03 21:28 6492
1193b4be Poul Sander 2025-02-03 21:28 6493
static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6494
{
1193b4be Poul Sander 2025-02-03 21:28 6495
    return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
1193b4be Poul Sander 2025-02-03 21:28 6496
}
1193b4be Poul Sander 2025-02-03 21:28 6497
1193b4be Poul Sander 2025-02-03 21:28 6498
static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6499
{
1193b4be Poul Sander 2025-02-03 21:28 6500
    // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
1193b4be Poul Sander 2025-02-03 21:28 6501
    // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
1193b4be Poul Sander 2025-02-03 21:28 6502
    // Perhaps should tend further a neater test for this.
1193b4be Poul Sander 2025-02-03 21:28 6503
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6504
    ImVec2 size_min;
1193b4be Poul Sander 2025-02-03 21:28 6505
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
1193b4be Poul Sander 2025-02-03 21:28 6506
    {
1193b4be Poul Sander 2025-02-03 21:28 6507
        size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
1193b4be Poul Sander 2025-02-03 21:28 6508
        size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
1193b4be Poul Sander 2025-02-03 21:28 6509
    }
1193b4be Poul Sander 2025-02-03 21:28 6510
    else
1193b4be Poul Sander 2025-02-03 21:28 6511
    {
1193b4be Poul Sander 2025-02-03 21:28 6512
        size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
1193b4be Poul Sander 2025-02-03 21:28 6513
        size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
1193b4be Poul Sander 2025-02-03 21:28 6514
    }
1193b4be Poul Sander 2025-02-03 21:28 6515
1193b4be Poul Sander 2025-02-03 21:28 6516
    // Reduce artifacts with very small windows
1193b4be Poul Sander 2025-02-03 21:28 6517
    ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
1193b4be Poul Sander 2025-02-03 21:28 6518
    size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
1193b4be Poul Sander 2025-02-03 21:28 6519
    return size_min;
1193b4be Poul Sander 2025-02-03 21:28 6520
}
1193b4be Poul Sander 2025-02-03 21:28 6521
1193b4be Poul Sander 2025-02-03 21:28 6522
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
1193b4be Poul Sander 2025-02-03 21:28 6523
{
1193b4be Poul Sander 2025-02-03 21:28 6524
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6525
    ImVec2 new_size = size_desired;
1193b4be Poul Sander 2025-02-03 21:28 6526
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
1193b4be Poul Sander 2025-02-03 21:28 6527
    {
1193b4be Poul Sander 2025-02-03 21:28 6528
        // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
1193b4be Poul Sander 2025-02-03 21:28 6529
        ImRect cr = g.NextWindowData.SizeConstraintRect;
1193b4be Poul Sander 2025-02-03 21:28 6530
        new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
1193b4be Poul Sander 2025-02-03 21:28 6531
        new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
1193b4be Poul Sander 2025-02-03 21:28 6532
        if (g.NextWindowData.SizeCallback)
1193b4be Poul Sander 2025-02-03 21:28 6533
        {
1193b4be Poul Sander 2025-02-03 21:28 6534
            ImGuiSizeCallbackData data;
1193b4be Poul Sander 2025-02-03 21:28 6535
            data.UserData = g.NextWindowData.SizeCallbackUserData;
1193b4be Poul Sander 2025-02-03 21:28 6536
            data.Pos = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6537
            data.CurrentSize = window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 6538
            data.DesiredSize = new_size;
1193b4be Poul Sander 2025-02-03 21:28 6539
            g.NextWindowData.SizeCallback(&data);
1193b4be Poul Sander 2025-02-03 21:28 6540
            new_size = data.DesiredSize;
1193b4be Poul Sander 2025-02-03 21:28 6541
        }
1193b4be Poul Sander 2025-02-03 21:28 6542
        new_size.x = IM_TRUNC(new_size.x);
1193b4be Poul Sander 2025-02-03 21:28 6543
        new_size.y = IM_TRUNC(new_size.y);
1193b4be Poul Sander 2025-02-03 21:28 6544
    }
1193b4be Poul Sander 2025-02-03 21:28 6545
1193b4be Poul Sander 2025-02-03 21:28 6546
    // Minimum size
1193b4be Poul Sander 2025-02-03 21:28 6547
    ImVec2 size_min = CalcWindowMinSize(window);
1193b4be Poul Sander 2025-02-03 21:28 6548
    return ImMax(new_size, size_min);
1193b4be Poul Sander 2025-02-03 21:28 6549
}
1193b4be Poul Sander 2025-02-03 21:28 6550
1193b4be Poul Sander 2025-02-03 21:28 6551
static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
1193b4be Poul Sander 2025-02-03 21:28 6552
{
1193b4be Poul Sander 2025-02-03 21:28 6553
    bool preserve_old_content_sizes = false;
1193b4be Poul Sander 2025-02-03 21:28 6554
    if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
1193b4be Poul Sander 2025-02-03 21:28 6555
        preserve_old_content_sizes = true;
1193b4be Poul Sander 2025-02-03 21:28 6556
    else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
1193b4be Poul Sander 2025-02-03 21:28 6557
        preserve_old_content_sizes = true;
1193b4be Poul Sander 2025-02-03 21:28 6558
    if (preserve_old_content_sizes)
1193b4be Poul Sander 2025-02-03 21:28 6559
    {
1193b4be Poul Sander 2025-02-03 21:28 6560
        *content_size_current = window->ContentSize;
1193b4be Poul Sander 2025-02-03 21:28 6561
        *content_size_ideal = window->ContentSizeIdeal;
1193b4be Poul Sander 2025-02-03 21:28 6562
        return;
1193b4be Poul Sander 2025-02-03 21:28 6563
    }
1193b4be Poul Sander 2025-02-03 21:28 6564
1193b4be Poul Sander 2025-02-03 21:28 6565
    content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
1193b4be Poul Sander 2025-02-03 21:28 6566
    content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
1193b4be Poul Sander 2025-02-03 21:28 6567
    content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
1193b4be Poul Sander 2025-02-03 21:28 6568
    content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
1193b4be Poul Sander 2025-02-03 21:28 6569
}
1193b4be Poul Sander 2025-02-03 21:28 6570
1193b4be Poul Sander 2025-02-03 21:28 6571
static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
1193b4be Poul Sander 2025-02-03 21:28 6572
{
1193b4be Poul Sander 2025-02-03 21:28 6573
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6574
    ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 6575
    const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
1193b4be Poul Sander 2025-02-03 21:28 6576
    const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
1193b4be Poul Sander 2025-02-03 21:28 6577
    ImVec2 size_pad = window->WindowPadding * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 6578
    ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
1193b4be Poul Sander 2025-02-03 21:28 6579
    if (window->Flags & ImGuiWindowFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 6580
    {
1193b4be Poul Sander 2025-02-03 21:28 6581
        // Tooltip always resize
1193b4be Poul Sander 2025-02-03 21:28 6582
        return size_desired;
1193b4be Poul Sander 2025-02-03 21:28 6583
    }
1193b4be Poul Sander 2025-02-03 21:28 6584
    else
1193b4be Poul Sander 2025-02-03 21:28 6585
    {
1193b4be Poul Sander 2025-02-03 21:28 6586
        // Maximum window size is determined by the viewport size or monitor size
1193b4be Poul Sander 2025-02-03 21:28 6587
        ImVec2 size_min = CalcWindowMinSize(window);
1193b4be Poul Sander 2025-02-03 21:28 6588
        ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6589
1193b4be Poul Sander 2025-02-03 21:28 6590
        // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
1193b4be Poul Sander 2025-02-03 21:28 6591
        if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0)
1193b4be Poul Sander 2025-02-03 21:28 6592
        {
1193b4be Poul Sander 2025-02-03 21:28 6593
            if (!window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 6594
                size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 6595
            const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
1193b4be Poul Sander 2025-02-03 21:28 6596
            if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
1193b4be Poul Sander 2025-02-03 21:28 6597
                size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 6598
        }
1193b4be Poul Sander 2025-02-03 21:28 6599
1193b4be Poul Sander 2025-02-03 21:28 6600
        ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, size_max));
1193b4be Poul Sander 2025-02-03 21:28 6601
1193b4be Poul Sander 2025-02-03 21:28 6602
        // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
1193b4be Poul Sander 2025-02-03 21:28 6603
        // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
1193b4be Poul Sander 2025-02-03 21:28 6604
        // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
1193b4be Poul Sander 2025-02-03 21:28 6605
        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
1193b4be Poul Sander 2025-02-03 21:28 6606
            size_auto_fit.y = window->SizeFull.y;
1193b4be Poul Sander 2025-02-03 21:28 6607
        else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
1193b4be Poul Sander 2025-02-03 21:28 6608
            size_auto_fit.x = window->SizeFull.x;
1193b4be Poul Sander 2025-02-03 21:28 6609
1193b4be Poul Sander 2025-02-03 21:28 6610
        // When the window cannot fit all contents (either because of constraints, either because screen is too small),
1193b4be Poul Sander 2025-02-03 21:28 6611
        // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
1193b4be Poul Sander 2025-02-03 21:28 6612
        ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
1193b4be Poul Sander 2025-02-03 21:28 6613
        bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 6614
        bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 6615
        if (will_have_scrollbar_x)
1193b4be Poul Sander 2025-02-03 21:28 6616
            size_auto_fit.y += style.ScrollbarSize;
1193b4be Poul Sander 2025-02-03 21:28 6617
        if (will_have_scrollbar_y)
1193b4be Poul Sander 2025-02-03 21:28 6618
            size_auto_fit.x += style.ScrollbarSize;
1193b4be Poul Sander 2025-02-03 21:28 6619
        return size_auto_fit;
1193b4be Poul Sander 2025-02-03 21:28 6620
    }
1193b4be Poul Sander 2025-02-03 21:28 6621
}
1193b4be Poul Sander 2025-02-03 21:28 6622
1193b4be Poul Sander 2025-02-03 21:28 6623
ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6624
{
1193b4be Poul Sander 2025-02-03 21:28 6625
    ImVec2 size_contents_current;
1193b4be Poul Sander 2025-02-03 21:28 6626
    ImVec2 size_contents_ideal;
1193b4be Poul Sander 2025-02-03 21:28 6627
    CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
1193b4be Poul Sander 2025-02-03 21:28 6628
    ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
1193b4be Poul Sander 2025-02-03 21:28 6629
    ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
1193b4be Poul Sander 2025-02-03 21:28 6630
    return size_final;
1193b4be Poul Sander 2025-02-03 21:28 6631
}
1193b4be Poul Sander 2025-02-03 21:28 6632
1193b4be Poul Sander 2025-02-03 21:28 6633
static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6634
{
1193b4be Poul Sander 2025-02-03 21:28 6635
    if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
1193b4be Poul Sander 2025-02-03 21:28 6636
        return ImGuiCol_PopupBg;
1193b4be Poul Sander 2025-02-03 21:28 6637
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 6638
        return ImGuiCol_ChildBg;
1193b4be Poul Sander 2025-02-03 21:28 6639
    return ImGuiCol_WindowBg;
1193b4be Poul Sander 2025-02-03 21:28 6640
}
1193b4be Poul Sander 2025-02-03 21:28 6641
1193b4be Poul Sander 2025-02-03 21:28 6642
static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
1193b4be Poul Sander 2025-02-03 21:28 6643
{
1193b4be Poul Sander 2025-02-03 21:28 6644
    ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
1193b4be Poul Sander 2025-02-03 21:28 6645
    ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
1193b4be Poul Sander 2025-02-03 21:28 6646
    ImVec2 size_expected = pos_max - pos_min;
1193b4be Poul Sander 2025-02-03 21:28 6647
    ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
1193b4be Poul Sander 2025-02-03 21:28 6648
    *out_pos = pos_min;
1193b4be Poul Sander 2025-02-03 21:28 6649
    if (corner_norm.x == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6650
        out_pos->x -= (size_constrained.x - size_expected.x);
1193b4be Poul Sander 2025-02-03 21:28 6651
    if (corner_norm.y == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6652
        out_pos->y -= (size_constrained.y - size_expected.y);
1193b4be Poul Sander 2025-02-03 21:28 6653
    *out_size = size_constrained;
1193b4be Poul Sander 2025-02-03 21:28 6654
}
1193b4be Poul Sander 2025-02-03 21:28 6655
1193b4be Poul Sander 2025-02-03 21:28 6656
// Data for resizing from resize grip / corner
1193b4be Poul Sander 2025-02-03 21:28 6657
struct ImGuiResizeGripDef
1193b4be Poul Sander 2025-02-03 21:28 6658
{
1193b4be Poul Sander 2025-02-03 21:28 6659
    ImVec2  CornerPosN;
1193b4be Poul Sander 2025-02-03 21:28 6660
    ImVec2  InnerDir;
1193b4be Poul Sander 2025-02-03 21:28 6661
    int     AngleMin12, AngleMax12;
1193b4be Poul Sander 2025-02-03 21:28 6662
};
1193b4be Poul Sander 2025-02-03 21:28 6663
static const ImGuiResizeGripDef resize_grip_def[4] =
1193b4be Poul Sander 2025-02-03 21:28 6664
{
1193b4be Poul Sander 2025-02-03 21:28 6665
    { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right
1193b4be Poul Sander 2025-02-03 21:28 6666
    { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left
1193b4be Poul Sander 2025-02-03 21:28 6667
    { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)
1193b4be Poul Sander 2025-02-03 21:28 6668
    { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)
1193b4be Poul Sander 2025-02-03 21:28 6669
};
1193b4be Poul Sander 2025-02-03 21:28 6670
1193b4be Poul Sander 2025-02-03 21:28 6671
// Data for resizing from borders
1193b4be Poul Sander 2025-02-03 21:28 6672
struct ImGuiResizeBorderDef
1193b4be Poul Sander 2025-02-03 21:28 6673
{
1193b4be Poul Sander 2025-02-03 21:28 6674
    ImVec2  InnerDir;               // Normal toward inside
1193b4be Poul Sander 2025-02-03 21:28 6675
    ImVec2  SegmentN1, SegmentN2;   // End positions, normalized (0,0: upper left)
1193b4be Poul Sander 2025-02-03 21:28 6676
    float   OuterAngle;             // Angle toward outside
1193b4be Poul Sander 2025-02-03 21:28 6677
};
1193b4be Poul Sander 2025-02-03 21:28 6678
static const ImGuiResizeBorderDef resize_border_def[4] =
1193b4be Poul Sander 2025-02-03 21:28 6679
{
1193b4be Poul Sander 2025-02-03 21:28 6680
    { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
1193b4be Poul Sander 2025-02-03 21:28 6681
    { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
1193b4be Poul Sander 2025-02-03 21:28 6682
    { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
1193b4be Poul Sander 2025-02-03 21:28 6683
    { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down
1193b4be Poul Sander 2025-02-03 21:28 6684
};
1193b4be Poul Sander 2025-02-03 21:28 6685
1193b4be Poul Sander 2025-02-03 21:28 6686
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
1193b4be Poul Sander 2025-02-03 21:28 6687
{
1193b4be Poul Sander 2025-02-03 21:28 6688
    ImRect rect = window->Rect();
1193b4be Poul Sander 2025-02-03 21:28 6689
    if (thickness == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6690
        rect.Max -= ImVec2(1, 1);
1193b4be Poul Sander 2025-02-03 21:28 6691
    if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }
1193b4be Poul Sander 2025-02-03 21:28 6692
    if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }
1193b4be Poul Sander 2025-02-03 21:28 6693
    if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }
1193b4be Poul Sander 2025-02-03 21:28 6694
    if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }
1193b4be Poul Sander 2025-02-03 21:28 6695
    IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 6696
    return ImRect();
1193b4be Poul Sander 2025-02-03 21:28 6697
}
1193b4be Poul Sander 2025-02-03 21:28 6698
1193b4be Poul Sander 2025-02-03 21:28 6699
// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
1193b4be Poul Sander 2025-02-03 21:28 6700
ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
1193b4be Poul Sander 2025-02-03 21:28 6701
{
1193b4be Poul Sander 2025-02-03 21:28 6702
    IM_ASSERT(n >= 0 && n < 4);
1193b4be Poul Sander 2025-02-03 21:28 6703
    ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
1193b4be Poul Sander 2025-02-03 21:28 6704
    id = ImHashStr("#RESIZE", 0, id);
1193b4be Poul Sander 2025-02-03 21:28 6705
    id = ImHashData(&n, sizeof(int), id);
1193b4be Poul Sander 2025-02-03 21:28 6706
    return id;
1193b4be Poul Sander 2025-02-03 21:28 6707
}
1193b4be Poul Sander 2025-02-03 21:28 6708
1193b4be Poul Sander 2025-02-03 21:28 6709
// Borders (Left, Right, Up, Down)
1193b4be Poul Sander 2025-02-03 21:28 6710
ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
1193b4be Poul Sander 2025-02-03 21:28 6711
{
1193b4be Poul Sander 2025-02-03 21:28 6712
    IM_ASSERT(dir >= 0 && dir < 4);
1193b4be Poul Sander 2025-02-03 21:28 6713
    int n = (int)dir + 4;
1193b4be Poul Sander 2025-02-03 21:28 6714
    ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
1193b4be Poul Sander 2025-02-03 21:28 6715
    id = ImHashStr("#RESIZE", 0, id);
1193b4be Poul Sander 2025-02-03 21:28 6716
    id = ImHashData(&n, sizeof(int), id);
1193b4be Poul Sander 2025-02-03 21:28 6717
    return id;
1193b4be Poul Sander 2025-02-03 21:28 6718
}
1193b4be Poul Sander 2025-02-03 21:28 6719
1193b4be Poul Sander 2025-02-03 21:28 6720
// Handle resize for: Resize Grips, Borders, Gamepad
1193b4be Poul Sander 2025-02-03 21:28 6721
// Return true when using auto-fit (double-click on resize grip)
1193b4be Poul Sander 2025-02-03 21:28 6722
static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
1193b4be Poul Sander 2025-02-03 21:28 6723
{
1193b4be Poul Sander 2025-02-03 21:28 6724
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6725
    ImGuiWindowFlags flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 6726
1193b4be Poul Sander 2025-02-03 21:28 6727
    if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 6728
        return false;
1193b4be Poul Sander 2025-02-03 21:28 6729
    if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
1193b4be Poul Sander 2025-02-03 21:28 6730
        return false;
1193b4be Poul Sander 2025-02-03 21:28 6731
1193b4be Poul Sander 2025-02-03 21:28 6732
    int ret_auto_fit_mask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 6733
    const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
1193b4be Poul Sander 2025-02-03 21:28 6734
    const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 6735
    const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 6736
1193b4be Poul Sander 2025-02-03 21:28 6737
    ImRect clamp_rect = visibility_rect;
1193b4be Poul Sander 2025-02-03 21:28 6738
    const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
1193b4be Poul Sander 2025-02-03 21:28 6739
    if (window_move_from_title_bar)
1193b4be Poul Sander 2025-02-03 21:28 6740
        clamp_rect.Min.y -= window->TitleBarHeight;
1193b4be Poul Sander 2025-02-03 21:28 6741
1193b4be Poul Sander 2025-02-03 21:28 6742
    ImVec2 pos_target(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6743
    ImVec2 size_target(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6744
1193b4be Poul Sander 2025-02-03 21:28 6745
    // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
1193b4be Poul Sander 2025-02-03 21:28 6746
    // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
1193b4be Poul Sander 2025-02-03 21:28 6747
    //   This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
1193b4be Poul Sander 2025-02-03 21:28 6748
    // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow.
1193b4be Poul Sander 2025-02-03 21:28 6749
    // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
1193b4be Poul Sander 2025-02-03 21:28 6750
    // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
1193b4be Poul Sander 2025-02-03 21:28 6751
    const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
1193b4be Poul Sander 2025-02-03 21:28 6752
    if (clip_with_viewport_rect)
1193b4be Poul Sander 2025-02-03 21:28 6753
        window->ClipRect = window->Viewport->GetMainRect();
1193b4be Poul Sander 2025-02-03 21:28 6754
1193b4be Poul Sander 2025-02-03 21:28 6755
    // Resize grips and borders are on layer 1
1193b4be Poul Sander 2025-02-03 21:28 6756
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
1193b4be Poul Sander 2025-02-03 21:28 6757
1193b4be Poul Sander 2025-02-03 21:28 6758
    // Manual resize grips
1193b4be Poul Sander 2025-02-03 21:28 6759
    PushID("#RESIZE");
1193b4be Poul Sander 2025-02-03 21:28 6760
    for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
1193b4be Poul Sander 2025-02-03 21:28 6761
    {
1193b4be Poul Sander 2025-02-03 21:28 6762
        const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
1193b4be Poul Sander 2025-02-03 21:28 6763
        const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
1193b4be Poul Sander 2025-02-03 21:28 6764
1193b4be Poul Sander 2025-02-03 21:28 6765
        // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
1193b4be Poul Sander 2025-02-03 21:28 6766
        bool hovered, held;
1193b4be Poul Sander 2025-02-03 21:28 6767
        ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
1193b4be Poul Sander 2025-02-03 21:28 6768
        if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 6769
        if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 6770
        ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
1193b4be Poul Sander 2025-02-03 21:28 6771
        ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
1193b4be Poul Sander 2025-02-03 21:28 6772
        ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
1193b4be Poul Sander 2025-02-03 21:28 6773
        //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 6774
        if (hovered || held)
1193b4be Poul Sander 2025-02-03 21:28 6775
            SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
1193b4be Poul Sander 2025-02-03 21:28 6776
1193b4be Poul Sander 2025-02-03 21:28 6777
        if (held && g.IO.MouseDoubleClicked[0])
1193b4be Poul Sander 2025-02-03 21:28 6778
        {
1193b4be Poul Sander 2025-02-03 21:28 6779
            // Auto-fit when double-clicking
1193b4be Poul Sander 2025-02-03 21:28 6780
            size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
1193b4be Poul Sander 2025-02-03 21:28 6781
            ret_auto_fit_mask = 0x03; // Both axises
1193b4be Poul Sander 2025-02-03 21:28 6782
            ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 6783
        }
1193b4be Poul Sander 2025-02-03 21:28 6784
        else if (held)
1193b4be Poul Sander 2025-02-03 21:28 6785
        {
1193b4be Poul Sander 2025-02-03 21:28 6786
            // Resize from any of the four corners
1193b4be Poul Sander 2025-02-03 21:28 6787
            // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
1193b4be Poul Sander 2025-02-03 21:28 6788
            ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6789
            ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6790
            ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
1193b4be Poul Sander 2025-02-03 21:28 6791
            corner_target = ImClamp(corner_target, clamp_min, clamp_max);
1193b4be Poul Sander 2025-02-03 21:28 6792
            CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
1193b4be Poul Sander 2025-02-03 21:28 6793
        }
1193b4be Poul Sander 2025-02-03 21:28 6794
1193b4be Poul Sander 2025-02-03 21:28 6795
        // Only lower-left grip is visible before hovering/activating
1193b4be Poul Sander 2025-02-03 21:28 6796
        if (resize_grip_n == 0 || held || hovered)
1193b4be Poul Sander 2025-02-03 21:28 6797
            resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
1193b4be Poul Sander 2025-02-03 21:28 6798
    }
1193b4be Poul Sander 2025-02-03 21:28 6799
1193b4be Poul Sander 2025-02-03 21:28 6800
    int resize_border_mask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 6801
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 6802
        resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
1193b4be Poul Sander 2025-02-03 21:28 6803
    else
1193b4be Poul Sander 2025-02-03 21:28 6804
        resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
1193b4be Poul Sander 2025-02-03 21:28 6805
    for (int border_n = 0; border_n < 4; border_n++)
1193b4be Poul Sander 2025-02-03 21:28 6806
    {
1193b4be Poul Sander 2025-02-03 21:28 6807
        if ((resize_border_mask & (1 << border_n)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 6808
            continue;
1193b4be Poul Sander 2025-02-03 21:28 6809
        const ImGuiResizeBorderDef& def = resize_border_def[border_n];
1193b4be Poul Sander 2025-02-03 21:28 6810
        const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
1193b4be Poul Sander 2025-02-03 21:28 6811
1193b4be Poul Sander 2025-02-03 21:28 6812
        bool hovered, held;
1193b4be Poul Sander 2025-02-03 21:28 6813
        ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
1193b4be Poul Sander 2025-02-03 21:28 6814
        ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
1193b4be Poul Sander 2025-02-03 21:28 6815
        ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
1193b4be Poul Sander 2025-02-03 21:28 6816
        ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
1193b4be Poul Sander 2025-02-03 21:28 6817
        //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 6818
        if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
1193b4be Poul Sander 2025-02-03 21:28 6819
            hovered = false;
1193b4be Poul Sander 2025-02-03 21:28 6820
        if (hovered || held)
1193b4be Poul Sander 2025-02-03 21:28 6821
            SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
1193b4be Poul Sander 2025-02-03 21:28 6822
        if (held && g.IO.MouseDoubleClicked[0])
1193b4be Poul Sander 2025-02-03 21:28 6823
        {
1193b4be Poul Sander 2025-02-03 21:28 6824
            // Double-clicking bottom or right border auto-fit on this axis
1193b4be Poul Sander 2025-02-03 21:28 6825
            // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
1193b4be Poul Sander 2025-02-03 21:28 6826
            // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
1193b4be Poul Sander 2025-02-03 21:28 6827
            if (border_n == 1 || border_n == 3) // Right and bottom border
1193b4be Poul Sander 2025-02-03 21:28 6828
            {
1193b4be Poul Sander 2025-02-03 21:28 6829
                size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
1193b4be Poul Sander 2025-02-03 21:28 6830
                ret_auto_fit_mask |= (1 << axis);
1193b4be Poul Sander 2025-02-03 21:28 6831
                hovered = held = false; // So border doesn't show highlighted at new position
1193b4be Poul Sander 2025-02-03 21:28 6832
            }
1193b4be Poul Sander 2025-02-03 21:28 6833
            ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 6834
        }
1193b4be Poul Sander 2025-02-03 21:28 6835
        else if (held)
1193b4be Poul Sander 2025-02-03 21:28 6836
        {
1193b4be Poul Sander 2025-02-03 21:28 6837
            // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
1193b4be Poul Sander 2025-02-03 21:28 6838
            // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
1193b4be Poul Sander 2025-02-03 21:28 6839
            // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
1193b4be Poul Sander 2025-02-03 21:28 6840
            const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true));
1193b4be Poul Sander 2025-02-03 21:28 6841
            if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
1193b4be Poul Sander 2025-02-03 21:28 6842
            {
1193b4be Poul Sander 2025-02-03 21:28 6843
                g.WindowResizeBorderExpectedRect = border_rect;
1193b4be Poul Sander 2025-02-03 21:28 6844
                g.WindowResizeRelativeMode = false;
1193b4be Poul Sander 2025-02-03 21:28 6845
            }
1193b4be Poul Sander 2025-02-03 21:28 6846
            if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 6847
                g.WindowResizeRelativeMode = true;
1193b4be Poul Sander 2025-02-03 21:28 6848
1193b4be Poul Sander 2025-02-03 21:28 6849
            const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
1193b4be Poul Sander 2025-02-03 21:28 6850
            const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
1193b4be Poul Sander 2025-02-03 21:28 6851
            const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
1193b4be Poul Sander 2025-02-03 21:28 6852
1193b4be Poul Sander 2025-02-03 21:28 6853
            // Use absolute mode position
1193b4be Poul Sander 2025-02-03 21:28 6854
            ImVec2 border_target = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6855
            border_target[axis] = border_target_abs_mode_for_axis;
1193b4be Poul Sander 2025-02-03 21:28 6856
1193b4be Poul Sander 2025-02-03 21:28 6857
            // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
1193b4be Poul Sander 2025-02-03 21:28 6858
            bool ignore_resize = false;
1193b4be Poul Sander 2025-02-03 21:28 6859
            if (g.WindowResizeRelativeMode)
1193b4be Poul Sander 2025-02-03 21:28 6860
            {
1193b4be Poul Sander 2025-02-03 21:28 6861
                //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
1193b4be Poul Sander 2025-02-03 21:28 6862
                border_target[axis] = border_target_rel_mode_for_axis;
1193b4be Poul Sander 2025-02-03 21:28 6863
                if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))
1193b4be Poul Sander 2025-02-03 21:28 6864
                    ignore_resize = true;
1193b4be Poul Sander 2025-02-03 21:28 6865
            }
1193b4be Poul Sander 2025-02-03 21:28 6866
1193b4be Poul Sander 2025-02-03 21:28 6867
            // Clamp, apply
1193b4be Poul Sander 2025-02-03 21:28 6868
            ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6869
            ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 6870
            border_target = ImClamp(border_target, clamp_min, clamp_max);
1193b4be Poul Sander 2025-02-03 21:28 6871
            if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
1193b4be Poul Sander 2025-02-03 21:28 6872
            {
1193b4be Poul Sander 2025-02-03 21:28 6873
                ImGuiWindow* parent_window = window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 6874
                ImGuiWindowFlags parent_flags = parent_window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 6875
                ImRect border_limit_rect = parent_window->InnerRect;
1193b4be Poul Sander 2025-02-03 21:28 6876
                border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
1193b4be Poul Sander 2025-02-03 21:28 6877
                if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)))
1193b4be Poul Sander 2025-02-03 21:28 6878
                    border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 6879
                if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar))
1193b4be Poul Sander 2025-02-03 21:28 6880
                    border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 6881
            }
1193b4be Poul Sander 2025-02-03 21:28 6882
            if (!ignore_resize)
1193b4be Poul Sander 2025-02-03 21:28 6883
                CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
1193b4be Poul Sander 2025-02-03 21:28 6884
        }
1193b4be Poul Sander 2025-02-03 21:28 6885
        if (hovered)
1193b4be Poul Sander 2025-02-03 21:28 6886
            *border_hovered = border_n;
1193b4be Poul Sander 2025-02-03 21:28 6887
        if (held)
1193b4be Poul Sander 2025-02-03 21:28 6888
            *border_held = border_n;
1193b4be Poul Sander 2025-02-03 21:28 6889
    }
1193b4be Poul Sander 2025-02-03 21:28 6890
    PopID();
1193b4be Poul Sander 2025-02-03 21:28 6891
1193b4be Poul Sander 2025-02-03 21:28 6892
    // Restore nav layer
1193b4be Poul Sander 2025-02-03 21:28 6893
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 6894
1193b4be Poul Sander 2025-02-03 21:28 6895
    // Navigation resize (keyboard/gamepad)
1193b4be Poul Sander 2025-02-03 21:28 6896
    // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
1193b4be Poul Sander 2025-02-03 21:28 6897
    // Not even sure the callback works here.
1193b4be Poul Sander 2025-02-03 21:28 6898
    if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)
1193b4be Poul Sander 2025-02-03 21:28 6899
    {
1193b4be Poul Sander 2025-02-03 21:28 6900
        ImVec2 nav_resize_dir;
1193b4be Poul Sander 2025-02-03 21:28 6901
        if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
1193b4be Poul Sander 2025-02-03 21:28 6902
            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
1193b4be Poul Sander 2025-02-03 21:28 6903
        if (g.NavInputSource == ImGuiInputSource_Gamepad)
1193b4be Poul Sander 2025-02-03 21:28 6904
            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
1193b4be Poul Sander 2025-02-03 21:28 6905
        if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6906
        {
1193b4be Poul Sander 2025-02-03 21:28 6907
            const float NAV_RESIZE_SPEED = 600.0f;
1193b4be Poul Sander 2025-02-03 21:28 6908
            const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
1193b4be Poul Sander 2025-02-03 21:28 6909
            g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
1193b4be Poul Sander 2025-02-03 21:28 6910
            g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size
1193b4be Poul Sander 2025-02-03 21:28 6911
            g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 6912
            g.NavHighlightItemUnderNav = true;
1193b4be Poul Sander 2025-02-03 21:28 6913
            resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
1193b4be Poul Sander 2025-02-03 21:28 6914
            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
1193b4be Poul Sander 2025-02-03 21:28 6915
            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6916
            {
1193b4be Poul Sander 2025-02-03 21:28 6917
                // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
1193b4be Poul Sander 2025-02-03 21:28 6918
                size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
1193b4be Poul Sander 2025-02-03 21:28 6919
                g.NavWindowingAccumDeltaSize -= accum_floored;
1193b4be Poul Sander 2025-02-03 21:28 6920
            }
1193b4be Poul Sander 2025-02-03 21:28 6921
        }
1193b4be Poul Sander 2025-02-03 21:28 6922
    }
1193b4be Poul Sander 2025-02-03 21:28 6923
1193b4be Poul Sander 2025-02-03 21:28 6924
    // Apply back modified position/size to window
1193b4be Poul Sander 2025-02-03 21:28 6925
    const ImVec2 curr_pos = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 6926
    const ImVec2 curr_size = window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 6927
    if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
1193b4be Poul Sander 2025-02-03 21:28 6928
        window->Size.x = window->SizeFull.x = size_target.x;
1193b4be Poul Sander 2025-02-03 21:28 6929
    if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
1193b4be Poul Sander 2025-02-03 21:28 6930
        window->Size.y = window->SizeFull.y = size_target.y;
1193b4be Poul Sander 2025-02-03 21:28 6931
    if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
1193b4be Poul Sander 2025-02-03 21:28 6932
        window->Pos.x = ImTrunc(pos_target.x);
1193b4be Poul Sander 2025-02-03 21:28 6933
    if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
1193b4be Poul Sander 2025-02-03 21:28 6934
        window->Pos.y = ImTrunc(pos_target.y);
1193b4be Poul Sander 2025-02-03 21:28 6935
    if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
1193b4be Poul Sander 2025-02-03 21:28 6936
        MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 6937
1193b4be Poul Sander 2025-02-03 21:28 6938
    // Recalculate next expected border expected coordinates
1193b4be Poul Sander 2025-02-03 21:28 6939
    if (*border_held != -1)
1193b4be Poul Sander 2025-02-03 21:28 6940
        g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
1193b4be Poul Sander 2025-02-03 21:28 6941
1193b4be Poul Sander 2025-02-03 21:28 6942
    return ret_auto_fit_mask;
1193b4be Poul Sander 2025-02-03 21:28 6943
}
1193b4be Poul Sander 2025-02-03 21:28 6944
1193b4be Poul Sander 2025-02-03 21:28 6945
static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
1193b4be Poul Sander 2025-02-03 21:28 6946
{
1193b4be Poul Sander 2025-02-03 21:28 6947
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6948
    ImVec2 size_for_clamping = window->Size;
1193b4be Poul Sander 2025-02-03 21:28 6949
    if (g.IO.ConfigWindowsMoveFromTitleBarOnly && window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 6950
        size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.
1193b4be Poul Sander 2025-02-03 21:28 6951
    else if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
1193b4be Poul Sander 2025-02-03 21:28 6952
        size_for_clamping.y = window->TitleBarHeight;
1193b4be Poul Sander 2025-02-03 21:28 6953
    window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
1193b4be Poul Sander 2025-02-03 21:28 6954
}
1193b4be Poul Sander 2025-02-03 21:28 6955
1193b4be Poul Sander 2025-02-03 21:28 6956
static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
1193b4be Poul Sander 2025-02-03 21:28 6957
{
1193b4be Poul Sander 2025-02-03 21:28 6958
    const ImGuiResizeBorderDef& def = resize_border_def[border_n];
1193b4be Poul Sander 2025-02-03 21:28 6959
    const float rounding = window->WindowRounding;
1193b4be Poul Sander 2025-02-03 21:28 6960
    const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 6961
    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
1193b4be Poul Sander 2025-02-03 21:28 6962
    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
1193b4be Poul Sander 2025-02-03 21:28 6963
    window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
1193b4be Poul Sander 2025-02-03 21:28 6964
}
1193b4be Poul Sander 2025-02-03 21:28 6965
1193b4be Poul Sander 2025-02-03 21:28 6966
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 6967
{
1193b4be Poul Sander 2025-02-03 21:28 6968
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6969
    const float border_size = window->WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 6970
    const ImU32 border_col = GetColorU32(ImGuiCol_Border);
1193b4be Poul Sander 2025-02-03 21:28 6971
    if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
1193b4be Poul Sander 2025-02-03 21:28 6972
        window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
1193b4be Poul Sander 2025-02-03 21:28 6973
    else if (border_size > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 6974
    {
1193b4be Poul Sander 2025-02-03 21:28 6975
        if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
1193b4be Poul Sander 2025-02-03 21:28 6976
            RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
1193b4be Poul Sander 2025-02-03 21:28 6977
        if (window->ChildFlags & ImGuiChildFlags_ResizeY)
1193b4be Poul Sander 2025-02-03 21:28 6978
            RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
1193b4be Poul Sander 2025-02-03 21:28 6979
    }
1193b4be Poul Sander 2025-02-03 21:28 6980
    if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
1193b4be Poul Sander 2025-02-03 21:28 6981
    {
1193b4be Poul Sander 2025-02-03 21:28 6982
        const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
1193b4be Poul Sander 2025-02-03 21:28 6983
        const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
1193b4be Poul Sander 2025-02-03 21:28 6984
        RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
1193b4be Poul Sander 2025-02-03 21:28 6985
    }
1193b4be Poul Sander 2025-02-03 21:28 6986
    if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 6987
    {
1193b4be Poul Sander 2025-02-03 21:28 6988
        float y = window->Pos.y + window->TitleBarHeight - 1;
1193b4be Poul Sander 2025-02-03 21:28 6989
        window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize);
1193b4be Poul Sander 2025-02-03 21:28 6990
    }
1193b4be Poul Sander 2025-02-03 21:28 6991
}
1193b4be Poul Sander 2025-02-03 21:28 6992
1193b4be Poul Sander 2025-02-03 21:28 6993
// Draw background and borders
1193b4be Poul Sander 2025-02-03 21:28 6994
// Draw and handle scrollbars
1193b4be Poul Sander 2025-02-03 21:28 6995
void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
1193b4be Poul Sander 2025-02-03 21:28 6996
{
1193b4be Poul Sander 2025-02-03 21:28 6997
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 6998
    ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 6999
    ImGuiWindowFlags flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7000
1193b4be Poul Sander 2025-02-03 21:28 7001
    // Ensure that ScrollBar doesn't read last frame's SkipItems
1193b4be Poul Sander 2025-02-03 21:28 7002
    IM_ASSERT(window->BeginCount == 0);
1193b4be Poul Sander 2025-02-03 21:28 7003
    window->SkipItems = false;
1193b4be Poul Sander 2025-02-03 21:28 7004
1193b4be Poul Sander 2025-02-03 21:28 7005
    // Draw window + handle manual resize
1193b4be Poul Sander 2025-02-03 21:28 7006
    // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
1193b4be Poul Sander 2025-02-03 21:28 7007
    const float window_rounding = window->WindowRounding;
1193b4be Poul Sander 2025-02-03 21:28 7008
    const float window_border_size = window->WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 7009
    if (window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7010
    {
1193b4be Poul Sander 2025-02-03 21:28 7011
        // Title bar only
1193b4be Poul Sander 2025-02-03 21:28 7012
        const float backup_border_size = style.FrameBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 7013
        g.Style.FrameBorderSize = window->WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 7014
        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
1193b4be Poul Sander 2025-02-03 21:28 7015
        if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 7016
            title_bar_col |= IM_COL32_A_MASK; // No alpha (we don't support is_docking_transparent_payload here because simpler and less meaningful, but could with a bit of code shuffle/reuse)
1193b4be Poul Sander 2025-02-03 21:28 7017
        RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
1193b4be Poul Sander 2025-02-03 21:28 7018
        g.Style.FrameBorderSize = backup_border_size;
1193b4be Poul Sander 2025-02-03 21:28 7019
    }
1193b4be Poul Sander 2025-02-03 21:28 7020
    else
1193b4be Poul Sander 2025-02-03 21:28 7021
    {
1193b4be Poul Sander 2025-02-03 21:28 7022
        // Window background
1193b4be Poul Sander 2025-02-03 21:28 7023
        if (!(flags & ImGuiWindowFlags_NoBackground))
1193b4be Poul Sander 2025-02-03 21:28 7024
        {
1193b4be Poul Sander 2025-02-03 21:28 7025
            bool is_docking_transparent_payload = false;
1193b4be Poul Sander 2025-02-03 21:28 7026
            if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
1193b4be Poul Sander 2025-02-03 21:28 7027
                if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
1193b4be Poul Sander 2025-02-03 21:28 7028
                    is_docking_transparent_payload = true;
1193b4be Poul Sander 2025-02-03 21:28 7029
1193b4be Poul Sander 2025-02-03 21:28 7030
            ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
1193b4be Poul Sander 2025-02-03 21:28 7031
            if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 7032
            {
1193b4be Poul Sander 2025-02-03 21:28 7033
                bg_col |= IM_COL32_A_MASK; // No alpha
1193b4be Poul Sander 2025-02-03 21:28 7034
                if (is_docking_transparent_payload)
1193b4be Poul Sander 2025-02-03 21:28 7035
                    window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
1193b4be Poul Sander 2025-02-03 21:28 7036
            }
1193b4be Poul Sander 2025-02-03 21:28 7037
            else
1193b4be Poul Sander 2025-02-03 21:28 7038
            {
1193b4be Poul Sander 2025-02-03 21:28 7039
                // Adjust alpha. For docking
1193b4be Poul Sander 2025-02-03 21:28 7040
                bool override_alpha = false;
1193b4be Poul Sander 2025-02-03 21:28 7041
                float alpha = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 7042
                if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
1193b4be Poul Sander 2025-02-03 21:28 7043
                {
1193b4be Poul Sander 2025-02-03 21:28 7044
                    alpha = g.NextWindowData.BgAlphaVal;
1193b4be Poul Sander 2025-02-03 21:28 7045
                    override_alpha = true;
1193b4be Poul Sander 2025-02-03 21:28 7046
                }
1193b4be Poul Sander 2025-02-03 21:28 7047
                if (is_docking_transparent_payload)
1193b4be Poul Sander 2025-02-03 21:28 7048
                {
1193b4be Poul Sander 2025-02-03 21:28 7049
                    alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
1193b4be Poul Sander 2025-02-03 21:28 7050
                    override_alpha = true;
1193b4be Poul Sander 2025-02-03 21:28 7051
                }
1193b4be Poul Sander 2025-02-03 21:28 7052
                if (override_alpha)
1193b4be Poul Sander 2025-02-03 21:28 7053
                    bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
1193b4be Poul Sander 2025-02-03 21:28 7054
            }
1193b4be Poul Sander 2025-02-03 21:28 7055
1193b4be Poul Sander 2025-02-03 21:28 7056
            // Render, for docked windows and host windows we ensure bg goes before decorations
1193b4be Poul Sander 2025-02-03 21:28 7057
            if (window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7058
                window->DockNode->LastBgColor = bg_col;
1193b4be Poul Sander 2025-02-03 21:28 7059
            ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList;
1193b4be Poul Sander 2025-02-03 21:28 7060
            if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
1193b4be Poul Sander 2025-02-03 21:28 7061
                bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
1193b4be Poul Sander 2025-02-03 21:28 7062
            bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
1193b4be Poul Sander 2025-02-03 21:28 7063
            if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
1193b4be Poul Sander 2025-02-03 21:28 7064
                bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
1193b4be Poul Sander 2025-02-03 21:28 7065
        }
1193b4be Poul Sander 2025-02-03 21:28 7066
        if (window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7067
            window->DockNode->IsBgDrawnThisFrame = true;
1193b4be Poul Sander 2025-02-03 21:28 7068
1193b4be Poul Sander 2025-02-03 21:28 7069
        // Title bar
1193b4be Poul Sander 2025-02-03 21:28 7070
        // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
1193b4be Poul Sander 2025-02-03 21:28 7071
        // in order for their pos/size to be matching their undocking state.)
1193b4be Poul Sander 2025-02-03 21:28 7072
        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7073
        {
1193b4be Poul Sander 2025-02-03 21:28 7074
            ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
1193b4be Poul Sander 2025-02-03 21:28 7075
            if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 7076
                title_bar_col |= IM_COL32_A_MASK; // No alpha
1193b4be Poul Sander 2025-02-03 21:28 7077
            window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
1193b4be Poul Sander 2025-02-03 21:28 7078
        }
1193b4be Poul Sander 2025-02-03 21:28 7079
1193b4be Poul Sander 2025-02-03 21:28 7080
        // Menu bar
1193b4be Poul Sander 2025-02-03 21:28 7081
        if (flags & ImGuiWindowFlags_MenuBar)
1193b4be Poul Sander 2025-02-03 21:28 7082
        {
1193b4be Poul Sander 2025-02-03 21:28 7083
            ImRect menu_bar_rect = window->MenuBarRect();
1193b4be Poul Sander 2025-02-03 21:28 7084
            menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
1193b4be Poul Sander 2025-02-03 21:28 7085
            window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
1193b4be Poul Sander 2025-02-03 21:28 7086
            if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
1193b4be Poul Sander 2025-02-03 21:28 7087
                window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
1193b4be Poul Sander 2025-02-03 21:28 7088
        }
1193b4be Poul Sander 2025-02-03 21:28 7089
1193b4be Poul Sander 2025-02-03 21:28 7090
        // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
1193b4be Poul Sander 2025-02-03 21:28 7091
        ImGuiDockNode* node = window->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 7092
        if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
1193b4be Poul Sander 2025-02-03 21:28 7093
        {
1193b4be Poul Sander 2025-02-03 21:28 7094
            float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f);
1193b4be Poul Sander 2025-02-03 21:28 7095
            float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f);
1193b4be Poul Sander 2025-02-03 21:28 7096
            ImVec2 p = node->Pos;
1193b4be Poul Sander 2025-02-03 21:28 7097
            ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
1193b4be Poul Sander 2025-02-03 21:28 7098
            ImGuiID unhide_id = window->GetID("#UNHIDE");
1193b4be Poul Sander 2025-02-03 21:28 7099
            KeepAliveID(unhide_id);
1193b4be Poul Sander 2025-02-03 21:28 7100
            bool hovered, held;
1193b4be Poul Sander 2025-02-03 21:28 7101
            if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren))
1193b4be Poul Sander 2025-02-03 21:28 7102
                node->WantHiddenTabBarToggle = true;
1193b4be Poul Sander 2025-02-03 21:28 7103
            else if (held && IsMouseDragging(0))
1193b4be Poul Sander 2025-02-03 21:28 7104
                StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button
1193b4be Poul Sander 2025-02-03 21:28 7105
1193b4be Poul Sander 2025-02-03 21:28 7106
            // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
1193b4be Poul Sander 2025-02-03 21:28 7107
            ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1193b4be Poul Sander 2025-02-03 21:28 7108
            window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
1193b4be Poul Sander 2025-02-03 21:28 7109
        }
1193b4be Poul Sander 2025-02-03 21:28 7110
1193b4be Poul Sander 2025-02-03 21:28 7111
        // Scrollbars
1193b4be Poul Sander 2025-02-03 21:28 7112
        if (window->ScrollbarX)
1193b4be Poul Sander 2025-02-03 21:28 7113
            Scrollbar(ImGuiAxis_X);
1193b4be Poul Sander 2025-02-03 21:28 7114
        if (window->ScrollbarY)
1193b4be Poul Sander 2025-02-03 21:28 7115
            Scrollbar(ImGuiAxis_Y);
1193b4be Poul Sander 2025-02-03 21:28 7116
1193b4be Poul Sander 2025-02-03 21:28 7117
        // Render resize grips (after their input handling so we don't have a frame of latency)
1193b4be Poul Sander 2025-02-03 21:28 7118
        if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
1193b4be Poul Sander 2025-02-03 21:28 7119
        {
1193b4be Poul Sander 2025-02-03 21:28 7120
            for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
1193b4be Poul Sander 2025-02-03 21:28 7121
            {
1193b4be Poul Sander 2025-02-03 21:28 7122
                const ImU32 col = resize_grip_col[resize_grip_n];
1193b4be Poul Sander 2025-02-03 21:28 7123
                if ((col & IM_COL32_A_MASK) == 0)
1193b4be Poul Sander 2025-02-03 21:28 7124
                    continue;
1193b4be Poul Sander 2025-02-03 21:28 7125
                const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
1193b4be Poul Sander 2025-02-03 21:28 7126
                const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
1193b4be Poul Sander 2025-02-03 21:28 7127
                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
1193b4be Poul Sander 2025-02-03 21:28 7128
                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
1193b4be Poul Sander 2025-02-03 21:28 7129
                window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
1193b4be Poul Sander 2025-02-03 21:28 7130
                window->DrawList->PathFillConvex(col);
1193b4be Poul Sander 2025-02-03 21:28 7131
            }
1193b4be Poul Sander 2025-02-03 21:28 7132
        }
1193b4be Poul Sander 2025-02-03 21:28 7133
1193b4be Poul Sander 2025-02-03 21:28 7134
        // Borders (for dock node host they will be rendered over after the tab bar)
1193b4be Poul Sander 2025-02-03 21:28 7135
        if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 7136
            RenderWindowOuterBorders(window);
1193b4be Poul Sander 2025-02-03 21:28 7137
    }
1193b4be Poul Sander 2025-02-03 21:28 7138
}
1193b4be Poul Sander 2025-02-03 21:28 7139
1193b4be Poul Sander 2025-02-03 21:28 7140
// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
1193b4be Poul Sander 2025-02-03 21:28 7141
// Render title text, collapse button, close button
1193b4be Poul Sander 2025-02-03 21:28 7142
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
1193b4be Poul Sander 2025-02-03 21:28 7143
{
1193b4be Poul Sander 2025-02-03 21:28 7144
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 7145
    ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 7146
    ImGuiWindowFlags flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7147
1193b4be Poul Sander 2025-02-03 21:28 7148
    const bool has_close_button = (p_open != NULL);
1193b4be Poul Sander 2025-02-03 21:28 7149
    const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 7150
1193b4be Poul Sander 2025-02-03 21:28 7151
    // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
1193b4be Poul Sander 2025-02-03 21:28 7152
    // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?
1193b4be Poul Sander 2025-02-03 21:28 7153
    const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
1193b4be Poul Sander 2025-02-03 21:28 7154
    g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
1193b4be Poul Sander 2025-02-03 21:28 7155
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
1193b4be Poul Sander 2025-02-03 21:28 7156
1193b4be Poul Sander 2025-02-03 21:28 7157
    // Layout buttons
1193b4be Poul Sander 2025-02-03 21:28 7158
    // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
1193b4be Poul Sander 2025-02-03 21:28 7159
    float pad_l = style.FramePadding.x;
1193b4be Poul Sander 2025-02-03 21:28 7160
    float pad_r = style.FramePadding.x;
1193b4be Poul Sander 2025-02-03 21:28 7161
    float button_sz = g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 7162
    ImVec2 close_button_pos;
1193b4be Poul Sander 2025-02-03 21:28 7163
    ImVec2 collapse_button_pos;
1193b4be Poul Sander 2025-02-03 21:28 7164
    if (has_close_button)
1193b4be Poul Sander 2025-02-03 21:28 7165
    {
1193b4be Poul Sander 2025-02-03 21:28 7166
        close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 7167
        pad_r += button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 7168
    }
1193b4be Poul Sander 2025-02-03 21:28 7169
    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
1193b4be Poul Sander 2025-02-03 21:28 7170
    {
1193b4be Poul Sander 2025-02-03 21:28 7171
        collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 7172
        pad_r += button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 7173
    }
1193b4be Poul Sander 2025-02-03 21:28 7174
    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
1193b4be Poul Sander 2025-02-03 21:28 7175
    {
1193b4be Poul Sander 2025-02-03 21:28 7176
        collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 7177
        pad_l += button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 7178
    }
1193b4be Poul Sander 2025-02-03 21:28 7179
1193b4be Poul Sander 2025-02-03 21:28 7180
    // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
1193b4be Poul Sander 2025-02-03 21:28 7181
    if (has_collapse_button)
1193b4be Poul Sander 2025-02-03 21:28 7182
        if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
1193b4be Poul Sander 2025-02-03 21:28 7183
            window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
1193b4be Poul Sander 2025-02-03 21:28 7184
1193b4be Poul Sander 2025-02-03 21:28 7185
    // Close button
1193b4be Poul Sander 2025-02-03 21:28 7186
    if (has_close_button)
1193b4be Poul Sander 2025-02-03 21:28 7187
        if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
1193b4be Poul Sander 2025-02-03 21:28 7188
            *p_open = false;
1193b4be Poul Sander 2025-02-03 21:28 7189
1193b4be Poul Sander 2025-02-03 21:28 7190
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 7191
    g.CurrentItemFlags = item_flags_backup;
1193b4be Poul Sander 2025-02-03 21:28 7192
1193b4be Poul Sander 2025-02-03 21:28 7193
    // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
1193b4be Poul Sander 2025-02-03 21:28 7194
    // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
1193b4be Poul Sander 2025-02-03 21:28 7195
    const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7196
    const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7197
1193b4be Poul Sander 2025-02-03 21:28 7198
    // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
1193b4be Poul Sander 2025-02-03 21:28 7199
    // while uncentered title text will still reach edges correctly.
1193b4be Poul Sander 2025-02-03 21:28 7200
    if (pad_l > style.FramePadding.x)
1193b4be Poul Sander 2025-02-03 21:28 7201
        pad_l += g.Style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 7202
    if (pad_r > style.FramePadding.x)
1193b4be Poul Sander 2025-02-03 21:28 7203
        pad_r += g.Style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 7204
    if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
1193b4be Poul Sander 2025-02-03 21:28 7205
    {
1193b4be Poul Sander 2025-02-03 21:28 7206
        float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
1193b4be Poul Sander 2025-02-03 21:28 7207
        float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
1193b4be Poul Sander 2025-02-03 21:28 7208
        pad_l = ImMax(pad_l, pad_extend * centerness);
1193b4be Poul Sander 2025-02-03 21:28 7209
        pad_r = ImMax(pad_r, pad_extend * centerness);
1193b4be Poul Sander 2025-02-03 21:28 7210
    }
1193b4be Poul Sander 2025-02-03 21:28 7211
1193b4be Poul Sander 2025-02-03 21:28 7212
    ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 7213
    ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 7214
    if (flags & ImGuiWindowFlags_UnsavedDocument)
1193b4be Poul Sander 2025-02-03 21:28 7215
    {
1193b4be Poul Sander 2025-02-03 21:28 7216
        ImVec2 marker_pos;
1193b4be Poul Sander 2025-02-03 21:28 7217
        marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 7218
        marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
1193b4be Poul Sander 2025-02-03 21:28 7219
        if (marker_pos.x > layout_r.Min.x)
1193b4be Poul Sander 2025-02-03 21:28 7220
        {
1193b4be Poul Sander 2025-02-03 21:28 7221
            RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
1193b4be Poul Sander 2025-02-03 21:28 7222
            clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
1193b4be Poul Sander 2025-02-03 21:28 7223
        }
1193b4be Poul Sander 2025-02-03 21:28 7224
    }
1193b4be Poul Sander 2025-02-03 21:28 7225
    //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 7226
    //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 7227
    RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
1193b4be Poul Sander 2025-02-03 21:28 7228
}
1193b4be Poul Sander 2025-02-03 21:28 7229
1193b4be Poul Sander 2025-02-03 21:28 7230
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
1193b4be Poul Sander 2025-02-03 21:28 7231
{
1193b4be Poul Sander 2025-02-03 21:28 7232
    window->ParentWindow = parent_window;
1193b4be Poul Sander 2025-02-03 21:28 7233
    window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
1193b4be Poul Sander 2025-02-03 21:28 7234
    if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
1193b4be Poul Sander 2025-02-03 21:28 7235
    {
1193b4be Poul Sander 2025-02-03 21:28 7236
        window->RootWindowDockTree = parent_window->RootWindowDockTree;
1193b4be Poul Sander 2025-02-03 21:28 7237
        if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
1193b4be Poul Sander 2025-02-03 21:28 7238
            window->RootWindow = parent_window->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 7239
    }
1193b4be Poul Sander 2025-02-03 21:28 7240
    if (parent_window && (flags & ImGuiWindowFlags_Popup))
1193b4be Poul Sander 2025-02-03 21:28 7241
        window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
1193b4be Poul Sander 2025-02-03 21:28 7242
    if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ?
1193b4be Poul Sander 2025-02-03 21:28 7243
        window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
1193b4be Poul Sander 2025-02-03 21:28 7244
    while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
1193b4be Poul Sander 2025-02-03 21:28 7245
    {
1193b4be Poul Sander 2025-02-03 21:28 7246
        IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 7247
        window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 7248
    }
1193b4be Poul Sander 2025-02-03 21:28 7249
}
1193b4be Poul Sander 2025-02-03 21:28 7250
1193b4be Poul Sander 2025-02-03 21:28 7251
// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
1193b4be Poul Sander 2025-02-03 21:28 7252
// This is designed as a toy/test-bed for
1193b4be Poul Sander 2025-02-03 21:28 7253
void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 7254
{
1193b4be Poul Sander 2025-02-03 21:28 7255
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 7256
    window->SkipRefresh = false;
1193b4be Poul Sander 2025-02-03 21:28 7257
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
1193b4be Poul Sander 2025-02-03 21:28 7258
        return;
1193b4be Poul Sander 2025-02-03 21:28 7259
    if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
1193b4be Poul Sander 2025-02-03 21:28 7260
    {
1193b4be Poul Sander 2025-02-03 21:28 7261
        // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
1193b4be Poul Sander 2025-02-03 21:28 7262
        if (window->Appearing) // If currently appearing
1193b4be Poul Sander 2025-02-03 21:28 7263
            return;
1193b4be Poul Sander 2025-02-03 21:28 7264
        if (window->Hidden) // If was hidden (previous frame)
1193b4be Poul Sander 2025-02-03 21:28 7265
            return;
1193b4be Poul Sander 2025-02-03 21:28 7266
        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
1193b4be Poul Sander 2025-02-03 21:28 7267
            if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))
1193b4be Poul Sander 2025-02-03 21:28 7268
                return;
1193b4be Poul Sander 2025-02-03 21:28 7269
        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 7270
            if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))
1193b4be Poul Sander 2025-02-03 21:28 7271
                return;
1193b4be Poul Sander 2025-02-03 21:28 7272
        window->DrawList = NULL;
1193b4be Poul Sander 2025-02-03 21:28 7273
        window->SkipRefresh = true;
1193b4be Poul Sander 2025-02-03 21:28 7274
    }
1193b4be Poul Sander 2025-02-03 21:28 7275
}
1193b4be Poul Sander 2025-02-03 21:28 7276
1193b4be Poul Sander 2025-02-03 21:28 7277
static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 7278
{
1193b4be Poul Sander 2025-02-03 21:28 7279
    window->Active = true;
1193b4be Poul Sander 2025-02-03 21:28 7280
    for (ImGuiWindow* child : window->DC.ChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 7281
        if (!child->Hidden)
1193b4be Poul Sander 2025-02-03 21:28 7282
        {
1193b4be Poul Sander 2025-02-03 21:28 7283
            child->Active = child->SkipRefresh = true;
1193b4be Poul Sander 2025-02-03 21:28 7284
            SetWindowActiveForSkipRefresh(child);
1193b4be Poul Sander 2025-02-03 21:28 7285
        }
1193b4be Poul Sander 2025-02-03 21:28 7286
}
1193b4be Poul Sander 2025-02-03 21:28 7287
1193b4be Poul Sander 2025-02-03 21:28 7288
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
1193b4be Poul Sander 2025-02-03 21:28 7289
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
1193b4be Poul Sander 2025-02-03 21:28 7290
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
1193b4be Poul Sander 2025-02-03 21:28 7291
// - WindowA            // FindBlockingModal() returns Modal1
1193b4be Poul Sander 2025-02-03 21:28 7292
//   - WindowB          //                  .. returns Modal1
1193b4be Poul Sander 2025-02-03 21:28 7293
//   - Modal1           //                  .. returns Modal2
1193b4be Poul Sander 2025-02-03 21:28 7294
//      - WindowC       //                  .. returns Modal2
1193b4be Poul Sander 2025-02-03 21:28 7295
//          - WindowD   //                  .. returns Modal2
1193b4be Poul Sander 2025-02-03 21:28 7296
//          - Modal2    //                  .. returns Modal2
1193b4be Poul Sander 2025-02-03 21:28 7297
//            - WindowE //                  .. returns NULL
1193b4be Poul Sander 2025-02-03 21:28 7298
// Notes:
1193b4be Poul Sander 2025-02-03 21:28 7299
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
1193b4be Poul Sander 2025-02-03 21:28 7300
//   Only difference is here we check for ->Active/WasActive but it may be unnecessary.
1193b4be Poul Sander 2025-02-03 21:28 7301
ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 7302
{
1193b4be Poul Sander 2025-02-03 21:28 7303
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 7304
    if (g.OpenPopupStack.Size <= 0)
1193b4be Poul Sander 2025-02-03 21:28 7305
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 7306
1193b4be Poul Sander 2025-02-03 21:28 7307
    // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
1193b4be Poul Sander 2025-02-03 21:28 7308
    for (ImGuiPopupData& popup_data : g.OpenPopupStack)
1193b4be Poul Sander 2025-02-03 21:28 7309
    {
1193b4be Poul Sander 2025-02-03 21:28 7310
        ImGuiWindow* popup_window = popup_data.Window;
1193b4be Poul Sander 2025-02-03 21:28 7311
        if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
1193b4be Poul Sander 2025-02-03 21:28 7312
            continue;
1193b4be Poul Sander 2025-02-03 21:28 7313
        if (!popup_window->Active && !popup_window->WasActive)      // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
1193b4be Poul Sander 2025-02-03 21:28 7314
            continue;
1193b4be Poul Sander 2025-02-03 21:28 7315
        if (window == NULL)                                         // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
1193b4be Poul Sander 2025-02-03 21:28 7316
            return popup_window;
1193b4be Poul Sander 2025-02-03 21:28 7317
        if (IsWindowWithinBeginStackOf(window, popup_window))       // Window may be over modal
1193b4be Poul Sander 2025-02-03 21:28 7318
            continue;
1193b4be Poul Sander 2025-02-03 21:28 7319
        return popup_window;                                        // Place window right below first block modal
1193b4be Poul Sander 2025-02-03 21:28 7320
    }
1193b4be Poul Sander 2025-02-03 21:28 7321
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 7322
}
1193b4be Poul Sander 2025-02-03 21:28 7323
1193b4be Poul Sander 2025-02-03 21:28 7324
// Push a new Dear ImGui window to add widgets to.
1193b4be Poul Sander 2025-02-03 21:28 7325
// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
1193b4be Poul Sander 2025-02-03 21:28 7326
// - Begin/End can be called multiple times during the frame with the same window name to append content.
1193b4be Poul Sander 2025-02-03 21:28 7327
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
1193b4be Poul Sander 2025-02-03 21:28 7328
//   You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
1193b4be Poul Sander 2025-02-03 21:28 7329
// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
1193b4be Poul Sander 2025-02-03 21:28 7330
// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
1193b4be Poul Sander 2025-02-03 21:28 7331
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 7332
{
1193b4be Poul Sander 2025-02-03 21:28 7333
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 7334
    const ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 7335
    IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
1193b4be Poul Sander 2025-02-03 21:28 7336
    IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 7337
    IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
1193b4be Poul Sander 2025-02-03 21:28 7338
1193b4be Poul Sander 2025-02-03 21:28 7339
    // Find or create
1193b4be Poul Sander 2025-02-03 21:28 7340
    ImGuiWindow* window = FindWindowByName(name);
1193b4be Poul Sander 2025-02-03 21:28 7341
    const bool window_just_created = (window == NULL);
1193b4be Poul Sander 2025-02-03 21:28 7342
    if (window_just_created)
1193b4be Poul Sander 2025-02-03 21:28 7343
        window = CreateNewWindow(name, flags);
1193b4be Poul Sander 2025-02-03 21:28 7344
1193b4be Poul Sander 2025-02-03 21:28 7345
    // [DEBUG] Debug break requested by user
1193b4be Poul Sander 2025-02-03 21:28 7346
    if (g.DebugBreakInWindow == window->ID)
1193b4be Poul Sander 2025-02-03 21:28 7347
        IM_DEBUG_BREAK();
1193b4be Poul Sander 2025-02-03 21:28 7348
1193b4be Poul Sander 2025-02-03 21:28 7349
    // Automatically disable manual moving/resizing when NoInputs is set
1193b4be Poul Sander 2025-02-03 21:28 7350
    if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
1193b4be Poul Sander 2025-02-03 21:28 7351
        flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
1193b4be Poul Sander 2025-02-03 21:28 7352
1193b4be Poul Sander 2025-02-03 21:28 7353
    const int current_frame = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 7354
    const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
1193b4be Poul Sander 2025-02-03 21:28 7355
    window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
1193b4be Poul Sander 2025-02-03 21:28 7356
1193b4be Poul Sander 2025-02-03 21:28 7357
    // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
1193b4be Poul Sander 2025-02-03 21:28 7358
    bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
1193b4be Poul Sander 2025-02-03 21:28 7359
    if (flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 7360
    {
1193b4be Poul Sander 2025-02-03 21:28 7361
        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
1193b4be Poul Sander 2025-02-03 21:28 7362
        window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
1193b4be Poul Sander 2025-02-03 21:28 7363
        window_just_activated_by_user |= (window != popup_ref.Window);
1193b4be Poul Sander 2025-02-03 21:28 7364
    }
1193b4be Poul Sander 2025-02-03 21:28 7365
1193b4be Poul Sander 2025-02-03 21:28 7366
    // Update Flags, LastFrameActive, BeginOrderXXX fields
1193b4be Poul Sander 2025-02-03 21:28 7367
    const bool window_was_appearing = window->Appearing;
1193b4be Poul Sander 2025-02-03 21:28 7368
    if (first_begin_of_the_frame)
1193b4be Poul Sander 2025-02-03 21:28 7369
    {
1193b4be Poul Sander 2025-02-03 21:28 7370
        UpdateWindowInFocusOrderList(window, window_just_created, flags);
1193b4be Poul Sander 2025-02-03 21:28 7371
        window->Appearing = window_just_activated_by_user;
1193b4be Poul Sander 2025-02-03 21:28 7372
        if (window->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 7373
            SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
1193b4be Poul Sander 2025-02-03 21:28 7374
        window->FlagsPreviousFrame = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7375
        window->Flags = (ImGuiWindowFlags)flags;
1193b4be Poul Sander 2025-02-03 21:28 7376
        window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
1193b4be Poul Sander 2025-02-03 21:28 7377
        window->LastFrameActive = current_frame;
1193b4be Poul Sander 2025-02-03 21:28 7378
        window->LastTimeActive = (float)g.Time;
1193b4be Poul Sander 2025-02-03 21:28 7379
        window->BeginOrderWithinParent = 0;
1193b4be Poul Sander 2025-02-03 21:28 7380
        window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
1193b4be Poul Sander 2025-02-03 21:28 7381
    }
1193b4be Poul Sander 2025-02-03 21:28 7382
    else
1193b4be Poul Sander 2025-02-03 21:28 7383
    {
1193b4be Poul Sander 2025-02-03 21:28 7384
        flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7385
    }
1193b4be Poul Sander 2025-02-03 21:28 7386
1193b4be Poul Sander 2025-02-03 21:28 7387
    // Docking
1193b4be Poul Sander 2025-02-03 21:28 7388
    // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
1193b4be Poul Sander 2025-02-03 21:28 7389
    IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
1193b4be Poul Sander 2025-02-03 21:28 7390
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
1193b4be Poul Sander 2025-02-03 21:28 7391
        SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
1193b4be Poul Sander 2025-02-03 21:28 7392
    if (first_begin_of_the_frame)
1193b4be Poul Sander 2025-02-03 21:28 7393
    {
1193b4be Poul Sander 2025-02-03 21:28 7394
        bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
1193b4be Poul Sander 2025-02-03 21:28 7395
        bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
1193b4be Poul Sander 2025-02-03 21:28 7396
        bool dock_node_was_visible = window->DockNodeIsVisible;
1193b4be Poul Sander 2025-02-03 21:28 7397
        bool dock_tab_was_visible = window->DockTabIsVisible;
1193b4be Poul Sander 2025-02-03 21:28 7398
        if (has_dock_node || new_auto_dock_node)
1193b4be Poul Sander 2025-02-03 21:28 7399
        {
1193b4be Poul Sander 2025-02-03 21:28 7400
            BeginDocked(window, p_open);
1193b4be Poul Sander 2025-02-03 21:28 7401
            flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7402
            if (window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7403
            {
1193b4be Poul Sander 2025-02-03 21:28 7404
                IM_ASSERT(window->DockNode != NULL);
1193b4be Poul Sander 2025-02-03 21:28 7405
                g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints
1193b4be Poul Sander 2025-02-03 21:28 7406
            }
1193b4be Poul Sander 2025-02-03 21:28 7407
1193b4be Poul Sander 2025-02-03 21:28 7408
            // Amend the Appearing flag
1193b4be Poul Sander 2025-02-03 21:28 7409
            if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)
1193b4be Poul Sander 2025-02-03 21:28 7410
            {
1193b4be Poul Sander 2025-02-03 21:28 7411
                window->Appearing = true;
1193b4be Poul Sander 2025-02-03 21:28 7412
                SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
1193b4be Poul Sander 2025-02-03 21:28 7413
            }
1193b4be Poul Sander 2025-02-03 21:28 7414
        }
1193b4be Poul Sander 2025-02-03 21:28 7415
        else
1193b4be Poul Sander 2025-02-03 21:28 7416
        {
1193b4be Poul Sander 2025-02-03 21:28 7417
            window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 7418
        }
1193b4be Poul Sander 2025-02-03 21:28 7419
    }
1193b4be Poul Sander 2025-02-03 21:28 7420
1193b4be Poul Sander 2025-02-03 21:28 7421
    // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
1193b4be Poul Sander 2025-02-03 21:28 7422
    ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
1193b4be Poul Sander 2025-02-03 21:28 7423
    ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 7424
    IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
1193b4be Poul Sander 2025-02-03 21:28 7425
1193b4be Poul Sander 2025-02-03 21:28 7426
    // We allow window memory to be compacted so recreate the base stack when needed.
1193b4be Poul Sander 2025-02-03 21:28 7427
    if (window->IDStack.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 7428
        window->IDStack.push_back(window->ID);
1193b4be Poul Sander 2025-02-03 21:28 7429
1193b4be Poul Sander 2025-02-03 21:28 7430
    // Add to stack
1193b4be Poul Sander 2025-02-03 21:28 7431
    g.CurrentWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 7432
    g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
1193b4be Poul Sander 2025-02-03 21:28 7433
    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
1193b4be Poul Sander 2025-02-03 21:28 7434
    window_stack_data.Window = window;
1193b4be Poul Sander 2025-02-03 21:28 7435
    window_stack_data.ParentLastItemDataBackup = g.LastItemData;
1193b4be Poul Sander 2025-02-03 21:28 7436
    window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
1193b4be Poul Sander 2025-02-03 21:28 7437
    ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
1193b4be Poul Sander 2025-02-03 21:28 7438
    g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
1193b4be Poul Sander 2025-02-03 21:28 7439
    if (flags & ImGuiWindowFlags_ChildMenu)
1193b4be Poul Sander 2025-02-03 21:28 7440
        g.BeginMenuDepth++;
1193b4be Poul Sander 2025-02-03 21:28 7441
1193b4be Poul Sander 2025-02-03 21:28 7442
    // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
1193b4be Poul Sander 2025-02-03 21:28 7443
    if (first_begin_of_the_frame)
1193b4be Poul Sander 2025-02-03 21:28 7444
    {
1193b4be Poul Sander 2025-02-03 21:28 7445
        UpdateWindowParentAndRootLinks(window, flags, parent_window);
1193b4be Poul Sander 2025-02-03 21:28 7446
        window->ParentWindowInBeginStack = parent_window_in_stack;
1193b4be Poul Sander 2025-02-03 21:28 7447
1193b4be Poul Sander 2025-02-03 21:28 7448
        // Focus route
1193b4be Poul Sander 2025-02-03 21:28 7449
        // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
1193b4be Poul Sander 2025-02-03 21:28 7450
        // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
1193b4be Poul Sander 2025-02-03 21:28 7451
        window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL;
1193b4be Poul Sander 2025-02-03 21:28 7452
        if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL)
1193b4be Poul Sander 2025-02-03 21:28 7453
            if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute)
1193b4be Poul Sander 2025-02-03 21:28 7454
                window->ParentWindowForFocusRoute = window->DockNode->HostWindow;
1193b4be Poul Sander 2025-02-03 21:28 7455
1193b4be Poul Sander 2025-02-03 21:28 7456
        // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()
1193b4be Poul Sander 2025-02-03 21:28 7457
        if (window->WindowClass.FocusRouteParentWindowId != 0)
1193b4be Poul Sander 2025-02-03 21:28 7458
        {
1193b4be Poul Sander 2025-02-03 21:28 7459
            window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId);
1193b4be Poul Sander 2025-02-03 21:28 7460
            IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId.
1193b4be Poul Sander 2025-02-03 21:28 7461
        }
1193b4be Poul Sander 2025-02-03 21:28 7462
    }
1193b4be Poul Sander 2025-02-03 21:28 7463
1193b4be Poul Sander 2025-02-03 21:28 7464
    // Add to focus scope stack
1193b4be Poul Sander 2025-02-03 21:28 7465
    PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
1193b4be Poul Sander 2025-02-03 21:28 7466
    window->NavRootFocusScopeId = g.CurrentFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 7467
1193b4be Poul Sander 2025-02-03 21:28 7468
    // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
1193b4be Poul Sander 2025-02-03 21:28 7469
    if (flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 7470
    {
1193b4be Poul Sander 2025-02-03 21:28 7471
        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
1193b4be Poul Sander 2025-02-03 21:28 7472
        popup_ref.Window = window;
1193b4be Poul Sander 2025-02-03 21:28 7473
        popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
1193b4be Poul Sander 2025-02-03 21:28 7474
        g.BeginPopupStack.push_back(popup_ref);
1193b4be Poul Sander 2025-02-03 21:28 7475
        window->PopupId = popup_ref.PopupId;
1193b4be Poul Sander 2025-02-03 21:28 7476
    }
1193b4be Poul Sander 2025-02-03 21:28 7477
1193b4be Poul Sander 2025-02-03 21:28 7478
    // Process SetNextWindow***() calls
1193b4be Poul Sander 2025-02-03 21:28 7479
    // (FIXME: Consider splitting the HasXXX flags into X/Y components
1193b4be Poul Sander 2025-02-03 21:28 7480
    bool window_pos_set_by_api = false;
1193b4be Poul Sander 2025-02-03 21:28 7481
    bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
1193b4be Poul Sander 2025-02-03 21:28 7482
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
1193b4be Poul Sander 2025-02-03 21:28 7483
    {
1193b4be Poul Sander 2025-02-03 21:28 7484
        window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
1193b4be Poul Sander 2025-02-03 21:28 7485
        if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
1193b4be Poul Sander 2025-02-03 21:28 7486
        {
1193b4be Poul Sander 2025-02-03 21:28 7487
            // May be processed on the next frame if this is our first frame and we are measuring size
1193b4be Poul Sander 2025-02-03 21:28 7488
            // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
1193b4be Poul Sander 2025-02-03 21:28 7489
            window->SetWindowPosVal = g.NextWindowData.PosVal;
1193b4be Poul Sander 2025-02-03 21:28 7490
            window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
1193b4be Poul Sander 2025-02-03 21:28 7491
            window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
1193b4be Poul Sander 2025-02-03 21:28 7492
        }
1193b4be Poul Sander 2025-02-03 21:28 7493
        else
1193b4be Poul Sander 2025-02-03 21:28 7494
        {
1193b4be Poul Sander 2025-02-03 21:28 7495
            SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
1193b4be Poul Sander 2025-02-03 21:28 7496
        }
1193b4be Poul Sander 2025-02-03 21:28 7497
    }
1193b4be Poul Sander 2025-02-03 21:28 7498
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
1193b4be Poul Sander 2025-02-03 21:28 7499
    {
1193b4be Poul Sander 2025-02-03 21:28 7500
        window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7501
        window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7502
        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
1193b4be Poul Sander 2025-02-03 21:28 7503
            g.NextWindowData.SizeVal.x = window->SizeFull.x;
1193b4be Poul Sander 2025-02-03 21:28 7504
        if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
1193b4be Poul Sander 2025-02-03 21:28 7505
            g.NextWindowData.SizeVal.y = window->SizeFull.y;
1193b4be Poul Sander 2025-02-03 21:28 7506
        SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
1193b4be Poul Sander 2025-02-03 21:28 7507
    }
1193b4be Poul Sander 2025-02-03 21:28 7508
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
1193b4be Poul Sander 2025-02-03 21:28 7509
    {
1193b4be Poul Sander 2025-02-03 21:28 7510
        if (g.NextWindowData.ScrollVal.x >= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 7511
        {
1193b4be Poul Sander 2025-02-03 21:28 7512
            window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
1193b4be Poul Sander 2025-02-03 21:28 7513
            window->ScrollTargetCenterRatio.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7514
        }
1193b4be Poul Sander 2025-02-03 21:28 7515
        if (g.NextWindowData.ScrollVal.y >= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 7516
        {
1193b4be Poul Sander 2025-02-03 21:28 7517
            window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
1193b4be Poul Sander 2025-02-03 21:28 7518
            window->ScrollTargetCenterRatio.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7519
        }
1193b4be Poul Sander 2025-02-03 21:28 7520
    }
1193b4be Poul Sander 2025-02-03 21:28 7521
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
1193b4be Poul Sander 2025-02-03 21:28 7522
        window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
1193b4be Poul Sander 2025-02-03 21:28 7523
    else if (first_begin_of_the_frame)
1193b4be Poul Sander 2025-02-03 21:28 7524
        window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7525
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
1193b4be Poul Sander 2025-02-03 21:28 7526
        window->WindowClass = g.NextWindowData.WindowClass;
1193b4be Poul Sander 2025-02-03 21:28 7527
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
1193b4be Poul Sander 2025-02-03 21:28 7528
        SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
1193b4be Poul Sander 2025-02-03 21:28 7529
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
1193b4be Poul Sander 2025-02-03 21:28 7530
        FocusWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 7531
    if (window->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 7532
        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
1193b4be Poul Sander 2025-02-03 21:28 7533
1193b4be Poul Sander 2025-02-03 21:28 7534
    // [EXPERIMENTAL] Skip Refresh mode
1193b4be Poul Sander 2025-02-03 21:28 7535
    UpdateWindowSkipRefresh(window);
1193b4be Poul Sander 2025-02-03 21:28 7536
1193b4be Poul Sander 2025-02-03 21:28 7537
    // Nested root windows (typically tooltips) override disabled state
1193b4be Poul Sander 2025-02-03 21:28 7538
    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 7539
        BeginDisabledOverrideReenable();
1193b4be Poul Sander 2025-02-03 21:28 7540
1193b4be Poul Sander 2025-02-03 21:28 7541
    // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
1193b4be Poul Sander 2025-02-03 21:28 7542
    g.CurrentWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 7543
1193b4be Poul Sander 2025-02-03 21:28 7544
    // When reusing window again multiple times a frame, just append content (don't need to setup again)
1193b4be Poul Sander 2025-02-03 21:28 7545
    if (first_begin_of_the_frame && !window->SkipRefresh)
1193b4be Poul Sander 2025-02-03 21:28 7546
    {
1193b4be Poul Sander 2025-02-03 21:28 7547
        // Initialize
1193b4be Poul Sander 2025-02-03 21:28 7548
        const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
1193b4be Poul Sander 2025-02-03 21:28 7549
        const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
1193b4be Poul Sander 2025-02-03 21:28 7550
        window->Active = true;
1193b4be Poul Sander 2025-02-03 21:28 7551
        window->HasCloseButton = (p_open != NULL);
1193b4be Poul Sander 2025-02-03 21:28 7552
        window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 7553
        window->IDStack.resize(1);
1193b4be Poul Sander 2025-02-03 21:28 7554
        window->DrawList->_ResetForNewFrame();
1193b4be Poul Sander 2025-02-03 21:28 7555
        window->DC.CurrentTableIdx = -1;
1193b4be Poul Sander 2025-02-03 21:28 7556
        if (flags & ImGuiWindowFlags_DockNodeHost)
1193b4be Poul Sander 2025-02-03 21:28 7557
        {
1193b4be Poul Sander 2025-02-03 21:28 7558
            window->DrawList->ChannelsSplit(2);
1193b4be Poul Sander 2025-02-03 21:28 7559
            window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later
1193b4be Poul Sander 2025-02-03 21:28 7560
        }
1193b4be Poul Sander 2025-02-03 21:28 7561
1193b4be Poul Sander 2025-02-03 21:28 7562
        // Restore buffer capacity when woken from a compacted state, to avoid
1193b4be Poul Sander 2025-02-03 21:28 7563
        if (window->MemoryCompacted)
1193b4be Poul Sander 2025-02-03 21:28 7564
            GcAwakeTransientWindowBuffers(window);
1193b4be Poul Sander 2025-02-03 21:28 7565
1193b4be Poul Sander 2025-02-03 21:28 7566
        // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
1193b4be Poul Sander 2025-02-03 21:28 7567
        // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
1193b4be Poul Sander 2025-02-03 21:28 7568
        bool window_title_visible_elsewhere = false;
1193b4be Poul Sander 2025-02-03 21:28 7569
        if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
1193b4be Poul Sander 2025-02-03 21:28 7570
            window_title_visible_elsewhere = true;
1193b4be Poul Sander 2025-02-03 21:28 7571
        else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
1193b4be Poul Sander 2025-02-03 21:28 7572
            window_title_visible_elsewhere = true;
1193b4be Poul Sander 2025-02-03 21:28 7573
        if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
1193b4be Poul Sander 2025-02-03 21:28 7574
        {
1193b4be Poul Sander 2025-02-03 21:28 7575
            size_t buf_len = (size_t)window->NameBufLen;
1193b4be Poul Sander 2025-02-03 21:28 7576
            window->Name = ImStrdupcpy(window->Name, &buf_len, name);
1193b4be Poul Sander 2025-02-03 21:28 7577
            window->NameBufLen = (int)buf_len;
1193b4be Poul Sander 2025-02-03 21:28 7578
        }
1193b4be Poul Sander 2025-02-03 21:28 7579
1193b4be Poul Sander 2025-02-03 21:28 7580
        // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
1193b4be Poul Sander 2025-02-03 21:28 7581
1193b4be Poul Sander 2025-02-03 21:28 7582
        // Update contents size from last frame for auto-fitting (or use explicit size)
1193b4be Poul Sander 2025-02-03 21:28 7583
        CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
1193b4be Poul Sander 2025-02-03 21:28 7584
1193b4be Poul Sander 2025-02-03 21:28 7585
        // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
1193b4be Poul Sander 2025-02-03 21:28 7586
        // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
1193b4be Poul Sander 2025-02-03 21:28 7587
        // it has a single usage before this code block and may be set below before it is finally checked.
1193b4be Poul Sander 2025-02-03 21:28 7588
        if (window->HiddenFramesCanSkipItems > 0)
1193b4be Poul Sander 2025-02-03 21:28 7589
            window->HiddenFramesCanSkipItems--;
1193b4be Poul Sander 2025-02-03 21:28 7590
        if (window->HiddenFramesCannotSkipItems > 0)
1193b4be Poul Sander 2025-02-03 21:28 7591
            window->HiddenFramesCannotSkipItems--;
1193b4be Poul Sander 2025-02-03 21:28 7592
        if (window->HiddenFramesForRenderOnly > 0)
1193b4be Poul Sander 2025-02-03 21:28 7593
            window->HiddenFramesForRenderOnly--;
1193b4be Poul Sander 2025-02-03 21:28 7594
1193b4be Poul Sander 2025-02-03 21:28 7595
        // Hide new windows for one frame until they calculate their size
1193b4be Poul Sander 2025-02-03 21:28 7596
        if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
1193b4be Poul Sander 2025-02-03 21:28 7597
            window->HiddenFramesCannotSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 7598
1193b4be Poul Sander 2025-02-03 21:28 7599
        // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
1193b4be Poul Sander 2025-02-03 21:28 7600
        // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
1193b4be Poul Sander 2025-02-03 21:28 7601
        if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 7602
        {
1193b4be Poul Sander 2025-02-03 21:28 7603
            window->HiddenFramesCannotSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 7604
            if (flags & ImGuiWindowFlags_AlwaysAutoResize)
1193b4be Poul Sander 2025-02-03 21:28 7605
            {
1193b4be Poul Sander 2025-02-03 21:28 7606
                if (!window_size_x_set_by_api)
1193b4be Poul Sander 2025-02-03 21:28 7607
                    window->Size.x = window->SizeFull.x = 0.f;
1193b4be Poul Sander 2025-02-03 21:28 7608
                if (!window_size_y_set_by_api)
1193b4be Poul Sander 2025-02-03 21:28 7609
                    window->Size.y = window->SizeFull.y = 0.f;
1193b4be Poul Sander 2025-02-03 21:28 7610
                window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
1193b4be Poul Sander 2025-02-03 21:28 7611
            }
1193b4be Poul Sander 2025-02-03 21:28 7612
        }
1193b4be Poul Sander 2025-02-03 21:28 7613
1193b4be Poul Sander 2025-02-03 21:28 7614
        // SELECT VIEWPORT
1193b4be Poul Sander 2025-02-03 21:28 7615
        // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
1193b4be Poul Sander 2025-02-03 21:28 7616
1193b4be Poul Sander 2025-02-03 21:28 7617
        WindowSelectViewport(window);
1193b4be Poul Sander 2025-02-03 21:28 7618
        SetCurrentViewport(window, window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 7619
        window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 7620
        SetCurrentWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 7621
        flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 7622
1193b4be Poul Sander 2025-02-03 21:28 7623
        // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
1193b4be Poul Sander 2025-02-03 21:28 7624
        // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
1193b4be Poul Sander 2025-02-03 21:28 7625
1193b4be Poul Sander 2025-02-03 21:28 7626
        if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 7627
            window->WindowBorderSize = style.ChildBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 7628
        else
1193b4be Poul Sander 2025-02-03 21:28 7629
            window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 7630
        window->WindowPadding = style.WindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 7631
        if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 7632
            window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7633
1193b4be Poul Sander 2025-02-03 21:28 7634
        // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
1193b4be Poul Sander 2025-02-03 21:28 7635
        window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
1193b4be Poul Sander 2025-02-03 21:28 7636
        window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
1193b4be Poul Sander 2025-02-03 21:28 7637
        window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 7638
        window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7639
1193b4be Poul Sander 2025-02-03 21:28 7640
        // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
1193b4be Poul Sander 2025-02-03 21:28 7641
        // Those flags will be altered further down in the function depending on more conditions.
1193b4be Poul Sander 2025-02-03 21:28 7642
        bool use_current_size_for_scrollbar_x = window_just_created;
1193b4be Poul Sander 2025-02-03 21:28 7643
        bool use_current_size_for_scrollbar_y = window_just_created;
1193b4be Poul Sander 2025-02-03 21:28 7644
        if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 7645
            use_current_size_for_scrollbar_x = true;
1193b4be Poul Sander 2025-02-03 21:28 7646
        if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
1193b4be Poul Sander 2025-02-03 21:28 7647
            use_current_size_for_scrollbar_y = true;
1193b4be Poul Sander 2025-02-03 21:28 7648
1193b4be Poul Sander 2025-02-03 21:28 7649
        // Collapse window by double-clicking on title bar
1193b4be Poul Sander 2025-02-03 21:28 7650
        // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
1193b4be Poul Sander 2025-02-03 21:28 7651
        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7652
        {
1193b4be Poul Sander 2025-02-03 21:28 7653
            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
1193b4be Poul Sander 2025-02-03 21:28 7654
            // so verify that we don't have items over the title bar.
1193b4be Poul Sander 2025-02-03 21:28 7655
            ImRect title_bar_rect = window->TitleBarRect();
1193b4be Poul Sander 2025-02-03 21:28 7656
            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
1193b4be Poul Sander 2025-02-03 21:28 7657
                if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 7658
                    window->WantCollapseToggle = true;
1193b4be Poul Sander 2025-02-03 21:28 7659
            if (window->WantCollapseToggle)
1193b4be Poul Sander 2025-02-03 21:28 7660
            {
1193b4be Poul Sander 2025-02-03 21:28 7661
                window->Collapsed = !window->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 7662
                if (!window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7663
                    use_current_size_for_scrollbar_y = true;
1193b4be Poul Sander 2025-02-03 21:28 7664
                MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 7665
            }
1193b4be Poul Sander 2025-02-03 21:28 7666
        }
1193b4be Poul Sander 2025-02-03 21:28 7667
        else
1193b4be Poul Sander 2025-02-03 21:28 7668
        {
1193b4be Poul Sander 2025-02-03 21:28 7669
            window->Collapsed = false;
1193b4be Poul Sander 2025-02-03 21:28 7670
        }
1193b4be Poul Sander 2025-02-03 21:28 7671
        window->WantCollapseToggle = false;
1193b4be Poul Sander 2025-02-03 21:28 7672
1193b4be Poul Sander 2025-02-03 21:28 7673
        // SIZE
1193b4be Poul Sander 2025-02-03 21:28 7674
1193b4be Poul Sander 2025-02-03 21:28 7675
        // Outer Decoration Sizes
1193b4be Poul Sander 2025-02-03 21:28 7676
        // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
1193b4be Poul Sander 2025-02-03 21:28 7677
        const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
1193b4be Poul Sander 2025-02-03 21:28 7678
        window->DecoOuterSizeX1 = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7679
        window->DecoOuterSizeX2 = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7680
        window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
1193b4be Poul Sander 2025-02-03 21:28 7681
        window->DecoOuterSizeY2 = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7682
        window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7683
1193b4be Poul Sander 2025-02-03 21:28 7684
        // Calculate auto-fit size, handle automatic resize
1193b4be Poul Sander 2025-02-03 21:28 7685
        const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
1193b4be Poul Sander 2025-02-03 21:28 7686
        if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7687
        {
1193b4be Poul Sander 2025-02-03 21:28 7688
            // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
1193b4be Poul Sander 2025-02-03 21:28 7689
            if (!window_size_x_set_by_api)
1193b4be Poul Sander 2025-02-03 21:28 7690
            {
1193b4be Poul Sander 2025-02-03 21:28 7691
                window->SizeFull.x = size_auto_fit.x;
1193b4be Poul Sander 2025-02-03 21:28 7692
                use_current_size_for_scrollbar_x = true;
1193b4be Poul Sander 2025-02-03 21:28 7693
            }
1193b4be Poul Sander 2025-02-03 21:28 7694
            if (!window_size_y_set_by_api)
1193b4be Poul Sander 2025-02-03 21:28 7695
            {
1193b4be Poul Sander 2025-02-03 21:28 7696
                window->SizeFull.y = size_auto_fit.y;
1193b4be Poul Sander 2025-02-03 21:28 7697
                use_current_size_for_scrollbar_y = true;
1193b4be Poul Sander 2025-02-03 21:28 7698
            }
1193b4be Poul Sander 2025-02-03 21:28 7699
        }
1193b4be Poul Sander 2025-02-03 21:28 7700
        else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 7701
        {
1193b4be Poul Sander 2025-02-03 21:28 7702
            // Auto-fit may only grow window during the first few frames
1193b4be Poul Sander 2025-02-03 21:28 7703
            // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
1193b4be Poul Sander 2025-02-03 21:28 7704
            if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
1193b4be Poul Sander 2025-02-03 21:28 7705
            {
1193b4be Poul Sander 2025-02-03 21:28 7706
                window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
1193b4be Poul Sander 2025-02-03 21:28 7707
                use_current_size_for_scrollbar_x = true;
1193b4be Poul Sander 2025-02-03 21:28 7708
            }
1193b4be Poul Sander 2025-02-03 21:28 7709
            if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 7710
            {
1193b4be Poul Sander 2025-02-03 21:28 7711
                window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
1193b4be Poul Sander 2025-02-03 21:28 7712
                use_current_size_for_scrollbar_y = true;
1193b4be Poul Sander 2025-02-03 21:28 7713
            }
1193b4be Poul Sander 2025-02-03 21:28 7714
            if (!window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7715
                MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 7716
        }
1193b4be Poul Sander 2025-02-03 21:28 7717
1193b4be Poul Sander 2025-02-03 21:28 7718
        // Apply minimum/maximum window size constraints and final size
1193b4be Poul Sander 2025-02-03 21:28 7719
        window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
1193b4be Poul Sander 2025-02-03 21:28 7720
        window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 7721
1193b4be Poul Sander 2025-02-03 21:28 7722
        // POSITION
1193b4be Poul Sander 2025-02-03 21:28 7723
1193b4be Poul Sander 2025-02-03 21:28 7724
        // Popup latch its initial position, will position itself when it appears next frame
1193b4be Poul Sander 2025-02-03 21:28 7725
        if (window_just_activated_by_user)
1193b4be Poul Sander 2025-02-03 21:28 7726
        {
1193b4be Poul Sander 2025-02-03 21:28 7727
            window->AutoPosLastDirection = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 7728
            if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
1193b4be Poul Sander 2025-02-03 21:28 7729
                window->Pos = g.BeginPopupStack.back().OpenPopupPos;
1193b4be Poul Sander 2025-02-03 21:28 7730
        }
1193b4be Poul Sander 2025-02-03 21:28 7731
1193b4be Poul Sander 2025-02-03 21:28 7732
        // Position child window
1193b4be Poul Sander 2025-02-03 21:28 7733
        if (flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 7734
        {
1193b4be Poul Sander 2025-02-03 21:28 7735
            IM_ASSERT(parent_window && parent_window->Active);
1193b4be Poul Sander 2025-02-03 21:28 7736
            window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
1193b4be Poul Sander 2025-02-03 21:28 7737
            parent_window->DC.ChildWindows.push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 7738
            if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
1193b4be Poul Sander 2025-02-03 21:28 7739
                window->Pos = parent_window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 7740
        }
1193b4be Poul Sander 2025-02-03 21:28 7741
1193b4be Poul Sander 2025-02-03 21:28 7742
        const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
1193b4be Poul Sander 2025-02-03 21:28 7743
        if (window_pos_with_pivot)
1193b4be Poul Sander 2025-02-03 21:28 7744
            SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
1193b4be Poul Sander 2025-02-03 21:28 7745
        else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
1193b4be Poul Sander 2025-02-03 21:28 7746
            window->Pos = FindBestWindowPosForPopup(window);
1193b4be Poul Sander 2025-02-03 21:28 7747
        else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
1193b4be Poul Sander 2025-02-03 21:28 7748
            window->Pos = FindBestWindowPosForPopup(window);
1193b4be Poul Sander 2025-02-03 21:28 7749
        else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
1193b4be Poul Sander 2025-02-03 21:28 7750
            window->Pos = FindBestWindowPosForPopup(window);
1193b4be Poul Sander 2025-02-03 21:28 7751
1193b4be Poul Sander 2025-02-03 21:28 7752
        // Late create viewport if we don't fit within our current host viewport.
1193b4be Poul Sander 2025-02-03 21:28 7753
        if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized))
1193b4be Poul Sander 2025-02-03 21:28 7754
            if (!window->Viewport->GetMainRect().Contains(window->Rect()))
1193b4be Poul Sander 2025-02-03 21:28 7755
            {
1193b4be Poul Sander 2025-02-03 21:28 7756
                // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
1193b4be Poul Sander 2025-02-03 21:28 7757
                //ImGuiViewport* old_viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 7758
                window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
1193b4be Poul Sander 2025-02-03 21:28 7759
1193b4be Poul Sander 2025-02-03 21:28 7760
                // FIXME-DPI
1193b4be Poul Sander 2025-02-03 21:28 7761
                //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
1193b4be Poul Sander 2025-02-03 21:28 7762
                SetCurrentViewport(window, window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 7763
                window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 7764
                SetCurrentWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 7765
            }
1193b4be Poul Sander 2025-02-03 21:28 7766
1193b4be Poul Sander 2025-02-03 21:28 7767
        if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 7768
            WindowSyncOwnedViewport(window, parent_window_in_stack);
1193b4be Poul Sander 2025-02-03 21:28 7769
1193b4be Poul Sander 2025-02-03 21:28 7770
        // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
1193b4be Poul Sander 2025-02-03 21:28 7771
        // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
1193b4be Poul Sander 2025-02-03 21:28 7772
        ImRect viewport_rect(window->Viewport->GetMainRect());
1193b4be Poul Sander 2025-02-03 21:28 7773
        ImRect viewport_work_rect(window->Viewport->GetWorkRect());
1193b4be Poul Sander 2025-02-03 21:28 7774
        ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
1193b4be Poul Sander 2025-02-03 21:28 7775
        ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
1193b4be Poul Sander 2025-02-03 21:28 7776
1193b4be Poul Sander 2025-02-03 21:28 7777
        // Clamp position/size so window stays visible within its viewport or monitor
1193b4be Poul Sander 2025-02-03 21:28 7778
        // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
1193b4be Poul Sander 2025-02-03 21:28 7779
        // FIXME: Similar to code in GetWindowAllowedExtentRect()
1193b4be Poul Sander 2025-02-03 21:28 7780
        if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 7781
        {
1193b4be Poul Sander 2025-02-03 21:28 7782
            if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 7783
            {
1193b4be Poul Sander 2025-02-03 21:28 7784
                ClampWindowPos(window, visibility_rect);
1193b4be Poul Sander 2025-02-03 21:28 7785
            }
1193b4be Poul Sander 2025-02-03 21:28 7786
            else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 7787
            {
1193b4be Poul Sander 2025-02-03 21:28 7788
                if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree)
1193b4be Poul Sander 2025-02-03 21:28 7789
                {
1193b4be Poul Sander 2025-02-03 21:28 7790
                    // While moving windows we allow them to straddle monitors (#7299, #3071)
1193b4be Poul Sander 2025-02-03 21:28 7791
                    visibility_rect = g.PlatformMonitorsFullWorkRect;
1193b4be Poul Sander 2025-02-03 21:28 7792
                }
1193b4be Poul Sander 2025-02-03 21:28 7793
                else
1193b4be Poul Sander 2025-02-03 21:28 7794
                {
1193b4be Poul Sander 2025-02-03 21:28 7795
                    // When not moving ensure visible in its monitor
1193b4be Poul Sander 2025-02-03 21:28 7796
                    // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.
1193b4be Poul Sander 2025-02-03 21:28 7797
                    const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 7798
                    visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize);
1193b4be Poul Sander 2025-02-03 21:28 7799
                }
1193b4be Poul Sander 2025-02-03 21:28 7800
                visibility_rect.Expand(-visibility_padding);
1193b4be Poul Sander 2025-02-03 21:28 7801
                ClampWindowPos(window, visibility_rect);
1193b4be Poul Sander 2025-02-03 21:28 7802
            }
1193b4be Poul Sander 2025-02-03 21:28 7803
        }
1193b4be Poul Sander 2025-02-03 21:28 7804
        window->Pos = ImTrunc(window->Pos);
1193b4be Poul Sander 2025-02-03 21:28 7805
1193b4be Poul Sander 2025-02-03 21:28 7806
        // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
1193b4be Poul Sander 2025-02-03 21:28 7807
        // Large values tend to lead to variety of artifacts and are not recommended.
1193b4be Poul Sander 2025-02-03 21:28 7808
        if (window->ViewportOwned || window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7809
            window->WindowRounding = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7810
        else
1193b4be Poul Sander 2025-02-03 21:28 7811
            window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
1193b4be Poul Sander 2025-02-03 21:28 7812
1193b4be Poul Sander 2025-02-03 21:28 7813
        // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
1193b4be Poul Sander 2025-02-03 21:28 7814
        //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
1193b4be Poul Sander 2025-02-03 21:28 7815
        //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 7816
1193b4be Poul Sander 2025-02-03 21:28 7817
        // Apply window focus (new and reactivated windows are moved to front)
1193b4be Poul Sander 2025-02-03 21:28 7818
        bool want_focus = false;
1193b4be Poul Sander 2025-02-03 21:28 7819
        if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
1193b4be Poul Sander 2025-02-03 21:28 7820
        {
1193b4be Poul Sander 2025-02-03 21:28 7821
            if (flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 7822
                want_focus = true;
1193b4be Poul Sander 2025-02-03 21:28 7823
            else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
1193b4be Poul Sander 2025-02-03 21:28 7824
                want_focus = true;
1193b4be Poul Sander 2025-02-03 21:28 7825
        }
1193b4be Poul Sander 2025-02-03 21:28 7826
1193b4be Poul Sander 2025-02-03 21:28 7827
        // [Test Engine] Register whole window in the item system (before submitting further decorations)
1193b4be Poul Sander 2025-02-03 21:28 7828
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 7829
        if (g.TestEngineHookItems)
1193b4be Poul Sander 2025-02-03 21:28 7830
        {
1193b4be Poul Sander 2025-02-03 21:28 7831
            IM_ASSERT(window->IDStack.Size == 1);
1193b4be Poul Sander 2025-02-03 21:28 7832
            window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
1193b4be Poul Sander 2025-02-03 21:28 7833
            IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
1193b4be Poul Sander 2025-02-03 21:28 7834
            IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
1193b4be Poul Sander 2025-02-03 21:28 7835
            window->IDStack.Size = 1;
1193b4be Poul Sander 2025-02-03 21:28 7836
        }
1193b4be Poul Sander 2025-02-03 21:28 7837
#endif
1193b4be Poul Sander 2025-02-03 21:28 7838
1193b4be Poul Sander 2025-02-03 21:28 7839
        // Decide if we are going to handle borders and resize grips
1193b4be Poul Sander 2025-02-03 21:28 7840
        const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
1193b4be Poul Sander 2025-02-03 21:28 7841
1193b4be Poul Sander 2025-02-03 21:28 7842
        // Handle manual resize: Resize Grips, Borders, Gamepad
1193b4be Poul Sander 2025-02-03 21:28 7843
        int border_hovered = -1, border_held = -1;
1193b4be Poul Sander 2025-02-03 21:28 7844
        ImU32 resize_grip_col[4] = {};
1193b4be Poul Sander 2025-02-03 21:28 7845
        const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
1193b4be Poul Sander 2025-02-03 21:28 7846
        const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
1193b4be Poul Sander 2025-02-03 21:28 7847
        if (handle_borders_and_resize_grips && !window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7848
            if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
1193b4be Poul Sander 2025-02-03 21:28 7849
            {
1193b4be Poul Sander 2025-02-03 21:28 7850
                if (auto_fit_mask & (1 << ImGuiAxis_X))
1193b4be Poul Sander 2025-02-03 21:28 7851
                    use_current_size_for_scrollbar_x = true;
1193b4be Poul Sander 2025-02-03 21:28 7852
                if (auto_fit_mask & (1 << ImGuiAxis_Y))
1193b4be Poul Sander 2025-02-03 21:28 7853
                    use_current_size_for_scrollbar_y = true;
1193b4be Poul Sander 2025-02-03 21:28 7854
            }
1193b4be Poul Sander 2025-02-03 21:28 7855
        window->ResizeBorderHovered = (signed char)border_hovered;
1193b4be Poul Sander 2025-02-03 21:28 7856
        window->ResizeBorderHeld = (signed char)border_held;
1193b4be Poul Sander 2025-02-03 21:28 7857
1193b4be Poul Sander 2025-02-03 21:28 7858
        // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
1193b4be Poul Sander 2025-02-03 21:28 7859
        if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 7860
        {
1193b4be Poul Sander 2025-02-03 21:28 7861
            if (!window->Viewport->PlatformRequestMove)
1193b4be Poul Sander 2025-02-03 21:28 7862
                window->Viewport->Pos = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 7863
            if (!window->Viewport->PlatformRequestResize)
1193b4be Poul Sander 2025-02-03 21:28 7864
                window->Viewport->Size = window->Size;
1193b4be Poul Sander 2025-02-03 21:28 7865
            window->Viewport->UpdateWorkRect();
1193b4be Poul Sander 2025-02-03 21:28 7866
            viewport_rect = window->Viewport->GetMainRect();
1193b4be Poul Sander 2025-02-03 21:28 7867
        }
1193b4be Poul Sander 2025-02-03 21:28 7868
1193b4be Poul Sander 2025-02-03 21:28 7869
        // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
1193b4be Poul Sander 2025-02-03 21:28 7870
        window->ViewportPos = window->Viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 7871
1193b4be Poul Sander 2025-02-03 21:28 7872
        // SCROLLBAR VISIBILITY
1193b4be Poul Sander 2025-02-03 21:28 7873
1193b4be Poul Sander 2025-02-03 21:28 7874
        // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
1193b4be Poul Sander 2025-02-03 21:28 7875
        if (!window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 7876
        {
1193b4be Poul Sander 2025-02-03 21:28 7877
            // When reading the current size we need to read it after size constraints have been applied.
1193b4be Poul Sander 2025-02-03 21:28 7878
            // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
1193b4be Poul Sander 2025-02-03 21:28 7879
            // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
1193b4be Poul Sander 2025-02-03 21:28 7880
            ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
1193b4be Poul Sander 2025-02-03 21:28 7881
            ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
1193b4be Poul Sander 2025-02-03 21:28 7882
            ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 7883
            float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
1193b4be Poul Sander 2025-02-03 21:28 7884
            float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
1193b4be Poul Sander 2025-02-03 21:28 7885
            //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
1193b4be Poul Sander 2025-02-03 21:28 7886
            window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
1193b4be Poul Sander 2025-02-03 21:28 7887
            window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
1193b4be Poul Sander 2025-02-03 21:28 7888
            if (window->ScrollbarX && !window->ScrollbarY)
1193b4be Poul Sander 2025-02-03 21:28 7889
                window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 7890
            window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 7891
1193b4be Poul Sander 2025-02-03 21:28 7892
            // Amend the partially filled window->DecorationXXX values.
1193b4be Poul Sander 2025-02-03 21:28 7893
            window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
1193b4be Poul Sander 2025-02-03 21:28 7894
            window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
1193b4be Poul Sander 2025-02-03 21:28 7895
        }
1193b4be Poul Sander 2025-02-03 21:28 7896
1193b4be Poul Sander 2025-02-03 21:28 7897
        // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
1193b4be Poul Sander 2025-02-03 21:28 7898
        // Update various regions. Variables they depend on should be set above in this function.
1193b4be Poul Sander 2025-02-03 21:28 7899
        // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
1193b4be Poul Sander 2025-02-03 21:28 7900
1193b4be Poul Sander 2025-02-03 21:28 7901
        // Outer rectangle
1193b4be Poul Sander 2025-02-03 21:28 7902
        // Not affected by window border size. Used by:
1193b4be Poul Sander 2025-02-03 21:28 7903
        // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
1193b4be Poul Sander 2025-02-03 21:28 7904
        // - Begin() initial clipping rect for drawing window background and borders.
1193b4be Poul Sander 2025-02-03 21:28 7905
        // - Begin() clipping whole child
1193b4be Poul Sander 2025-02-03 21:28 7906
        const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
1193b4be Poul Sander 2025-02-03 21:28 7907
        const ImRect outer_rect = window->Rect();
1193b4be Poul Sander 2025-02-03 21:28 7908
        const ImRect title_bar_rect = window->TitleBarRect();
1193b4be Poul Sander 2025-02-03 21:28 7909
        window->OuterRectClipped = outer_rect;
1193b4be Poul Sander 2025-02-03 21:28 7910
        if (window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 7911
            window->OuterRectClipped.Min.y += window->TitleBarHeight;
1193b4be Poul Sander 2025-02-03 21:28 7912
        window->OuterRectClipped.ClipWith(host_rect);
1193b4be Poul Sander 2025-02-03 21:28 7913
1193b4be Poul Sander 2025-02-03 21:28 7914
        // Inner rectangle
1193b4be Poul Sander 2025-02-03 21:28 7915
        // Not affected by window border size. Used by:
1193b4be Poul Sander 2025-02-03 21:28 7916
        // - InnerClipRect
1193b4be Poul Sander 2025-02-03 21:28 7917
        // - ScrollToRectEx()
1193b4be Poul Sander 2025-02-03 21:28 7918
        // - NavUpdatePageUpPageDown()
1193b4be Poul Sander 2025-02-03 21:28 7919
        // - Scrollbar()
1193b4be Poul Sander 2025-02-03 21:28 7920
        window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
1193b4be Poul Sander 2025-02-03 21:28 7921
        window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
1193b4be Poul Sander 2025-02-03 21:28 7922
        window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
1193b4be Poul Sander 2025-02-03 21:28 7923
        window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
1193b4be Poul Sander 2025-02-03 21:28 7924
1193b4be Poul Sander 2025-02-03 21:28 7925
        // Inner clipping rectangle.
1193b4be Poul Sander 2025-02-03 21:28 7926
        // - Extend a outside of normal work region up to borders.
1193b4be Poul Sander 2025-02-03 21:28 7927
        // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
1193b4be Poul Sander 2025-02-03 21:28 7928
        // - It also makes clipped items be more noticeable.
1193b4be Poul Sander 2025-02-03 21:28 7929
        // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
1193b4be Poul Sander 2025-02-03 21:28 7930
        // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
1193b4be Poul Sander 2025-02-03 21:28 7931
        // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
1193b4be Poul Sander 2025-02-03 21:28 7932
        // Affected by window/frame border size. Used by:
1193b4be Poul Sander 2025-02-03 21:28 7933
        // - Begin() initial clip rect
1193b4be Poul Sander 2025-02-03 21:28 7934
        float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
1193b4be Poul Sander 2025-02-03 21:28 7935
1193b4be Poul Sander 2025-02-03 21:28 7936
        // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
1193b4be Poul Sander 2025-02-03 21:28 7937
        // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
1193b4be Poul Sander 2025-02-03 21:28 7938
        // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here).
1193b4be Poul Sander 2025-02-03 21:28 7939
        window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 7940
        window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 7941
        window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 7942
        window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 7943
        window->InnerClipRect.ClipWithFull(host_rect);
1193b4be Poul Sander 2025-02-03 21:28 7944
1193b4be Poul Sander 2025-02-03 21:28 7945
        // Default item width. Make it proportional to window size if window manually resizes
1193b4be Poul Sander 2025-02-03 21:28 7946
        if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
1193b4be Poul Sander 2025-02-03 21:28 7947
            window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
1193b4be Poul Sander 2025-02-03 21:28 7948
        else
1193b4be Poul Sander 2025-02-03 21:28 7949
            window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
1193b4be Poul Sander 2025-02-03 21:28 7950
1193b4be Poul Sander 2025-02-03 21:28 7951
        // SCROLLING
1193b4be Poul Sander 2025-02-03 21:28 7952
1193b4be Poul Sander 2025-02-03 21:28 7953
        // Lock down maximum scrolling
1193b4be Poul Sander 2025-02-03 21:28 7954
        // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
1193b4be Poul Sander 2025-02-03 21:28 7955
        // for right/bottom aligned items without creating a scrollbar.
1193b4be Poul Sander 2025-02-03 21:28 7956
        window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
1193b4be Poul Sander 2025-02-03 21:28 7957
        window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
1193b4be Poul Sander 2025-02-03 21:28 7958
1193b4be Poul Sander 2025-02-03 21:28 7959
        // Apply scrolling
1193b4be Poul Sander 2025-02-03 21:28 7960
        window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
1193b4be Poul Sander 2025-02-03 21:28 7961
        window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 7962
        window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 7963
1193b4be Poul Sander 2025-02-03 21:28 7964
        // DRAWING
1193b4be Poul Sander 2025-02-03 21:28 7965
1193b4be Poul Sander 2025-02-03 21:28 7966
        // Setup draw list and outer clipping rectangle
1193b4be Poul Sander 2025-02-03 21:28 7967
        IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
1193b4be Poul Sander 2025-02-03 21:28 7968
        window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
1193b4be Poul Sander 2025-02-03 21:28 7969
        PushClipRect(host_rect.Min, host_rect.Max, false);
1193b4be Poul Sander 2025-02-03 21:28 7970
1193b4be Poul Sander 2025-02-03 21:28 7971
        // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
1193b4be Poul Sander 2025-02-03 21:28 7972
        // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
1193b4be Poul Sander 2025-02-03 21:28 7973
        // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
1193b4be Poul Sander 2025-02-03 21:28 7974
        const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
1193b4be Poul Sander 2025-02-03 21:28 7975
        if (is_undocked_or_docked_visible)
1193b4be Poul Sander 2025-02-03 21:28 7976
        {
1193b4be Poul Sander 2025-02-03 21:28 7977
            bool render_decorations_in_parent = false;
1193b4be Poul Sander 2025-02-03 21:28 7978
            if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
1193b4be Poul Sander 2025-02-03 21:28 7979
            {
1193b4be Poul Sander 2025-02-03 21:28 7980
                // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
1193b4be Poul Sander 2025-02-03 21:28 7981
                // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
1193b4be Poul Sander 2025-02-03 21:28 7982
                ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
1193b4be Poul Sander 2025-02-03 21:28 7983
                bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
1193b4be Poul Sander 2025-02-03 21:28 7984
                bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 7985
                if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
1193b4be Poul Sander 2025-02-03 21:28 7986
                    render_decorations_in_parent = true;
1193b4be Poul Sander 2025-02-03 21:28 7987
            }
1193b4be Poul Sander 2025-02-03 21:28 7988
            if (render_decorations_in_parent)
1193b4be Poul Sander 2025-02-03 21:28 7989
                window->DrawList = parent_window->DrawList;
1193b4be Poul Sander 2025-02-03 21:28 7990
1193b4be Poul Sander 2025-02-03 21:28 7991
            // Handle title bar, scrollbar, resize grips and resize borders
1193b4be Poul Sander 2025-02-03 21:28 7992
            const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 7993
            const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));
1193b4be Poul Sander 2025-02-03 21:28 7994
            RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
1193b4be Poul Sander 2025-02-03 21:28 7995
1193b4be Poul Sander 2025-02-03 21:28 7996
            if (render_decorations_in_parent)
1193b4be Poul Sander 2025-02-03 21:28 7997
                window->DrawList = &window->DrawListInst;
1193b4be Poul Sander 2025-02-03 21:28 7998
        }
1193b4be Poul Sander 2025-02-03 21:28 7999
1193b4be Poul Sander 2025-02-03 21:28 8000
        // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
1193b4be Poul Sander 2025-02-03 21:28 8001
1193b4be Poul Sander 2025-02-03 21:28 8002
        // Work rectangle.
1193b4be Poul Sander 2025-02-03 21:28 8003
        // Affected by window padding and border size. Used by:
1193b4be Poul Sander 2025-02-03 21:28 8004
        // - Columns() for right-most edge
1193b4be Poul Sander 2025-02-03 21:28 8005
        // - TreeNode(), CollapsingHeader() for right-most edge
1193b4be Poul Sander 2025-02-03 21:28 8006
        // - BeginTabBar() for right-most edge
1193b4be Poul Sander 2025-02-03 21:28 8007
        const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 8008
        const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 8009
        const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
1193b4be Poul Sander 2025-02-03 21:28 8010
        const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
1193b4be Poul Sander 2025-02-03 21:28 8011
        window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
1193b4be Poul Sander 2025-02-03 21:28 8012
        window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
1193b4be Poul Sander 2025-02-03 21:28 8013
        window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
1193b4be Poul Sander 2025-02-03 21:28 8014
        window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
1193b4be Poul Sander 2025-02-03 21:28 8015
        window->ParentWorkRect = window->WorkRect;
1193b4be Poul Sander 2025-02-03 21:28 8016
1193b4be Poul Sander 2025-02-03 21:28 8017
        // [LEGACY] Content Region
1193b4be Poul Sander 2025-02-03 21:28 8018
        // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
1193b4be Poul Sander 2025-02-03 21:28 8019
        // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
1193b4be Poul Sander 2025-02-03 21:28 8020
        // Used by:
1193b4be Poul Sander 2025-02-03 21:28 8021
        // - Mouse wheel scrolling + many other things
1193b4be Poul Sander 2025-02-03 21:28 8022
        window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
1193b4be Poul Sander 2025-02-03 21:28 8023
        window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
1193b4be Poul Sander 2025-02-03 21:28 8024
        window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
1193b4be Poul Sander 2025-02-03 21:28 8025
        window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
1193b4be Poul Sander 2025-02-03 21:28 8026
1193b4be Poul Sander 2025-02-03 21:28 8027
        // Setup drawing context
1193b4be Poul Sander 2025-02-03 21:28 8028
        // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
1193b4be Poul Sander 2025-02-03 21:28 8029
        window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
1193b4be Poul Sander 2025-02-03 21:28 8030
        window->DC.GroupOffset.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 8031
        window->DC.ColumnsOffset.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 8032
1193b4be Poul Sander 2025-02-03 21:28 8033
        // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
1193b4be Poul Sander 2025-02-03 21:28 8034
        // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.
1193b4be Poul Sander 2025-02-03 21:28 8035
        double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
1193b4be Poul Sander 2025-02-03 21:28 8036
        double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
1193b4be Poul Sander 2025-02-03 21:28 8037
        window->DC.CursorStartPos  = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
1193b4be Poul Sander 2025-02-03 21:28 8038
        window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
1193b4be Poul Sander 2025-02-03 21:28 8039
        window->DC.CursorPos = window->DC.CursorStartPos;
1193b4be Poul Sander 2025-02-03 21:28 8040
        window->DC.CursorPosPrevLine = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 8041
        window->DC.CursorMaxPos = window->DC.CursorStartPos;
1193b4be Poul Sander 2025-02-03 21:28 8042
        window->DC.IdealMaxPos = window->DC.CursorStartPos;
1193b4be Poul Sander 2025-02-03 21:28 8043
        window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 8044
        window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 8045
        window->DC.IsSameLine = window->DC.IsSetPos = false;
1193b4be Poul Sander 2025-02-03 21:28 8046
1193b4be Poul Sander 2025-02-03 21:28 8047
        window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 8048
        window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
1193b4be Poul Sander 2025-02-03 21:28 8049
        window->DC.NavLayersActiveMaskNext = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 8050
        window->DC.NavIsScrollPushableX = true;
1193b4be Poul Sander 2025-02-03 21:28 8051
        window->DC.NavHideHighlightOneFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 8052
        window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 8053
1193b4be Poul Sander 2025-02-03 21:28 8054
        window->DC.MenuBarAppending = false;
1193b4be Poul Sander 2025-02-03 21:28 8055
        window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
1193b4be Poul Sander 2025-02-03 21:28 8056
        window->DC.TreeDepth = 0;
1193b4be Poul Sander 2025-02-03 21:28 8057
        window->DC.TreeHasStackDataDepthMask = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 8058
        window->DC.ChildWindows.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 8059
        window->DC.StateStorage = &window->StateStorage;
1193b4be Poul Sander 2025-02-03 21:28 8060
        window->DC.CurrentColumns = NULL;
1193b4be Poul Sander 2025-02-03 21:28 8061
        window->DC.LayoutType = ImGuiLayoutType_Vertical;
1193b4be Poul Sander 2025-02-03 21:28 8062
        window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
1193b4be Poul Sander 2025-02-03 21:28 8063
1193b4be Poul Sander 2025-02-03 21:28 8064
        window->DC.ItemWidth = window->ItemWidthDefault;
1193b4be Poul Sander 2025-02-03 21:28 8065
        window->DC.TextWrapPos = -1.0f; // disabled
1193b4be Poul Sander 2025-02-03 21:28 8066
        window->DC.ItemWidthStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 8067
        window->DC.TextWrapPosStack.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 8068
        if (flags & ImGuiWindowFlags_Modal)
1193b4be Poul Sander 2025-02-03 21:28 8069
            window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
1193b4be Poul Sander 2025-02-03 21:28 8070
1193b4be Poul Sander 2025-02-03 21:28 8071
        if (window->AutoFitFramesX > 0)
1193b4be Poul Sander 2025-02-03 21:28 8072
            window->AutoFitFramesX--;
1193b4be Poul Sander 2025-02-03 21:28 8073
        if (window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 8074
            window->AutoFitFramesY--;
1193b4be Poul Sander 2025-02-03 21:28 8075
1193b4be Poul Sander 2025-02-03 21:28 8076
        // Clear SetNextWindowXXX data (can aim to move this higher in the function)
1193b4be Poul Sander 2025-02-03 21:28 8077
        g.NextWindowData.ClearFlags();
1193b4be Poul Sander 2025-02-03 21:28 8078
1193b4be Poul Sander 2025-02-03 21:28 8079
        // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
1193b4be Poul Sander 2025-02-03 21:28 8080
        // We ImGuiFocusRequestFlags_UnlessBelowModal to:
1193b4be Poul Sander 2025-02-03 21:28 8081
        // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
1193b4be Poul Sander 2025-02-03 21:28 8082
        // - Position window behind the modal that is not a begin-parent of this window.
1193b4be Poul Sander 2025-02-03 21:28 8083
        if (want_focus)
1193b4be Poul Sander 2025-02-03 21:28 8084
            FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
1193b4be Poul Sander 2025-02-03 21:28 8085
        if (want_focus && window == g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 8086
            NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
1193b4be Poul Sander 2025-02-03 21:28 8087
1193b4be Poul Sander 2025-02-03 21:28 8088
        // Close requested by platform window (apply to all windows in this viewport)
1193b4be Poul Sander 2025-02-03 21:28 8089
        if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
1193b4be Poul Sander 2025-02-03 21:28 8090
        {
1193b4be Poul Sander 2025-02-03 21:28 8091
            IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name);
1193b4be Poul Sander 2025-02-03 21:28 8092
            *p_open = false;
1193b4be Poul Sander 2025-02-03 21:28 8093
            g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. // FIXME-NAV: Try removing.
1193b4be Poul Sander 2025-02-03 21:28 8094
        }
1193b4be Poul Sander 2025-02-03 21:28 8095
1193b4be Poul Sander 2025-02-03 21:28 8096
        // Title bar
1193b4be Poul Sander 2025-02-03 21:28 8097
        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 8098
            RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
1193b4be Poul Sander 2025-02-03 21:28 8099
1193b4be Poul Sander 2025-02-03 21:28 8100
        // Clear hit test shape every frame
1193b4be Poul Sander 2025-02-03 21:28 8101
        window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
1193b4be Poul Sander 2025-02-03 21:28 8102
1193b4be Poul Sander 2025-02-03 21:28 8103
        if (flags & ImGuiWindowFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 8104
            g.TooltipPreviousWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 8105
1193b4be Poul Sander 2025-02-03 21:28 8106
        // Pressing CTRL+C while holding on a window copy its content to the clipboard
1193b4be Poul Sander 2025-02-03 21:28 8107
        // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
1193b4be Poul Sander 2025-02-03 21:28 8108
        // Maybe we can support CTRL+C on every element?
1193b4be Poul Sander 2025-02-03 21:28 8109
        /*
1193b4be Poul Sander 2025-02-03 21:28 8110
        //if (g.NavWindow == window && g.ActiveId == 0)
1193b4be Poul Sander 2025-02-03 21:28 8111
        if (g.ActiveId == window->MoveId)
1193b4be Poul Sander 2025-02-03 21:28 8112
            if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
1193b4be Poul Sander 2025-02-03 21:28 8113
                LogToClipboard();
1193b4be Poul Sander 2025-02-03 21:28 8114
        */
1193b4be Poul Sander 2025-02-03 21:28 8115
1193b4be Poul Sander 2025-02-03 21:28 8116
        if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
1193b4be Poul Sander 2025-02-03 21:28 8117
        {
1193b4be Poul Sander 2025-02-03 21:28 8118
            // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
1193b4be Poul Sander 2025-02-03 21:28 8119
            // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
1193b4be Poul Sander 2025-02-03 21:28 8120
            if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8121
                BeginDockableDragDropSource(window);
1193b4be Poul Sander 2025-02-03 21:28 8122
1193b4be Poul Sander 2025-02-03 21:28 8123
            // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
1193b4be Poul Sander 2025-02-03 21:28 8124
            if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
1193b4be Poul Sander 2025-02-03 21:28 8125
                if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)
1193b4be Poul Sander 2025-02-03 21:28 8126
                    if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
1193b4be Poul Sander 2025-02-03 21:28 8127
                        BeginDockableDragDropTarget(window);
1193b4be Poul Sander 2025-02-03 21:28 8128
        }
1193b4be Poul Sander 2025-02-03 21:28 8129
1193b4be Poul Sander 2025-02-03 21:28 8130
        // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
1193b4be Poul Sander 2025-02-03 21:28 8131
        // This is useful to allow creating context menus on title bar only, etc.
1193b4be Poul Sander 2025-02-03 21:28 8132
        SetLastItemDataForWindow(window, title_bar_rect);
1193b4be Poul Sander 2025-02-03 21:28 8133
1193b4be Poul Sander 2025-02-03 21:28 8134
        // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 8135
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 8136
        if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
1193b4be Poul Sander 2025-02-03 21:28 8137
            DebugLocateItemResolveWithLastItem();
1193b4be Poul Sander 2025-02-03 21:28 8138
#endif
1193b4be Poul Sander 2025-02-03 21:28 8139
1193b4be Poul Sander 2025-02-03 21:28 8140
        // [Test Engine] Register title bar / tab with MoveId.
1193b4be Poul Sander 2025-02-03 21:28 8141
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 8142
        if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
1193b4be Poul Sander 2025-02-03 21:28 8143
            IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
1193b4be Poul Sander 2025-02-03 21:28 8144
#endif
1193b4be Poul Sander 2025-02-03 21:28 8145
    }
1193b4be Poul Sander 2025-02-03 21:28 8146
    else
1193b4be Poul Sander 2025-02-03 21:28 8147
    {
1193b4be Poul Sander 2025-02-03 21:28 8148
        // Skip refresh always mark active
1193b4be Poul Sander 2025-02-03 21:28 8149
        if (window->SkipRefresh)
1193b4be Poul Sander 2025-02-03 21:28 8150
            SetWindowActiveForSkipRefresh(window);
1193b4be Poul Sander 2025-02-03 21:28 8151
1193b4be Poul Sander 2025-02-03 21:28 8152
        // Append
1193b4be Poul Sander 2025-02-03 21:28 8153
        SetCurrentViewport(window, window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 8154
        SetCurrentWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 8155
        g.NextWindowData.ClearFlags();
1193b4be Poul Sander 2025-02-03 21:28 8156
        SetLastItemDataForWindow(window, window->TitleBarRect());
1193b4be Poul Sander 2025-02-03 21:28 8157
    }
1193b4be Poul Sander 2025-02-03 21:28 8158
1193b4be Poul Sander 2025-02-03 21:28 8159
    if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)
1193b4be Poul Sander 2025-02-03 21:28 8160
        PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
1193b4be Poul Sander 2025-02-03 21:28 8161
1193b4be Poul Sander 2025-02-03 21:28 8162
    // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
1193b4be Poul Sander 2025-02-03 21:28 8163
    window->WriteAccessed = false;
1193b4be Poul Sander 2025-02-03 21:28 8164
    window->BeginCount++;
1193b4be Poul Sander 2025-02-03 21:28 8165
1193b4be Poul Sander 2025-02-03 21:28 8166
    // Update visibility
1193b4be Poul Sander 2025-02-03 21:28 8167
    if (first_begin_of_the_frame && !window->SkipRefresh)
1193b4be Poul Sander 2025-02-03 21:28 8168
    {
1193b4be Poul Sander 2025-02-03 21:28 8169
        // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
1193b4be Poul Sander 2025-02-03 21:28 8170
        // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
1193b4be Poul Sander 2025-02-03 21:28 8171
        // This is analogous to regular windows being hidden from one frame.
1193b4be Poul Sander 2025-02-03 21:28 8172
        // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed.
1193b4be Poul Sander 2025-02-03 21:28 8173
        if (window->DockIsActive && !window->DockTabIsVisible)
1193b4be Poul Sander 2025-02-03 21:28 8174
        {
1193b4be Poul Sander 2025-02-03 21:28 8175
            if (window->LastFrameJustFocused == g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 8176
                window->HiddenFramesCannotSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8177
            else
1193b4be Poul Sander 2025-02-03 21:28 8178
                window->HiddenFramesCanSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8179
        }
1193b4be Poul Sander 2025-02-03 21:28 8180
1193b4be Poul Sander 2025-02-03 21:28 8181
        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
1193b4be Poul Sander 2025-02-03 21:28 8182
        {
1193b4be Poul Sander 2025-02-03 21:28 8183
            // Child window can be out of sight and have "negative" clip windows.
1193b4be Poul Sander 2025-02-03 21:28 8184
            // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
1193b4be Poul Sander 2025-02-03 21:28 8185
            IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive);
1193b4be Poul Sander 2025-02-03 21:28 8186
            const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
1193b4be Poul Sander 2025-02-03 21:28 8187
            if (!g.LogEnabled && !nav_request)
1193b4be Poul Sander 2025-02-03 21:28 8188
                if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
1193b4be Poul Sander 2025-02-03 21:28 8189
                {
1193b4be Poul Sander 2025-02-03 21:28 8190
                    if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 8191
                        window->HiddenFramesCannotSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8192
                    else
1193b4be Poul Sander 2025-02-03 21:28 8193
                        window->HiddenFramesCanSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8194
                }
1193b4be Poul Sander 2025-02-03 21:28 8195
1193b4be Poul Sander 2025-02-03 21:28 8196
            // Hide along with parent or if parent is collapsed
1193b4be Poul Sander 2025-02-03 21:28 8197
            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
1193b4be Poul Sander 2025-02-03 21:28 8198
                window->HiddenFramesCanSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8199
            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
1193b4be Poul Sander 2025-02-03 21:28 8200
                window->HiddenFramesCannotSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8201
        }
1193b4be Poul Sander 2025-02-03 21:28 8202
1193b4be Poul Sander 2025-02-03 21:28 8203
        // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
1193b4be Poul Sander 2025-02-03 21:28 8204
        if (style.Alpha <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 8205
            window->HiddenFramesCanSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8206
1193b4be Poul Sander 2025-02-03 21:28 8207
        // Update the Hidden flag
1193b4be Poul Sander 2025-02-03 21:28 8208
        bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
1193b4be Poul Sander 2025-02-03 21:28 8209
        window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
1193b4be Poul Sander 2025-02-03 21:28 8210
1193b4be Poul Sander 2025-02-03 21:28 8211
        // Disable inputs for requested number of frames
1193b4be Poul Sander 2025-02-03 21:28 8212
        if (window->DisableInputsFrames > 0)
1193b4be Poul Sander 2025-02-03 21:28 8213
        {
1193b4be Poul Sander 2025-02-03 21:28 8214
            window->DisableInputsFrames--;
1193b4be Poul Sander 2025-02-03 21:28 8215
            window->Flags |= ImGuiWindowFlags_NoInputs;
1193b4be Poul Sander 2025-02-03 21:28 8216
        }
1193b4be Poul Sander 2025-02-03 21:28 8217
1193b4be Poul Sander 2025-02-03 21:28 8218
        // Update the SkipItems flag, used to early out of all items functions (no layout required)
1193b4be Poul Sander 2025-02-03 21:28 8219
        bool skip_items = false;
1193b4be Poul Sander 2025-02-03 21:28 8220
        if (window->Collapsed || !window->Active || hidden_regular)
1193b4be Poul Sander 2025-02-03 21:28 8221
            if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
1193b4be Poul Sander 2025-02-03 21:28 8222
                skip_items = true;
1193b4be Poul Sander 2025-02-03 21:28 8223
        window->SkipItems = skip_items;
1193b4be Poul Sander 2025-02-03 21:28 8224
1193b4be Poul Sander 2025-02-03 21:28 8225
        // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.
1193b4be Poul Sander 2025-02-03 21:28 8226
        if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 8227
            window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask;
1193b4be Poul Sander 2025-02-03 21:28 8228
1193b4be Poul Sander 2025-02-03 21:28 8229
        // Sanity check: there are two spots which can set Appearing = true
1193b4be Poul Sander 2025-02-03 21:28 8230
        // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false
1193b4be Poul Sander 2025-02-03 21:28 8231
        // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.
1193b4be Poul Sander 2025-02-03 21:28 8232
        if (window->SkipItems && !window->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 8233
            IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177
1193b4be Poul Sander 2025-02-03 21:28 8234
    }
1193b4be Poul Sander 2025-02-03 21:28 8235
    else if (first_begin_of_the_frame)
1193b4be Poul Sander 2025-02-03 21:28 8236
    {
1193b4be Poul Sander 2025-02-03 21:28 8237
        // Skip refresh mode
1193b4be Poul Sander 2025-02-03 21:28 8238
        window->SkipItems = true;
1193b4be Poul Sander 2025-02-03 21:28 8239
    }
1193b4be Poul Sander 2025-02-03 21:28 8240
1193b4be Poul Sander 2025-02-03 21:28 8241
    // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
1193b4be Poul Sander 2025-02-03 21:28 8242
    // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
1193b4be Poul Sander 2025-02-03 21:28 8243
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 8244
    if (!window->IsFallbackWindow)
1193b4be Poul Sander 2025-02-03 21:28 8245
        if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
1193b4be Poul Sander 2025-02-03 21:28 8246
        {
1193b4be Poul Sander 2025-02-03 21:28 8247
            if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
1193b4be Poul Sander 2025-02-03 21:28 8248
            if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
1193b4be Poul Sander 2025-02-03 21:28 8249
            return false;
1193b4be Poul Sander 2025-02-03 21:28 8250
        }
1193b4be Poul Sander 2025-02-03 21:28 8251
#endif
1193b4be Poul Sander 2025-02-03 21:28 8252
1193b4be Poul Sander 2025-02-03 21:28 8253
    return !window->SkipItems;
1193b4be Poul Sander 2025-02-03 21:28 8254
}
1193b4be Poul Sander 2025-02-03 21:28 8255
1193b4be Poul Sander 2025-02-03 21:28 8256
static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
1193b4be Poul Sander 2025-02-03 21:28 8257
{
1193b4be Poul Sander 2025-02-03 21:28 8258
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8259
    if (window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 8260
        SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect);
1193b4be Poul Sander 2025-02-03 21:28 8261
    else
1193b4be Poul Sander 2025-02-03 21:28 8262
        SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect);
1193b4be Poul Sander 2025-02-03 21:28 8263
}
1193b4be Poul Sander 2025-02-03 21:28 8264
1193b4be Poul Sander 2025-02-03 21:28 8265
void ImGui::End()
1193b4be Poul Sander 2025-02-03 21:28 8266
{
1193b4be Poul Sander 2025-02-03 21:28 8267
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8268
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8269
1193b4be Poul Sander 2025-02-03 21:28 8270
    // Error checking: verify that user hasn't called End() too many times!
1193b4be Poul Sander 2025-02-03 21:28 8271
    if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
1193b4be Poul Sander 2025-02-03 21:28 8272
    {
1193b4be Poul Sander 2025-02-03 21:28 8273
        IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 8274
        return;
1193b4be Poul Sander 2025-02-03 21:28 8275
    }
1193b4be Poul Sander 2025-02-03 21:28 8276
    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8277
1193b4be Poul Sander 2025-02-03 21:28 8278
    // Error checking: verify that user doesn't directly call End() on a child window.
1193b4be Poul Sander 2025-02-03 21:28 8279
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 8280
        IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
1193b4be Poul Sander 2025-02-03 21:28 8281
1193b4be Poul Sander 2025-02-03 21:28 8282
    // Close anything that is open
1193b4be Poul Sander 2025-02-03 21:28 8283
    if (window->DC.CurrentColumns)
1193b4be Poul Sander 2025-02-03 21:28 8284
        EndColumns();
1193b4be Poul Sander 2025-02-03 21:28 8285
    if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)   // Pop inner window clip rectangle
1193b4be Poul Sander 2025-02-03 21:28 8286
        PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 8287
    PopFocusScope();
1193b4be Poul Sander 2025-02-03 21:28 8288
    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 8289
        EndDisabledOverrideReenable();
1193b4be Poul Sander 2025-02-03 21:28 8290
1193b4be Poul Sander 2025-02-03 21:28 8291
    if (window->SkipRefresh)
1193b4be Poul Sander 2025-02-03 21:28 8292
    {
1193b4be Poul Sander 2025-02-03 21:28 8293
        IM_ASSERT(window->DrawList == NULL);
1193b4be Poul Sander 2025-02-03 21:28 8294
        window->DrawList = &window->DrawListInst;
1193b4be Poul Sander 2025-02-03 21:28 8295
    }
1193b4be Poul Sander 2025-02-03 21:28 8296
1193b4be Poul Sander 2025-02-03 21:28 8297
    // Stop logging
1193b4be Poul Sander 2025-02-03 21:28 8298
    if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging
1193b4be Poul Sander 2025-02-03 21:28 8299
        LogFinish();
1193b4be Poul Sander 2025-02-03 21:28 8300
1193b4be Poul Sander 2025-02-03 21:28 8301
    if (window->DC.IsSetPos)
1193b4be Poul Sander 2025-02-03 21:28 8302
        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
1193b4be Poul Sander 2025-02-03 21:28 8303
1193b4be Poul Sander 2025-02-03 21:28 8304
    // Docking: report contents sizes to parent to allow for auto-resize
1193b4be Poul Sander 2025-02-03 21:28 8305
    if (window->DockNode && window->DockTabIsVisible)
1193b4be Poul Sander 2025-02-03 21:28 8306
        if (ImGuiWindow* host_window = window->DockNode->HostWindow)         // FIXME-DOCK
1193b4be Poul Sander 2025-02-03 21:28 8307
            host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 8308
1193b4be Poul Sander 2025-02-03 21:28 8309
    // Pop from window stack
1193b4be Poul Sander 2025-02-03 21:28 8310
    g.LastItemData = window_stack_data.ParentLastItemDataBackup;
1193b4be Poul Sander 2025-02-03 21:28 8311
    if (window->Flags & ImGuiWindowFlags_ChildMenu)
1193b4be Poul Sander 2025-02-03 21:28 8312
        g.BeginMenuDepth--;
1193b4be Poul Sander 2025-02-03 21:28 8313
    if (window->Flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 8314
        g.BeginPopupStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8315
1193b4be Poul Sander 2025-02-03 21:28 8316
    // Error handling, state recovery
1193b4be Poul Sander 2025-02-03 21:28 8317
    if (g.IO.ConfigErrorRecovery)
1193b4be Poul Sander 2025-02-03 21:28 8318
        ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
1193b4be Poul Sander 2025-02-03 21:28 8319
1193b4be Poul Sander 2025-02-03 21:28 8320
    g.CurrentWindowStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8321
    SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
1193b4be Poul Sander 2025-02-03 21:28 8322
    if (g.CurrentWindow)
1193b4be Poul Sander 2025-02-03 21:28 8323
        SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 8324
}
1193b4be Poul Sander 2025-02-03 21:28 8325
1193b4be Poul Sander 2025-02-03 21:28 8326
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8327
{
1193b4be Poul Sander 2025-02-03 21:28 8328
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8329
    IM_ASSERT(window == window->RootWindow);
1193b4be Poul Sander 2025-02-03 21:28 8330
1193b4be Poul Sander 2025-02-03 21:28 8331
    const int cur_order = window->FocusOrder;
1193b4be Poul Sander 2025-02-03 21:28 8332
    IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
1193b4be Poul Sander 2025-02-03 21:28 8333
    if (g.WindowsFocusOrder.back() == window)
1193b4be Poul Sander 2025-02-03 21:28 8334
        return;
1193b4be Poul Sander 2025-02-03 21:28 8335
1193b4be Poul Sander 2025-02-03 21:28 8336
    const int new_order = g.WindowsFocusOrder.Size - 1;
1193b4be Poul Sander 2025-02-03 21:28 8337
    for (int n = cur_order; n < new_order; n++)
1193b4be Poul Sander 2025-02-03 21:28 8338
    {
1193b4be Poul Sander 2025-02-03 21:28 8339
        g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
1193b4be Poul Sander 2025-02-03 21:28 8340
        g.WindowsFocusOrder[n]->FocusOrder--;
1193b4be Poul Sander 2025-02-03 21:28 8341
        IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
1193b4be Poul Sander 2025-02-03 21:28 8342
    }
1193b4be Poul Sander 2025-02-03 21:28 8343
    g.WindowsFocusOrder[new_order] = window;
1193b4be Poul Sander 2025-02-03 21:28 8344
    window->FocusOrder = (short)new_order;
1193b4be Poul Sander 2025-02-03 21:28 8345
}
1193b4be Poul Sander 2025-02-03 21:28 8346
1193b4be Poul Sander 2025-02-03 21:28 8347
void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8348
{
1193b4be Poul Sander 2025-02-03 21:28 8349
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8350
    ImGuiWindow* current_front_window = g.Windows.back();
1193b4be Poul Sander 2025-02-03 21:28 8351
    if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)
1193b4be Poul Sander 2025-02-03 21:28 8352
        return;
1193b4be Poul Sander 2025-02-03 21:28 8353
    for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
1193b4be Poul Sander 2025-02-03 21:28 8354
        if (g.Windows[i] == window)
1193b4be Poul Sander 2025-02-03 21:28 8355
        {
1193b4be Poul Sander 2025-02-03 21:28 8356
            memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
1193b4be Poul Sander 2025-02-03 21:28 8357
            g.Windows[g.Windows.Size - 1] = window;
1193b4be Poul Sander 2025-02-03 21:28 8358
            break;
1193b4be Poul Sander 2025-02-03 21:28 8359
        }
1193b4be Poul Sander 2025-02-03 21:28 8360
}
1193b4be Poul Sander 2025-02-03 21:28 8361
1193b4be Poul Sander 2025-02-03 21:28 8362
void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8363
{
1193b4be Poul Sander 2025-02-03 21:28 8364
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8365
    if (g.Windows[0] == window)
1193b4be Poul Sander 2025-02-03 21:28 8366
        return;
1193b4be Poul Sander 2025-02-03 21:28 8367
    for (int i = 0; i < g.Windows.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 8368
        if (g.Windows[i] == window)
1193b4be Poul Sander 2025-02-03 21:28 8369
        {
1193b4be Poul Sander 2025-02-03 21:28 8370
            memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
1193b4be Poul Sander 2025-02-03 21:28 8371
            g.Windows[0] = window;
1193b4be Poul Sander 2025-02-03 21:28 8372
            break;
1193b4be Poul Sander 2025-02-03 21:28 8373
        }
1193b4be Poul Sander 2025-02-03 21:28 8374
}
1193b4be Poul Sander 2025-02-03 21:28 8375
1193b4be Poul Sander 2025-02-03 21:28 8376
void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
1193b4be Poul Sander 2025-02-03 21:28 8377
{
1193b4be Poul Sander 2025-02-03 21:28 8378
    IM_ASSERT(window != NULL && behind_window != NULL);
1193b4be Poul Sander 2025-02-03 21:28 8379
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8380
    window = window->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 8381
    behind_window = behind_window->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 8382
    int pos_wnd = FindWindowDisplayIndex(window);
1193b4be Poul Sander 2025-02-03 21:28 8383
    int pos_beh = FindWindowDisplayIndex(behind_window);
1193b4be Poul Sander 2025-02-03 21:28 8384
    if (pos_wnd < pos_beh)
1193b4be Poul Sander 2025-02-03 21:28 8385
    {
1193b4be Poul Sander 2025-02-03 21:28 8386
        size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
1193b4be Poul Sander 2025-02-03 21:28 8387
        memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
1193b4be Poul Sander 2025-02-03 21:28 8388
        g.Windows[pos_beh - 1] = window;
1193b4be Poul Sander 2025-02-03 21:28 8389
    }
1193b4be Poul Sander 2025-02-03 21:28 8390
    else
1193b4be Poul Sander 2025-02-03 21:28 8391
    {
1193b4be Poul Sander 2025-02-03 21:28 8392
        size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
1193b4be Poul Sander 2025-02-03 21:28 8393
        memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
1193b4be Poul Sander 2025-02-03 21:28 8394
        g.Windows[pos_beh] = window;
1193b4be Poul Sander 2025-02-03 21:28 8395
    }
1193b4be Poul Sander 2025-02-03 21:28 8396
}
1193b4be Poul Sander 2025-02-03 21:28 8397
1193b4be Poul Sander 2025-02-03 21:28 8398
int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8399
{
1193b4be Poul Sander 2025-02-03 21:28 8400
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8401
    return g.Windows.index_from_ptr(g.Windows.find(window));
1193b4be Poul Sander 2025-02-03 21:28 8402
}
1193b4be Poul Sander 2025-02-03 21:28 8403
1193b4be Poul Sander 2025-02-03 21:28 8404
// Moving window to front of display and set focus (which happens to be back of our sorted list)
1193b4be Poul Sander 2025-02-03 21:28 8405
void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 8406
{
1193b4be Poul Sander 2025-02-03 21:28 8407
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8408
1193b4be Poul Sander 2025-02-03 21:28 8409
    // Modal check?
1193b4be Poul Sander 2025-02-03 21:28 8410
    if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
1193b4be Poul Sander 2025-02-03 21:28 8411
        if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
1193b4be Poul Sander 2025-02-03 21:28 8412
        {
1193b4be Poul Sander 2025-02-03 21:28 8413
            // This block would typically be reached in two situations:
1193b4be Poul Sander 2025-02-03 21:28 8414
            // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
1193b4be Poul Sander 2025-02-03 21:28 8415
            // - User clicking on void or anything behind a modal while a modal is open (window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 8416
            IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
1193b4be Poul Sander 2025-02-03 21:28 8417
            if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8418
                BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
1193b4be Poul Sander 2025-02-03 21:28 8419
            ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
1193b4be Poul Sander 2025-02-03 21:28 8420
            return;
1193b4be Poul Sander 2025-02-03 21:28 8421
        }
1193b4be Poul Sander 2025-02-03 21:28 8422
1193b4be Poul Sander 2025-02-03 21:28 8423
    // Find last focused child (if any) and focus it instead.
1193b4be Poul Sander 2025-02-03 21:28 8424
    if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 8425
        window = NavRestoreLastChildNavWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 8426
1193b4be Poul Sander 2025-02-03 21:28 8427
    // Apply focus
1193b4be Poul Sander 2025-02-03 21:28 8428
    if (g.NavWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 8429
    {
1193b4be Poul Sander 2025-02-03 21:28 8430
        SetNavWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 8431
        if (window && g.NavHighlightItemUnderNav)
1193b4be Poul Sander 2025-02-03 21:28 8432
            g.NavMousePosDirty = true;
1193b4be Poul Sander 2025-02-03 21:28 8433
        g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
1193b4be Poul Sander 2025-02-03 21:28 8434
        g.NavLayer = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 8435
        SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
1193b4be Poul Sander 2025-02-03 21:28 8436
        g.NavIdIsAlive = false;
1193b4be Poul Sander 2025-02-03 21:28 8437
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
1193b4be Poul Sander 2025-02-03 21:28 8438
1193b4be Poul Sander 2025-02-03 21:28 8439
        // Close popups if any
1193b4be Poul Sander 2025-02-03 21:28 8440
        ClosePopupsOverWindow(window, false);
1193b4be Poul Sander 2025-02-03 21:28 8441
    }
1193b4be Poul Sander 2025-02-03 21:28 8442
1193b4be Poul Sander 2025-02-03 21:28 8443
    // Move the root window to the top of the pile
1193b4be Poul Sander 2025-02-03 21:28 8444
    IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);
1193b4be Poul Sander 2025-02-03 21:28 8445
    ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;
1193b4be Poul Sander 2025-02-03 21:28 8446
    ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;
1193b4be Poul Sander 2025-02-03 21:28 8447
    ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
1193b4be Poul Sander 2025-02-03 21:28 8448
    bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
1193b4be Poul Sander 2025-02-03 21:28 8449
1193b4be Poul Sander 2025-02-03 21:28 8450
    // Steal active widgets. Some of the cases it triggers includes:
1193b4be Poul Sander 2025-02-03 21:28 8451
    // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
1193b4be Poul Sander 2025-02-03 21:28 8452
    // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 8453
    // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
1193b4be Poul Sander 2025-02-03 21:28 8454
    if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
1193b4be Poul Sander 2025-02-03 21:28 8455
        if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
1193b4be Poul Sander 2025-02-03 21:28 8456
            ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 8457
1193b4be Poul Sander 2025-02-03 21:28 8458
    // Passing NULL allow to disable keyboard focus
1193b4be Poul Sander 2025-02-03 21:28 8459
    if (!window)
1193b4be Poul Sander 2025-02-03 21:28 8460
        return;
1193b4be Poul Sander 2025-02-03 21:28 8461
    window->LastFrameJustFocused = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 8462
1193b4be Poul Sander 2025-02-03 21:28 8463
    // Select in dock node
1193b4be Poul Sander 2025-02-03 21:28 8464
    // For #2304 we avoid applying focus immediately before the tabbar is visible.
1193b4be Poul Sander 2025-02-03 21:28 8465
    //if (dock_node && dock_node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 8466
    //    dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
1193b4be Poul Sander 2025-02-03 21:28 8467
1193b4be Poul Sander 2025-02-03 21:28 8468
    // Bring to front
1193b4be Poul Sander 2025-02-03 21:28 8469
    BringWindowToFocusFront(focus_front_window);
1193b4be Poul Sander 2025-02-03 21:28 8470
    if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8471
        BringWindowToDisplayFront(display_front_window);
1193b4be Poul Sander 2025-02-03 21:28 8472
}
1193b4be Poul Sander 2025-02-03 21:28 8473
1193b4be Poul Sander 2025-02-03 21:28 8474
void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 8475
{
1193b4be Poul Sander 2025-02-03 21:28 8476
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8477
    int start_idx = g.WindowsFocusOrder.Size - 1;
1193b4be Poul Sander 2025-02-03 21:28 8478
    if (under_this_window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 8479
    {
1193b4be Poul Sander 2025-02-03 21:28 8480
        // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
1193b4be Poul Sander 2025-02-03 21:28 8481
        int offset = -1;
1193b4be Poul Sander 2025-02-03 21:28 8482
        while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 8483
        {
1193b4be Poul Sander 2025-02-03 21:28 8484
            under_this_window = under_this_window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8485
            offset = 0;
1193b4be Poul Sander 2025-02-03 21:28 8486
        }
1193b4be Poul Sander 2025-02-03 21:28 8487
        start_idx = FindWindowFocusIndex(under_this_window) + offset;
1193b4be Poul Sander 2025-02-03 21:28 8488
    }
1193b4be Poul Sander 2025-02-03 21:28 8489
    for (int i = start_idx; i >= 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 8490
    {
1193b4be Poul Sander 2025-02-03 21:28 8491
        // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
1193b4be Poul Sander 2025-02-03 21:28 8492
        ImGuiWindow* window = g.WindowsFocusOrder[i];
1193b4be Poul Sander 2025-02-03 21:28 8493
        if (window == ignore_window || !window->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 8494
            continue;
1193b4be Poul Sander 2025-02-03 21:28 8495
        if (filter_viewport != NULL && window->Viewport != filter_viewport)
1193b4be Poul Sander 2025-02-03 21:28 8496
            continue;
1193b4be Poul Sander 2025-02-03 21:28 8497
        if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 8498
        {
1193b4be Poul Sander 2025-02-03 21:28 8499
            // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set...
1193b4be Poul Sander 2025-02-03 21:28 8500
            // This is failing (lagging by one frame) for docked windows.
1193b4be Poul Sander 2025-02-03 21:28 8501
            // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
1193b4be Poul Sander 2025-02-03 21:28 8502
            // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update)
1193b4be Poul Sander 2025-02-03 21:28 8503
            // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
1193b4be Poul Sander 2025-02-03 21:28 8504
            FocusWindow(window, flags);
1193b4be Poul Sander 2025-02-03 21:28 8505
            return;
1193b4be Poul Sander 2025-02-03 21:28 8506
        }
1193b4be Poul Sander 2025-02-03 21:28 8507
    }
1193b4be Poul Sander 2025-02-03 21:28 8508
    FocusWindow(NULL, flags);
1193b4be Poul Sander 2025-02-03 21:28 8509
}
1193b4be Poul Sander 2025-02-03 21:28 8510
1193b4be Poul Sander 2025-02-03 21:28 8511
// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
1193b4be Poul Sander 2025-02-03 21:28 8512
void ImGui::SetCurrentFont(ImFont* font)
1193b4be Poul Sander 2025-02-03 21:28 8513
{
1193b4be Poul Sander 2025-02-03 21:28 8514
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8515
    IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
1193b4be Poul Sander 2025-02-03 21:28 8516
    IM_ASSERT(font->Scale > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 8517
    g.Font = font;
1193b4be Poul Sander 2025-02-03 21:28 8518
    g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
1193b4be Poul Sander 2025-02-03 21:28 8519
    g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 8520
    g.FontScale = g.FontSize / g.Font->FontSize;
1193b4be Poul Sander 2025-02-03 21:28 8521
1193b4be Poul Sander 2025-02-03 21:28 8522
    ImFontAtlas* atlas = g.Font->ContainerAtlas;
1193b4be Poul Sander 2025-02-03 21:28 8523
    g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
1193b4be Poul Sander 2025-02-03 21:28 8524
    g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
1193b4be Poul Sander 2025-02-03 21:28 8525
    g.DrawListSharedData.Font = g.Font;
1193b4be Poul Sander 2025-02-03 21:28 8526
    g.DrawListSharedData.FontSize = g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 8527
    g.DrawListSharedData.FontScale = g.FontScale;
1193b4be Poul Sander 2025-02-03 21:28 8528
}
1193b4be Poul Sander 2025-02-03 21:28 8529
1193b4be Poul Sander 2025-02-03 21:28 8530
// Use ImDrawList::_SetTextureID(), making our shared g.FontStack[] authorative against window-local ImDrawList.
1193b4be Poul Sander 2025-02-03 21:28 8531
// - Whereas ImDrawList::PushTextureID()/PopTextureID() is not to be used across Begin() calls.
1193b4be Poul Sander 2025-02-03 21:28 8532
// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
1193b4be Poul Sander 2025-02-03 21:28 8533
//   - Some code paths never really fully worked with multiple atlas textures.
1193b4be Poul Sander 2025-02-03 21:28 8534
//   - The right-ish solution may be to remove _SetTextureID() and make AddText/RenderText lazily call PushTextureID()/PopTextureID()
1193b4be Poul Sander 2025-02-03 21:28 8535
//     the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
1193b4be Poul Sander 2025-02-03 21:28 8536
//     because we have a concrete need and a test bed for multiple atlas textures.
1193b4be Poul Sander 2025-02-03 21:28 8537
void ImGui::PushFont(ImFont* font)
1193b4be Poul Sander 2025-02-03 21:28 8538
{
1193b4be Poul Sander 2025-02-03 21:28 8539
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8540
    if (font == NULL)
1193b4be Poul Sander 2025-02-03 21:28 8541
        font = GetDefaultFont();
1193b4be Poul Sander 2025-02-03 21:28 8542
    g.FontStack.push_back(font);
1193b4be Poul Sander 2025-02-03 21:28 8543
    SetCurrentFont(font);
1193b4be Poul Sander 2025-02-03 21:28 8544
    g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID);
1193b4be Poul Sander 2025-02-03 21:28 8545
}
1193b4be Poul Sander 2025-02-03 21:28 8546
1193b4be Poul Sander 2025-02-03 21:28 8547
void  ImGui::PopFont()
1193b4be Poul Sander 2025-02-03 21:28 8548
{
1193b4be Poul Sander 2025-02-03 21:28 8549
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8550
    if (g.FontStack.Size <= 0)
1193b4be Poul Sander 2025-02-03 21:28 8551
    {
1193b4be Poul Sander 2025-02-03 21:28 8552
        IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 8553
        return;
1193b4be Poul Sander 2025-02-03 21:28 8554
    }
1193b4be Poul Sander 2025-02-03 21:28 8555
    g.FontStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8556
    ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8557
    SetCurrentFont(font);
1193b4be Poul Sander 2025-02-03 21:28 8558
    g.CurrentWindow->DrawList->_SetTextureID(font->ContainerAtlas->TexID);
1193b4be Poul Sander 2025-02-03 21:28 8559
}
1193b4be Poul Sander 2025-02-03 21:28 8560
1193b4be Poul Sander 2025-02-03 21:28 8561
void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
1193b4be Poul Sander 2025-02-03 21:28 8562
{
1193b4be Poul Sander 2025-02-03 21:28 8563
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8564
    ImGuiItemFlags item_flags = g.CurrentItemFlags;
1193b4be Poul Sander 2025-02-03 21:28 8565
    IM_ASSERT(item_flags == g.ItemFlagsStack.back());
1193b4be Poul Sander 2025-02-03 21:28 8566
    if (enabled)
1193b4be Poul Sander 2025-02-03 21:28 8567
        item_flags |= option;
1193b4be Poul Sander 2025-02-03 21:28 8568
    else
1193b4be Poul Sander 2025-02-03 21:28 8569
        item_flags &= ~option;
1193b4be Poul Sander 2025-02-03 21:28 8570
    g.CurrentItemFlags = item_flags;
1193b4be Poul Sander 2025-02-03 21:28 8571
    g.ItemFlagsStack.push_back(item_flags);
1193b4be Poul Sander 2025-02-03 21:28 8572
}
1193b4be Poul Sander 2025-02-03 21:28 8573
1193b4be Poul Sander 2025-02-03 21:28 8574
void ImGui::PopItemFlag()
1193b4be Poul Sander 2025-02-03 21:28 8575
{
1193b4be Poul Sander 2025-02-03 21:28 8576
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8577
    if (g.ItemFlagsStack.Size <= 1)
1193b4be Poul Sander 2025-02-03 21:28 8578
    {
1193b4be Poul Sander 2025-02-03 21:28 8579
        IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 8580
        return;
1193b4be Poul Sander 2025-02-03 21:28 8581
    }
1193b4be Poul Sander 2025-02-03 21:28 8582
    g.ItemFlagsStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8583
    g.CurrentItemFlags = g.ItemFlagsStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8584
}
1193b4be Poul Sander 2025-02-03 21:28 8585
1193b4be Poul Sander 2025-02-03 21:28 8586
// BeginDisabled()/EndDisabled()
1193b4be Poul Sander 2025-02-03 21:28 8587
// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
1193b4be Poul Sander 2025-02-03 21:28 8588
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
1193b4be Poul Sander 2025-02-03 21:28 8589
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
1193b4be Poul Sander 2025-02-03 21:28 8590
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
1193b4be Poul Sander 2025-02-03 21:28 8591
//   (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
1193b4be Poul Sander 2025-02-03 21:28 8592
// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
1193b4be Poul Sander 2025-02-03 21:28 8593
void ImGui::BeginDisabled(bool disabled)
1193b4be Poul Sander 2025-02-03 21:28 8594
{
1193b4be Poul Sander 2025-02-03 21:28 8595
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8596
    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
1193b4be Poul Sander 2025-02-03 21:28 8597
    if (!was_disabled && disabled)
1193b4be Poul Sander 2025-02-03 21:28 8598
    {
1193b4be Poul Sander 2025-02-03 21:28 8599
        g.DisabledAlphaBackup = g.Style.Alpha;
1193b4be Poul Sander 2025-02-03 21:28 8600
        g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
1193b4be Poul Sander 2025-02-03 21:28 8601
    }
1193b4be Poul Sander 2025-02-03 21:28 8602
    if (was_disabled || disabled)
1193b4be Poul Sander 2025-02-03 21:28 8603
        g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
1193b4be Poul Sander 2025-02-03 21:28 8604
    g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
1193b4be Poul Sander 2025-02-03 21:28 8605
    g.DisabledStackSize++;
1193b4be Poul Sander 2025-02-03 21:28 8606
}
1193b4be Poul Sander 2025-02-03 21:28 8607
1193b4be Poul Sander 2025-02-03 21:28 8608
void ImGui::EndDisabled()
1193b4be Poul Sander 2025-02-03 21:28 8609
{
1193b4be Poul Sander 2025-02-03 21:28 8610
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8611
    if (g.DisabledStackSize <= 0)
1193b4be Poul Sander 2025-02-03 21:28 8612
    {
1193b4be Poul Sander 2025-02-03 21:28 8613
        IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 8614
        return;
1193b4be Poul Sander 2025-02-03 21:28 8615
    }
1193b4be Poul Sander 2025-02-03 21:28 8616
    g.DisabledStackSize--;
1193b4be Poul Sander 2025-02-03 21:28 8617
    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
1193b4be Poul Sander 2025-02-03 21:28 8618
    //PopItemFlag();
1193b4be Poul Sander 2025-02-03 21:28 8619
    g.ItemFlagsStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8620
    g.CurrentItemFlags = g.ItemFlagsStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8621
    if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8622
        g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 8623
}
1193b4be Poul Sander 2025-02-03 21:28 8624
1193b4be Poul Sander 2025-02-03 21:28 8625
// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
1193b4be Poul Sander 2025-02-03 21:28 8626
// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
1193b4be Poul Sander 2025-02-03 21:28 8627
// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
1193b4be Poul Sander 2025-02-03 21:28 8628
void ImGui::BeginDisabledOverrideReenable()
1193b4be Poul Sander 2025-02-03 21:28 8629
{
1193b4be Poul Sander 2025-02-03 21:28 8630
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8631
    IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
1193b4be Poul Sander 2025-02-03 21:28 8632
    g.Style.Alpha = g.DisabledAlphaBackup;
1193b4be Poul Sander 2025-02-03 21:28 8633
    g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
1193b4be Poul Sander 2025-02-03 21:28 8634
    g.ItemFlagsStack.push_back(g.CurrentItemFlags);
1193b4be Poul Sander 2025-02-03 21:28 8635
    g.DisabledStackSize++;
1193b4be Poul Sander 2025-02-03 21:28 8636
}
1193b4be Poul Sander 2025-02-03 21:28 8637
1193b4be Poul Sander 2025-02-03 21:28 8638
void ImGui::EndDisabledOverrideReenable()
1193b4be Poul Sander 2025-02-03 21:28 8639
{
1193b4be Poul Sander 2025-02-03 21:28 8640
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8641
    g.DisabledStackSize--;
1193b4be Poul Sander 2025-02-03 21:28 8642
    IM_ASSERT(g.DisabledStackSize > 0);
1193b4be Poul Sander 2025-02-03 21:28 8643
    g.ItemFlagsStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8644
    g.CurrentItemFlags = g.ItemFlagsStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8645
    g.Style.Alpha = g.DisabledAlphaBackup * g.Style.DisabledAlpha;
1193b4be Poul Sander 2025-02-03 21:28 8646
}
1193b4be Poul Sander 2025-02-03 21:28 8647
1193b4be Poul Sander 2025-02-03 21:28 8648
void ImGui::PushTextWrapPos(float wrap_pos_x)
1193b4be Poul Sander 2025-02-03 21:28 8649
{
1193b4be Poul Sander 2025-02-03 21:28 8650
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8651
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8652
    window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
1193b4be Poul Sander 2025-02-03 21:28 8653
    window->DC.TextWrapPos = wrap_pos_x;
1193b4be Poul Sander 2025-02-03 21:28 8654
}
1193b4be Poul Sander 2025-02-03 21:28 8655
1193b4be Poul Sander 2025-02-03 21:28 8656
void ImGui::PopTextWrapPos()
1193b4be Poul Sander 2025-02-03 21:28 8657
{
1193b4be Poul Sander 2025-02-03 21:28 8658
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8659
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8660
    if (window->DC.TextWrapPosStack.Size <= 0)
1193b4be Poul Sander 2025-02-03 21:28 8661
    {
1193b4be Poul Sander 2025-02-03 21:28 8662
        IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 8663
        return;
1193b4be Poul Sander 2025-02-03 21:28 8664
    }
1193b4be Poul Sander 2025-02-03 21:28 8665
    window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
1193b4be Poul Sander 2025-02-03 21:28 8666
    window->DC.TextWrapPosStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 8667
}
1193b4be Poul Sander 2025-02-03 21:28 8668
1193b4be Poul Sander 2025-02-03 21:28 8669
static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
1193b4be Poul Sander 2025-02-03 21:28 8670
{
1193b4be Poul Sander 2025-02-03 21:28 8671
    ImGuiWindow* last_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 8672
    while (last_window != window)
1193b4be Poul Sander 2025-02-03 21:28 8673
    {
1193b4be Poul Sander 2025-02-03 21:28 8674
        last_window = window;
1193b4be Poul Sander 2025-02-03 21:28 8675
        window = window->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 8676
        if (popup_hierarchy)
1193b4be Poul Sander 2025-02-03 21:28 8677
            window = window->RootWindowPopupTree;
1193b4be Poul Sander 2025-02-03 21:28 8678
		if (dock_hierarchy)
1193b4be Poul Sander 2025-02-03 21:28 8679
			window = window->RootWindowDockTree;
1193b4be Poul Sander 2025-02-03 21:28 8680
	}
1193b4be Poul Sander 2025-02-03 21:28 8681
    return window;
1193b4be Poul Sander 2025-02-03 21:28 8682
}
1193b4be Poul Sander 2025-02-03 21:28 8683
1193b4be Poul Sander 2025-02-03 21:28 8684
bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
1193b4be Poul Sander 2025-02-03 21:28 8685
{
1193b4be Poul Sander 2025-02-03 21:28 8686
    ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
1193b4be Poul Sander 2025-02-03 21:28 8687
    if (window_root == potential_parent)
1193b4be Poul Sander 2025-02-03 21:28 8688
        return true;
1193b4be Poul Sander 2025-02-03 21:28 8689
    while (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 8690
    {
1193b4be Poul Sander 2025-02-03 21:28 8691
        if (window == potential_parent)
1193b4be Poul Sander 2025-02-03 21:28 8692
            return true;
1193b4be Poul Sander 2025-02-03 21:28 8693
        if (window == window_root) // end of chain
1193b4be Poul Sander 2025-02-03 21:28 8694
            return false;
1193b4be Poul Sander 2025-02-03 21:28 8695
        window = window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8696
    }
1193b4be Poul Sander 2025-02-03 21:28 8697
    return false;
1193b4be Poul Sander 2025-02-03 21:28 8698
}
1193b4be Poul Sander 2025-02-03 21:28 8699
1193b4be Poul Sander 2025-02-03 21:28 8700
bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
1193b4be Poul Sander 2025-02-03 21:28 8701
{
1193b4be Poul Sander 2025-02-03 21:28 8702
    if (window->RootWindow == potential_parent)
1193b4be Poul Sander 2025-02-03 21:28 8703
        return true;
1193b4be Poul Sander 2025-02-03 21:28 8704
    while (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 8705
    {
1193b4be Poul Sander 2025-02-03 21:28 8706
        if (window == potential_parent)
1193b4be Poul Sander 2025-02-03 21:28 8707
            return true;
1193b4be Poul Sander 2025-02-03 21:28 8708
        window = window->ParentWindowInBeginStack;
1193b4be Poul Sander 2025-02-03 21:28 8709
    }
1193b4be Poul Sander 2025-02-03 21:28 8710
    return false;
1193b4be Poul Sander 2025-02-03 21:28 8711
}
1193b4be Poul Sander 2025-02-03 21:28 8712
1193b4be Poul Sander 2025-02-03 21:28 8713
bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
1193b4be Poul Sander 2025-02-03 21:28 8714
{
1193b4be Poul Sander 2025-02-03 21:28 8715
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8716
1193b4be Poul Sander 2025-02-03 21:28 8717
    // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array
1193b4be Poul Sander 2025-02-03 21:28 8718
    const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
1193b4be Poul Sander 2025-02-03 21:28 8719
    if (display_layer_delta != 0)
1193b4be Poul Sander 2025-02-03 21:28 8720
        return display_layer_delta > 0;
1193b4be Poul Sander 2025-02-03 21:28 8721
1193b4be Poul Sander 2025-02-03 21:28 8722
    for (int i = g.Windows.Size - 1; i >= 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 8723
    {
1193b4be Poul Sander 2025-02-03 21:28 8724
        ImGuiWindow* candidate_window = g.Windows[i];
1193b4be Poul Sander 2025-02-03 21:28 8725
        if (candidate_window == potential_above)
1193b4be Poul Sander 2025-02-03 21:28 8726
            return true;
1193b4be Poul Sander 2025-02-03 21:28 8727
        if (candidate_window == potential_below)
1193b4be Poul Sander 2025-02-03 21:28 8728
            return false;
1193b4be Poul Sander 2025-02-03 21:28 8729
    }
1193b4be Poul Sander 2025-02-03 21:28 8730
    return false;
1193b4be Poul Sander 2025-02-03 21:28 8731
}
1193b4be Poul Sander 2025-02-03 21:28 8732
1193b4be Poul Sander 2025-02-03 21:28 8733
// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
1193b4be Poul Sander 2025-02-03 21:28 8734
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
1193b4be Poul Sander 2025-02-03 21:28 8735
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
1193b4be Poul Sander 2025-02-03 21:28 8736
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
1193b4be Poul Sander 2025-02-03 21:28 8737
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 8738
{
1193b4be Poul Sander 2025-02-03 21:28 8739
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8740
    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
1193b4be Poul Sander 2025-02-03 21:28 8741
1193b4be Poul Sander 2025-02-03 21:28 8742
    ImGuiWindow* ref_window = g.HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 8743
    ImGuiWindow* cur_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8744
    if (ref_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 8745
        return false;
1193b4be Poul Sander 2025-02-03 21:28 8746
1193b4be Poul Sander 2025-02-03 21:28 8747
    if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8748
    {
1193b4be Poul Sander 2025-02-03 21:28 8749
        IM_ASSERT(cur_window); // Not inside a Begin()/End()
1193b4be Poul Sander 2025-02-03 21:28 8750
        const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
1193b4be Poul Sander 2025-02-03 21:28 8751
        const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
1193b4be Poul Sander 2025-02-03 21:28 8752
        if (flags & ImGuiHoveredFlags_RootWindow)
1193b4be Poul Sander 2025-02-03 21:28 8753
            cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
1193b4be Poul Sander 2025-02-03 21:28 8754
1193b4be Poul Sander 2025-02-03 21:28 8755
        bool result;
1193b4be Poul Sander 2025-02-03 21:28 8756
        if (flags & ImGuiHoveredFlags_ChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 8757
            result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
1193b4be Poul Sander 2025-02-03 21:28 8758
        else
1193b4be Poul Sander 2025-02-03 21:28 8759
            result = (ref_window == cur_window);
1193b4be Poul Sander 2025-02-03 21:28 8760
        if (!result)
1193b4be Poul Sander 2025-02-03 21:28 8761
            return false;
1193b4be Poul Sander 2025-02-03 21:28 8762
    }
1193b4be Poul Sander 2025-02-03 21:28 8763
1193b4be Poul Sander 2025-02-03 21:28 8764
    if (!IsWindowContentHoverable(ref_window, flags))
1193b4be Poul Sander 2025-02-03 21:28 8765
        return false;
1193b4be Poul Sander 2025-02-03 21:28 8766
    if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
1193b4be Poul Sander 2025-02-03 21:28 8767
        if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
1193b4be Poul Sander 2025-02-03 21:28 8768
            return false;
1193b4be Poul Sander 2025-02-03 21:28 8769
1193b4be Poul Sander 2025-02-03 21:28 8770
    // When changing hovered window we requires a bit of stationary delay before activating hover timer.
1193b4be Poul Sander 2025-02-03 21:28 8771
    // FIXME: We don't support delay other than stationary one for now, other delay would need a way
1193b4be Poul Sander 2025-02-03 21:28 8772
    // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
1193b4be Poul Sander 2025-02-03 21:28 8773
    // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
1193b4be Poul Sander 2025-02-03 21:28 8774
    // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
1193b4be Poul Sander 2025-02-03 21:28 8775
    if (flags & ImGuiHoveredFlags_ForTooltip)
1193b4be Poul Sander 2025-02-03 21:28 8776
        flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
1193b4be Poul Sander 2025-02-03 21:28 8777
    if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
1193b4be Poul Sander 2025-02-03 21:28 8778
        return false;
1193b4be Poul Sander 2025-02-03 21:28 8779
1193b4be Poul Sander 2025-02-03 21:28 8780
    return true;
1193b4be Poul Sander 2025-02-03 21:28 8781
}
1193b4be Poul Sander 2025-02-03 21:28 8782
1193b4be Poul Sander 2025-02-03 21:28 8783
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 8784
{
1193b4be Poul Sander 2025-02-03 21:28 8785
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8786
    ImGuiWindow* ref_window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 8787
    ImGuiWindow* cur_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8788
1193b4be Poul Sander 2025-02-03 21:28 8789
    if (ref_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 8790
        return false;
1193b4be Poul Sander 2025-02-03 21:28 8791
    if (flags & ImGuiFocusedFlags_AnyWindow)
1193b4be Poul Sander 2025-02-03 21:28 8792
        return true;
1193b4be Poul Sander 2025-02-03 21:28 8793
1193b4be Poul Sander 2025-02-03 21:28 8794
    IM_ASSERT(cur_window); // Not inside a Begin()/End()
1193b4be Poul Sander 2025-02-03 21:28 8795
    const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
1193b4be Poul Sander 2025-02-03 21:28 8796
    const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
1193b4be Poul Sander 2025-02-03 21:28 8797
    if (flags & ImGuiHoveredFlags_RootWindow)
1193b4be Poul Sander 2025-02-03 21:28 8798
        cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
1193b4be Poul Sander 2025-02-03 21:28 8799
1193b4be Poul Sander 2025-02-03 21:28 8800
    if (flags & ImGuiHoveredFlags_ChildWindows)
1193b4be Poul Sander 2025-02-03 21:28 8801
        return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
1193b4be Poul Sander 2025-02-03 21:28 8802
    else
1193b4be Poul Sander 2025-02-03 21:28 8803
        return (ref_window == cur_window);
1193b4be Poul Sander 2025-02-03 21:28 8804
}
1193b4be Poul Sander 2025-02-03 21:28 8805
1193b4be Poul Sander 2025-02-03 21:28 8806
ImGuiID ImGui::GetWindowDockID()
1193b4be Poul Sander 2025-02-03 21:28 8807
{
1193b4be Poul Sander 2025-02-03 21:28 8808
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8809
    return g.CurrentWindow->DockId;
1193b4be Poul Sander 2025-02-03 21:28 8810
}
1193b4be Poul Sander 2025-02-03 21:28 8811
1193b4be Poul Sander 2025-02-03 21:28 8812
bool ImGui::IsWindowDocked()
1193b4be Poul Sander 2025-02-03 21:28 8813
{
1193b4be Poul Sander 2025-02-03 21:28 8814
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8815
    return g.CurrentWindow->DockIsActive;
1193b4be Poul Sander 2025-02-03 21:28 8816
}
1193b4be Poul Sander 2025-02-03 21:28 8817
1193b4be Poul Sander 2025-02-03 21:28 8818
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
1193b4be Poul Sander 2025-02-03 21:28 8819
// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
1193b4be Poul Sander 2025-02-03 21:28 8820
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
1193b4be Poul Sander 2025-02-03 21:28 8821
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8822
{
1193b4be Poul Sander 2025-02-03 21:28 8823
    return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
1193b4be Poul Sander 2025-02-03 21:28 8824
}
1193b4be Poul Sander 2025-02-03 21:28 8825
1193b4be Poul Sander 2025-02-03 21:28 8826
float ImGui::GetWindowWidth()
1193b4be Poul Sander 2025-02-03 21:28 8827
{
1193b4be Poul Sander 2025-02-03 21:28 8828
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8829
    return window->Size.x;
1193b4be Poul Sander 2025-02-03 21:28 8830
}
1193b4be Poul Sander 2025-02-03 21:28 8831
1193b4be Poul Sander 2025-02-03 21:28 8832
float ImGui::GetWindowHeight()
1193b4be Poul Sander 2025-02-03 21:28 8833
{
1193b4be Poul Sander 2025-02-03 21:28 8834
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8835
    return window->Size.y;
1193b4be Poul Sander 2025-02-03 21:28 8836
}
1193b4be Poul Sander 2025-02-03 21:28 8837
1193b4be Poul Sander 2025-02-03 21:28 8838
ImVec2 ImGui::GetWindowPos()
1193b4be Poul Sander 2025-02-03 21:28 8839
{
1193b4be Poul Sander 2025-02-03 21:28 8840
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8841
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 8842
    return window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 8843
}
1193b4be Poul Sander 2025-02-03 21:28 8844
1193b4be Poul Sander 2025-02-03 21:28 8845
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8846
{
1193b4be Poul Sander 2025-02-03 21:28 8847
    // Test condition (NB: bit 0 is always true) and clear flags for next time
1193b4be Poul Sander 2025-02-03 21:28 8848
    if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8849
        return;
1193b4be Poul Sander 2025-02-03 21:28 8850
1193b4be Poul Sander 2025-02-03 21:28 8851
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
1193b4be Poul Sander 2025-02-03 21:28 8852
    window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
1193b4be Poul Sander 2025-02-03 21:28 8853
    window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 8854
1193b4be Poul Sander 2025-02-03 21:28 8855
    // Set
1193b4be Poul Sander 2025-02-03 21:28 8856
    const ImVec2 old_pos = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 8857
    window->Pos = ImTrunc(pos);
1193b4be Poul Sander 2025-02-03 21:28 8858
    ImVec2 offset = window->Pos - old_pos;
1193b4be Poul Sander 2025-02-03 21:28 8859
    if (offset.x == 0.0f && offset.y == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 8860
        return;
1193b4be Poul Sander 2025-02-03 21:28 8861
    MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 8862
    // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.
1193b4be Poul Sander 2025-02-03 21:28 8863
    window->DC.CursorPos += offset;         // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
1193b4be Poul Sander 2025-02-03 21:28 8864
    window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
1193b4be Poul Sander 2025-02-03 21:28 8865
    window->DC.IdealMaxPos += offset;
1193b4be Poul Sander 2025-02-03 21:28 8866
    window->DC.CursorStartPos += offset;
1193b4be Poul Sander 2025-02-03 21:28 8867
}
1193b4be Poul Sander 2025-02-03 21:28 8868
1193b4be Poul Sander 2025-02-03 21:28 8869
void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8870
{
1193b4be Poul Sander 2025-02-03 21:28 8871
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 8872
    SetWindowPos(window, pos, cond);
1193b4be Poul Sander 2025-02-03 21:28 8873
}
1193b4be Poul Sander 2025-02-03 21:28 8874
1193b4be Poul Sander 2025-02-03 21:28 8875
void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8876
{
1193b4be Poul Sander 2025-02-03 21:28 8877
    if (ImGuiWindow* window = FindWindowByName(name))
1193b4be Poul Sander 2025-02-03 21:28 8878
        SetWindowPos(window, pos, cond);
1193b4be Poul Sander 2025-02-03 21:28 8879
}
1193b4be Poul Sander 2025-02-03 21:28 8880
1193b4be Poul Sander 2025-02-03 21:28 8881
ImVec2 ImGui::GetWindowSize()
1193b4be Poul Sander 2025-02-03 21:28 8882
{
1193b4be Poul Sander 2025-02-03 21:28 8883
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 8884
    return window->Size;
1193b4be Poul Sander 2025-02-03 21:28 8885
}
1193b4be Poul Sander 2025-02-03 21:28 8886
1193b4be Poul Sander 2025-02-03 21:28 8887
void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8888
{
1193b4be Poul Sander 2025-02-03 21:28 8889
    // Test condition (NB: bit 0 is always true) and clear flags for next time
1193b4be Poul Sander 2025-02-03 21:28 8890
    if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8891
        return;
1193b4be Poul Sander 2025-02-03 21:28 8892
1193b4be Poul Sander 2025-02-03 21:28 8893
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
1193b4be Poul Sander 2025-02-03 21:28 8894
    window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
1193b4be Poul Sander 2025-02-03 21:28 8895
1193b4be Poul Sander 2025-02-03 21:28 8896
    // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
1193b4be Poul Sander 2025-02-03 21:28 8897
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
1193b4be Poul Sander 2025-02-03 21:28 8898
        window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
1193b4be Poul Sander 2025-02-03 21:28 8899
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
1193b4be Poul Sander 2025-02-03 21:28 8900
        window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
1193b4be Poul Sander 2025-02-03 21:28 8901
1193b4be Poul Sander 2025-02-03 21:28 8902
    // Set
1193b4be Poul Sander 2025-02-03 21:28 8903
    ImVec2 old_size = window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 8904
    if (size.x <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 8905
        window->AutoFitOnlyGrows = false;
1193b4be Poul Sander 2025-02-03 21:28 8906
    else
1193b4be Poul Sander 2025-02-03 21:28 8907
        window->SizeFull.x = IM_TRUNC(size.x);
1193b4be Poul Sander 2025-02-03 21:28 8908
    if (size.y <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 8909
        window->AutoFitOnlyGrows = false;
1193b4be Poul Sander 2025-02-03 21:28 8910
    else
1193b4be Poul Sander 2025-02-03 21:28 8911
        window->SizeFull.y = IM_TRUNC(size.y);
1193b4be Poul Sander 2025-02-03 21:28 8912
    if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
1193b4be Poul Sander 2025-02-03 21:28 8913
        MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 8914
}
1193b4be Poul Sander 2025-02-03 21:28 8915
1193b4be Poul Sander 2025-02-03 21:28 8916
void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8917
{
1193b4be Poul Sander 2025-02-03 21:28 8918
    SetWindowSize(GImGui->CurrentWindow, size, cond);
1193b4be Poul Sander 2025-02-03 21:28 8919
}
1193b4be Poul Sander 2025-02-03 21:28 8920
1193b4be Poul Sander 2025-02-03 21:28 8921
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8922
{
1193b4be Poul Sander 2025-02-03 21:28 8923
    if (ImGuiWindow* window = FindWindowByName(name))
1193b4be Poul Sander 2025-02-03 21:28 8924
        SetWindowSize(window, size, cond);
1193b4be Poul Sander 2025-02-03 21:28 8925
}
1193b4be Poul Sander 2025-02-03 21:28 8926
1193b4be Poul Sander 2025-02-03 21:28 8927
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8928
{
1193b4be Poul Sander 2025-02-03 21:28 8929
    // Test condition (NB: bit 0 is always true) and clear flags for next time
1193b4be Poul Sander 2025-02-03 21:28 8930
    if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
1193b4be Poul Sander 2025-02-03 21:28 8931
        return;
1193b4be Poul Sander 2025-02-03 21:28 8932
    window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
1193b4be Poul Sander 2025-02-03 21:28 8933
1193b4be Poul Sander 2025-02-03 21:28 8934
    // Set
1193b4be Poul Sander 2025-02-03 21:28 8935
    window->Collapsed = collapsed;
1193b4be Poul Sander 2025-02-03 21:28 8936
}
1193b4be Poul Sander 2025-02-03 21:28 8937
1193b4be Poul Sander 2025-02-03 21:28 8938
void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
1193b4be Poul Sander 2025-02-03 21:28 8939
{
1193b4be Poul Sander 2025-02-03 21:28 8940
    IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
1193b4be Poul Sander 2025-02-03 21:28 8941
    window->HitTestHoleSize = ImVec2ih(size);
1193b4be Poul Sander 2025-02-03 21:28 8942
    window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
1193b4be Poul Sander 2025-02-03 21:28 8943
}
1193b4be Poul Sander 2025-02-03 21:28 8944
1193b4be Poul Sander 2025-02-03 21:28 8945
void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 8946
{
1193b4be Poul Sander 2025-02-03 21:28 8947
    window->Hidden = window->SkipItems = true;
1193b4be Poul Sander 2025-02-03 21:28 8948
    window->HiddenFramesCanSkipItems = 1;
1193b4be Poul Sander 2025-02-03 21:28 8949
}
1193b4be Poul Sander 2025-02-03 21:28 8950
1193b4be Poul Sander 2025-02-03 21:28 8951
void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8952
{
1193b4be Poul Sander 2025-02-03 21:28 8953
    SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
1193b4be Poul Sander 2025-02-03 21:28 8954
}
1193b4be Poul Sander 2025-02-03 21:28 8955
1193b4be Poul Sander 2025-02-03 21:28 8956
bool ImGui::IsWindowCollapsed()
1193b4be Poul Sander 2025-02-03 21:28 8957
{
1193b4be Poul Sander 2025-02-03 21:28 8958
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 8959
    return window->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 8960
}
1193b4be Poul Sander 2025-02-03 21:28 8961
1193b4be Poul Sander 2025-02-03 21:28 8962
bool ImGui::IsWindowAppearing()
1193b4be Poul Sander 2025-02-03 21:28 8963
{
1193b4be Poul Sander 2025-02-03 21:28 8964
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 8965
    return window->Appearing;
1193b4be Poul Sander 2025-02-03 21:28 8966
}
1193b4be Poul Sander 2025-02-03 21:28 8967
1193b4be Poul Sander 2025-02-03 21:28 8968
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 8969
{
1193b4be Poul Sander 2025-02-03 21:28 8970
    if (ImGuiWindow* window = FindWindowByName(name))
1193b4be Poul Sander 2025-02-03 21:28 8971
        SetWindowCollapsed(window, collapsed, cond);
1193b4be Poul Sander 2025-02-03 21:28 8972
}
1193b4be Poul Sander 2025-02-03 21:28 8973
1193b4be Poul Sander 2025-02-03 21:28 8974
void ImGui::SetWindowFocus()
1193b4be Poul Sander 2025-02-03 21:28 8975
{
1193b4be Poul Sander 2025-02-03 21:28 8976
    FocusWindow(GImGui->CurrentWindow);
1193b4be Poul Sander 2025-02-03 21:28 8977
}
1193b4be Poul Sander 2025-02-03 21:28 8978
1193b4be Poul Sander 2025-02-03 21:28 8979
void ImGui::SetWindowFocus(const char* name)
1193b4be Poul Sander 2025-02-03 21:28 8980
{
1193b4be Poul Sander 2025-02-03 21:28 8981
    if (name)
1193b4be Poul Sander 2025-02-03 21:28 8982
    {
1193b4be Poul Sander 2025-02-03 21:28 8983
        if (ImGuiWindow* window = FindWindowByName(name))
1193b4be Poul Sander 2025-02-03 21:28 8984
            FocusWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 8985
    }
1193b4be Poul Sander 2025-02-03 21:28 8986
    else
1193b4be Poul Sander 2025-02-03 21:28 8987
    {
1193b4be Poul Sander 2025-02-03 21:28 8988
        FocusWindow(NULL);
1193b4be Poul Sander 2025-02-03 21:28 8989
    }
1193b4be Poul Sander 2025-02-03 21:28 8990
}
1193b4be Poul Sander 2025-02-03 21:28 8991
1193b4be Poul Sander 2025-02-03 21:28 8992
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
1193b4be Poul Sander 2025-02-03 21:28 8993
{
1193b4be Poul Sander 2025-02-03 21:28 8994
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 8995
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
1193b4be Poul Sander 2025-02-03 21:28 8996
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
1193b4be Poul Sander 2025-02-03 21:28 8997
    g.NextWindowData.PosVal = pos;
1193b4be Poul Sander 2025-02-03 21:28 8998
    g.NextWindowData.PosPivotVal = pivot;
1193b4be Poul Sander 2025-02-03 21:28 8999
    g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
1193b4be Poul Sander 2025-02-03 21:28 9000
    g.NextWindowData.PosUndock = true;
1193b4be Poul Sander 2025-02-03 21:28 9001
}
1193b4be Poul Sander 2025-02-03 21:28 9002
1193b4be Poul Sander 2025-02-03 21:28 9003
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 9004
{
1193b4be Poul Sander 2025-02-03 21:28 9005
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9006
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
1193b4be Poul Sander 2025-02-03 21:28 9007
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
1193b4be Poul Sander 2025-02-03 21:28 9008
    g.NextWindowData.SizeVal = size;
1193b4be Poul Sander 2025-02-03 21:28 9009
    g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
1193b4be Poul Sander 2025-02-03 21:28 9010
}
1193b4be Poul Sander 2025-02-03 21:28 9011
1193b4be Poul Sander 2025-02-03 21:28 9012
// For each axis:
1193b4be Poul Sander 2025-02-03 21:28 9013
// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
1193b4be Poul Sander 2025-02-03 21:28 9014
// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
1193b4be Poul Sander 2025-02-03 21:28 9015
// - See "Demo->Examples->Constrained-resizing window" for examples.
1193b4be Poul Sander 2025-02-03 21:28 9016
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
1193b4be Poul Sander 2025-02-03 21:28 9017
{
1193b4be Poul Sander 2025-02-03 21:28 9018
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9019
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
1193b4be Poul Sander 2025-02-03 21:28 9020
    g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
1193b4be Poul Sander 2025-02-03 21:28 9021
    g.NextWindowData.SizeCallback = custom_callback;
1193b4be Poul Sander 2025-02-03 21:28 9022
    g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
1193b4be Poul Sander 2025-02-03 21:28 9023
}
1193b4be Poul Sander 2025-02-03 21:28 9024
1193b4be Poul Sander 2025-02-03 21:28 9025
// Content size = inner scrollable rectangle, padded with WindowPadding.
1193b4be Poul Sander 2025-02-03 21:28 9026
// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
1193b4be Poul Sander 2025-02-03 21:28 9027
void ImGui::SetNextWindowContentSize(const ImVec2& size)
1193b4be Poul Sander 2025-02-03 21:28 9028
{
1193b4be Poul Sander 2025-02-03 21:28 9029
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9030
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
1193b4be Poul Sander 2025-02-03 21:28 9031
    g.NextWindowData.ContentSizeVal = ImTrunc(size);
1193b4be Poul Sander 2025-02-03 21:28 9032
}
1193b4be Poul Sander 2025-02-03 21:28 9033
1193b4be Poul Sander 2025-02-03 21:28 9034
void ImGui::SetNextWindowScroll(const ImVec2& scroll)
1193b4be Poul Sander 2025-02-03 21:28 9035
{
1193b4be Poul Sander 2025-02-03 21:28 9036
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9037
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
1193b4be Poul Sander 2025-02-03 21:28 9038
    g.NextWindowData.ScrollVal = scroll;
1193b4be Poul Sander 2025-02-03 21:28 9039
}
1193b4be Poul Sander 2025-02-03 21:28 9040
1193b4be Poul Sander 2025-02-03 21:28 9041
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 9042
{
1193b4be Poul Sander 2025-02-03 21:28 9043
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9044
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
1193b4be Poul Sander 2025-02-03 21:28 9045
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
1193b4be Poul Sander 2025-02-03 21:28 9046
    g.NextWindowData.CollapsedVal = collapsed;
1193b4be Poul Sander 2025-02-03 21:28 9047
    g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
1193b4be Poul Sander 2025-02-03 21:28 9048
}
1193b4be Poul Sander 2025-02-03 21:28 9049
1193b4be Poul Sander 2025-02-03 21:28 9050
void ImGui::SetNextWindowFocus()
1193b4be Poul Sander 2025-02-03 21:28 9051
{
1193b4be Poul Sander 2025-02-03 21:28 9052
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9053
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
1193b4be Poul Sander 2025-02-03 21:28 9054
}
1193b4be Poul Sander 2025-02-03 21:28 9055
1193b4be Poul Sander 2025-02-03 21:28 9056
void ImGui::SetNextWindowBgAlpha(float alpha)
1193b4be Poul Sander 2025-02-03 21:28 9057
{
1193b4be Poul Sander 2025-02-03 21:28 9058
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9059
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
1193b4be Poul Sander 2025-02-03 21:28 9060
    g.NextWindowData.BgAlphaVal = alpha;
1193b4be Poul Sander 2025-02-03 21:28 9061
}
1193b4be Poul Sander 2025-02-03 21:28 9062
1193b4be Poul Sander 2025-02-03 21:28 9063
void ImGui::SetNextWindowViewport(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 9064
{
1193b4be Poul Sander 2025-02-03 21:28 9065
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9066
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
1193b4be Poul Sander 2025-02-03 21:28 9067
    g.NextWindowData.ViewportId = id;
1193b4be Poul Sander 2025-02-03 21:28 9068
}
1193b4be Poul Sander 2025-02-03 21:28 9069
1193b4be Poul Sander 2025-02-03 21:28 9070
void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 9071
{
1193b4be Poul Sander 2025-02-03 21:28 9072
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9073
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
1193b4be Poul Sander 2025-02-03 21:28 9074
    g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
1193b4be Poul Sander 2025-02-03 21:28 9075
    g.NextWindowData.DockId = id;
1193b4be Poul Sander 2025-02-03 21:28 9076
}
1193b4be Poul Sander 2025-02-03 21:28 9077
1193b4be Poul Sander 2025-02-03 21:28 9078
void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
1193b4be Poul Sander 2025-02-03 21:28 9079
{
1193b4be Poul Sander 2025-02-03 21:28 9080
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9081
    IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
1193b4be Poul Sander 2025-02-03 21:28 9082
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
1193b4be Poul Sander 2025-02-03 21:28 9083
    g.NextWindowData.WindowClass = *window_class;
1193b4be Poul Sander 2025-02-03 21:28 9084
}
1193b4be Poul Sander 2025-02-03 21:28 9085
1193b4be Poul Sander 2025-02-03 21:28 9086
// This is experimental and meant to be a toy for exploring a future/wider range of features.
1193b4be Poul Sander 2025-02-03 21:28 9087
void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 9088
{
1193b4be Poul Sander 2025-02-03 21:28 9089
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9090
    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
1193b4be Poul Sander 2025-02-03 21:28 9091
    g.NextWindowData.RefreshFlagsVal = flags;
1193b4be Poul Sander 2025-02-03 21:28 9092
}
1193b4be Poul Sander 2025-02-03 21:28 9093
1193b4be Poul Sander 2025-02-03 21:28 9094
ImDrawList* ImGui::GetWindowDrawList()
1193b4be Poul Sander 2025-02-03 21:28 9095
{
1193b4be Poul Sander 2025-02-03 21:28 9096
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 9097
    return window->DrawList;
1193b4be Poul Sander 2025-02-03 21:28 9098
}
1193b4be Poul Sander 2025-02-03 21:28 9099
1193b4be Poul Sander 2025-02-03 21:28 9100
float ImGui::GetWindowDpiScale()
1193b4be Poul Sander 2025-02-03 21:28 9101
{
1193b4be Poul Sander 2025-02-03 21:28 9102
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9103
    return g.CurrentDpiScale;
1193b4be Poul Sander 2025-02-03 21:28 9104
}
1193b4be Poul Sander 2025-02-03 21:28 9105
1193b4be Poul Sander 2025-02-03 21:28 9106
ImGuiViewport* ImGui::GetWindowViewport()
1193b4be Poul Sander 2025-02-03 21:28 9107
{
1193b4be Poul Sander 2025-02-03 21:28 9108
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9109
    IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 9110
    return g.CurrentViewport;
1193b4be Poul Sander 2025-02-03 21:28 9111
}
1193b4be Poul Sander 2025-02-03 21:28 9112
1193b4be Poul Sander 2025-02-03 21:28 9113
ImFont* ImGui::GetFont()
1193b4be Poul Sander 2025-02-03 21:28 9114
{
1193b4be Poul Sander 2025-02-03 21:28 9115
    return GImGui->Font;
1193b4be Poul Sander 2025-02-03 21:28 9116
}
1193b4be Poul Sander 2025-02-03 21:28 9117
1193b4be Poul Sander 2025-02-03 21:28 9118
float ImGui::GetFontSize()
1193b4be Poul Sander 2025-02-03 21:28 9119
{
1193b4be Poul Sander 2025-02-03 21:28 9120
    return GImGui->FontSize;
1193b4be Poul Sander 2025-02-03 21:28 9121
}
1193b4be Poul Sander 2025-02-03 21:28 9122
1193b4be Poul Sander 2025-02-03 21:28 9123
ImVec2 ImGui::GetFontTexUvWhitePixel()
1193b4be Poul Sander 2025-02-03 21:28 9124
{
1193b4be Poul Sander 2025-02-03 21:28 9125
    return GImGui->DrawListSharedData.TexUvWhitePixel;
1193b4be Poul Sander 2025-02-03 21:28 9126
}
1193b4be Poul Sander 2025-02-03 21:28 9127
1193b4be Poul Sander 2025-02-03 21:28 9128
void ImGui::SetWindowFontScale(float scale)
1193b4be Poul Sander 2025-02-03 21:28 9129
{
1193b4be Poul Sander 2025-02-03 21:28 9130
    IM_ASSERT(scale > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 9131
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9132
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 9133
    window->FontWindowScale = scale;
1193b4be Poul Sander 2025-02-03 21:28 9134
    g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
1193b4be Poul Sander 2025-02-03 21:28 9135
    g.FontScale = g.DrawListSharedData.FontScale = g.FontSize / g.Font->FontSize;
1193b4be Poul Sander 2025-02-03 21:28 9136
}
1193b4be Poul Sander 2025-02-03 21:28 9137
1193b4be Poul Sander 2025-02-03 21:28 9138
void ImGui::PushFocusScope(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 9139
{
1193b4be Poul Sander 2025-02-03 21:28 9140
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9141
    ImGuiFocusScopeData data;
1193b4be Poul Sander 2025-02-03 21:28 9142
    data.ID = id;
1193b4be Poul Sander 2025-02-03 21:28 9143
    data.WindowID = g.CurrentWindow->ID;
1193b4be Poul Sander 2025-02-03 21:28 9144
    g.FocusScopeStack.push_back(data);
1193b4be Poul Sander 2025-02-03 21:28 9145
    g.CurrentFocusScopeId = id;
1193b4be Poul Sander 2025-02-03 21:28 9146
}
1193b4be Poul Sander 2025-02-03 21:28 9147
1193b4be Poul Sander 2025-02-03 21:28 9148
void ImGui::PopFocusScope()
1193b4be Poul Sander 2025-02-03 21:28 9149
{
1193b4be Poul Sander 2025-02-03 21:28 9150
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9151
    if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
1193b4be Poul Sander 2025-02-03 21:28 9152
    {
1193b4be Poul Sander 2025-02-03 21:28 9153
        IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 9154
        return;
1193b4be Poul Sander 2025-02-03 21:28 9155
    }
1193b4be Poul Sander 2025-02-03 21:28 9156
    g.FocusScopeStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 9157
    g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
1193b4be Poul Sander 2025-02-03 21:28 9158
}
1193b4be Poul Sander 2025-02-03 21:28 9159
1193b4be Poul Sander 2025-02-03 21:28 9160
void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
1193b4be Poul Sander 2025-02-03 21:28 9161
{
1193b4be Poul Sander 2025-02-03 21:28 9162
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9163
    g.NavFocusScopeId = focus_scope_id;
1193b4be Poul Sander 2025-02-03 21:28 9164
    g.NavFocusRoute.resize(0); // Invalidate
1193b4be Poul Sander 2025-02-03 21:28 9165
    if (focus_scope_id == 0)
1193b4be Poul Sander 2025-02-03 21:28 9166
        return;
1193b4be Poul Sander 2025-02-03 21:28 9167
    IM_ASSERT(g.NavWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 9168
1193b4be Poul Sander 2025-02-03 21:28 9169
    // Store current path (in reverse order)
1193b4be Poul Sander 2025-02-03 21:28 9170
    if (focus_scope_id == g.CurrentFocusScopeId)
1193b4be Poul Sander 2025-02-03 21:28 9171
    {
1193b4be Poul Sander 2025-02-03 21:28 9172
        // Top of focus stack contains local focus scopes inside current window
1193b4be Poul Sander 2025-02-03 21:28 9173
        for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
1193b4be Poul Sander 2025-02-03 21:28 9174
            g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
1193b4be Poul Sander 2025-02-03 21:28 9175
    }
1193b4be Poul Sander 2025-02-03 21:28 9176
    else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
1193b4be Poul Sander 2025-02-03 21:28 9177
        g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
1193b4be Poul Sander 2025-02-03 21:28 9178
    else
1193b4be Poul Sander 2025-02-03 21:28 9179
        return;
1193b4be Poul Sander 2025-02-03 21:28 9180
1193b4be Poul Sander 2025-02-03 21:28 9181
    // Then follow on manually set ParentWindowForFocusRoute field (#6798)
1193b4be Poul Sander 2025-02-03 21:28 9182
    for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
1193b4be Poul Sander 2025-02-03 21:28 9183
        g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
1193b4be Poul Sander 2025-02-03 21:28 9184
    IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
1193b4be Poul Sander 2025-02-03 21:28 9185
}
1193b4be Poul Sander 2025-02-03 21:28 9186
1193b4be Poul Sander 2025-02-03 21:28 9187
// Focus = move navigation cursor, set scrolling, set focus window.
1193b4be Poul Sander 2025-02-03 21:28 9188
void ImGui::FocusItem()
1193b4be Poul Sander 2025-02-03 21:28 9189
{
1193b4be Poul Sander 2025-02-03 21:28 9190
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9191
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9192
    IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 9193
    if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
1193b4be Poul Sander 2025-02-03 21:28 9194
    {
1193b4be Poul Sander 2025-02-03 21:28 9195
        IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
1193b4be Poul Sander 2025-02-03 21:28 9196
        return;
1193b4be Poul Sander 2025-02-03 21:28 9197
    }
1193b4be Poul Sander 2025-02-03 21:28 9198
1193b4be Poul Sander 2025-02-03 21:28 9199
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
1193b4be Poul Sander 2025-02-03 21:28 9200
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 9201
    SetNavWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 9202
    NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
1193b4be Poul Sander 2025-02-03 21:28 9203
    NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
1193b4be Poul Sander 2025-02-03 21:28 9204
}
1193b4be Poul Sander 2025-02-03 21:28 9205
1193b4be Poul Sander 2025-02-03 21:28 9206
void ImGui::ActivateItemByID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 9207
{
1193b4be Poul Sander 2025-02-03 21:28 9208
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9209
    g.NavNextActivateId = id;
1193b4be Poul Sander 2025-02-03 21:28 9210
    g.NavNextActivateFlags = ImGuiActivateFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 9211
}
1193b4be Poul Sander 2025-02-03 21:28 9212
1193b4be Poul Sander 2025-02-03 21:28 9213
// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
1193b4be Poul Sander 2025-02-03 21:28 9214
// But ActivateItem() should function without altering scroll/focus?
1193b4be Poul Sander 2025-02-03 21:28 9215
void ImGui::SetKeyboardFocusHere(int offset)
1193b4be Poul Sander 2025-02-03 21:28 9216
{
1193b4be Poul Sander 2025-02-03 21:28 9217
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9218
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9219
    IM_ASSERT(offset >= -1);    // -1 is allowed but not below
1193b4be Poul Sander 2025-02-03 21:28 9220
    IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 9221
1193b4be Poul Sander 2025-02-03 21:28 9222
    // It makes sense in the vast majority of cases to never interrupt a drag and drop.
1193b4be Poul Sander 2025-02-03 21:28 9223
    // When we refactor this function into ActivateItem() we may want to make this an option.
1193b4be Poul Sander 2025-02-03 21:28 9224
    // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
1193b4be Poul Sander 2025-02-03 21:28 9225
    // is also automatically dropped in the event g.ActiveId is stolen.
1193b4be Poul Sander 2025-02-03 21:28 9226
    if (g.DragDropActive || g.MovingWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 9227
    {
1193b4be Poul Sander 2025-02-03 21:28 9228
        IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
1193b4be Poul Sander 2025-02-03 21:28 9229
        return;
1193b4be Poul Sander 2025-02-03 21:28 9230
    }
1193b4be Poul Sander 2025-02-03 21:28 9231
1193b4be Poul Sander 2025-02-03 21:28 9232
    SetNavWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 9233
1193b4be Poul Sander 2025-02-03 21:28 9234
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
1193b4be Poul Sander 2025-02-03 21:28 9235
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 9236
    NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
1193b4be Poul Sander 2025-02-03 21:28 9237
    if (offset == -1)
1193b4be Poul Sander 2025-02-03 21:28 9238
    {
1193b4be Poul Sander 2025-02-03 21:28 9239
        NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
1193b4be Poul Sander 2025-02-03 21:28 9240
    }
1193b4be Poul Sander 2025-02-03 21:28 9241
    else
1193b4be Poul Sander 2025-02-03 21:28 9242
    {
1193b4be Poul Sander 2025-02-03 21:28 9243
        g.NavTabbingDir = 1;
1193b4be Poul Sander 2025-02-03 21:28 9244
        g.NavTabbingCounter = offset + 1;
1193b4be Poul Sander 2025-02-03 21:28 9245
    }
1193b4be Poul Sander 2025-02-03 21:28 9246
}
1193b4be Poul Sander 2025-02-03 21:28 9247
1193b4be Poul Sander 2025-02-03 21:28 9248
void ImGui::SetItemDefaultFocus()
1193b4be Poul Sander 2025-02-03 21:28 9249
{
1193b4be Poul Sander 2025-02-03 21:28 9250
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9251
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9252
    if (!window->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 9253
        return;
1193b4be Poul Sander 2025-02-03 21:28 9254
    if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
1193b4be Poul Sander 2025-02-03 21:28 9255
        return;
1193b4be Poul Sander 2025-02-03 21:28 9256
1193b4be Poul Sander 2025-02-03 21:28 9257
    g.NavInitRequest = false;
1193b4be Poul Sander 2025-02-03 21:28 9258
    NavApplyItemToResult(&g.NavInitResult);
1193b4be Poul Sander 2025-02-03 21:28 9259
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 9260
1193b4be Poul Sander 2025-02-03 21:28 9261
    // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
1193b4be Poul Sander 2025-02-03 21:28 9262
    if (!window->ClipRect.Contains(g.LastItemData.Rect))
1193b4be Poul Sander 2025-02-03 21:28 9263
        ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 9264
}
1193b4be Poul Sander 2025-02-03 21:28 9265
1193b4be Poul Sander 2025-02-03 21:28 9266
void ImGui::SetStateStorage(ImGuiStorage* tree)
1193b4be Poul Sander 2025-02-03 21:28 9267
{
1193b4be Poul Sander 2025-02-03 21:28 9268
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9269
    window->DC.StateStorage = tree ? tree : &window->StateStorage;
1193b4be Poul Sander 2025-02-03 21:28 9270
}
1193b4be Poul Sander 2025-02-03 21:28 9271
1193b4be Poul Sander 2025-02-03 21:28 9272
ImGuiStorage* ImGui::GetStateStorage()
1193b4be Poul Sander 2025-02-03 21:28 9273
{
1193b4be Poul Sander 2025-02-03 21:28 9274
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9275
    return window->DC.StateStorage;
1193b4be Poul Sander 2025-02-03 21:28 9276
}
1193b4be Poul Sander 2025-02-03 21:28 9277
1193b4be Poul Sander 2025-02-03 21:28 9278
bool ImGui::IsRectVisible(const ImVec2& size)
1193b4be Poul Sander 2025-02-03 21:28 9279
{
1193b4be Poul Sander 2025-02-03 21:28 9280
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9281
    return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
1193b4be Poul Sander 2025-02-03 21:28 9282
}
1193b4be Poul Sander 2025-02-03 21:28 9283
1193b4be Poul Sander 2025-02-03 21:28 9284
bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
1193b4be Poul Sander 2025-02-03 21:28 9285
{
1193b4be Poul Sander 2025-02-03 21:28 9286
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9287
    return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
1193b4be Poul Sander 2025-02-03 21:28 9288
}
1193b4be Poul Sander 2025-02-03 21:28 9289
1193b4be Poul Sander 2025-02-03 21:28 9290
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9291
// [SECTION] ID STACK
1193b4be Poul Sander 2025-02-03 21:28 9292
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9293
1193b4be Poul Sander 2025-02-03 21:28 9294
// This is one of the very rare legacy case where we use ImGuiWindow methods,
1193b4be Poul Sander 2025-02-03 21:28 9295
// it should ideally be flattened at some point but it's been used a lots by widgets.
1193b4be Poul Sander 2025-02-03 21:28 9296
IM_MSVC_RUNTIME_CHECKS_OFF
1193b4be Poul Sander 2025-02-03 21:28 9297
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
1193b4be Poul Sander 2025-02-03 21:28 9298
{
1193b4be Poul Sander 2025-02-03 21:28 9299
    ImGuiID seed = IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 9300
    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
1193b4be Poul Sander 2025-02-03 21:28 9301
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9302
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 9303
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9304
        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
1193b4be Poul Sander 2025-02-03 21:28 9305
#endif
1193b4be Poul Sander 2025-02-03 21:28 9306
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9307
}
1193b4be Poul Sander 2025-02-03 21:28 9308
1193b4be Poul Sander 2025-02-03 21:28 9309
ImGuiID ImGuiWindow::GetID(const void* ptr)
1193b4be Poul Sander 2025-02-03 21:28 9310
{
1193b4be Poul Sander 2025-02-03 21:28 9311
    ImGuiID seed = IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 9312
    ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
1193b4be Poul Sander 2025-02-03 21:28 9313
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9314
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 9315
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9316
        ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
1193b4be Poul Sander 2025-02-03 21:28 9317
#endif
1193b4be Poul Sander 2025-02-03 21:28 9318
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9319
}
1193b4be Poul Sander 2025-02-03 21:28 9320
1193b4be Poul Sander 2025-02-03 21:28 9321
ImGuiID ImGuiWindow::GetID(int n)
1193b4be Poul Sander 2025-02-03 21:28 9322
{
1193b4be Poul Sander 2025-02-03 21:28 9323
    ImGuiID seed = IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 9324
    ImGuiID id = ImHashData(&n, sizeof(n), seed);
1193b4be Poul Sander 2025-02-03 21:28 9325
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9326
    ImGuiContext& g = *Ctx;
1193b4be Poul Sander 2025-02-03 21:28 9327
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9328
        ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
1193b4be Poul Sander 2025-02-03 21:28 9329
#endif
1193b4be Poul Sander 2025-02-03 21:28 9330
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9331
}
1193b4be Poul Sander 2025-02-03 21:28 9332
1193b4be Poul Sander 2025-02-03 21:28 9333
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
1193b4be Poul Sander 2025-02-03 21:28 9334
// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
1193b4be Poul Sander 2025-02-03 21:28 9335
ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
1193b4be Poul Sander 2025-02-03 21:28 9336
{
1193b4be Poul Sander 2025-02-03 21:28 9337
    ImGuiID seed = IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 9338
    ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);
1193b4be Poul Sander 2025-02-03 21:28 9339
    ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);
1193b4be Poul Sander 2025-02-03 21:28 9340
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9341
}
1193b4be Poul Sander 2025-02-03 21:28 9342
1193b4be Poul Sander 2025-02-03 21:28 9343
// "
1193b4be Poul Sander 2025-02-03 21:28 9344
ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
1193b4be Poul Sander 2025-02-03 21:28 9345
{
1193b4be Poul Sander 2025-02-03 21:28 9346
    ImGuiID seed = IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 9347
    ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
1193b4be Poul Sander 2025-02-03 21:28 9348
    ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
1193b4be Poul Sander 2025-02-03 21:28 9349
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9350
}
1193b4be Poul Sander 2025-02-03 21:28 9351
1193b4be Poul Sander 2025-02-03 21:28 9352
void ImGui::PushID(const char* str_id)
1193b4be Poul Sander 2025-02-03 21:28 9353
{
1193b4be Poul Sander 2025-02-03 21:28 9354
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9355
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9356
    ImGuiID id = window->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 9357
    window->IDStack.push_back(id);
1193b4be Poul Sander 2025-02-03 21:28 9358
}
1193b4be Poul Sander 2025-02-03 21:28 9359
1193b4be Poul Sander 2025-02-03 21:28 9360
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
1193b4be Poul Sander 2025-02-03 21:28 9361
{
1193b4be Poul Sander 2025-02-03 21:28 9362
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9363
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9364
    ImGuiID id = window->GetID(str_id_begin, str_id_end);
1193b4be Poul Sander 2025-02-03 21:28 9365
    window->IDStack.push_back(id);
1193b4be Poul Sander 2025-02-03 21:28 9366
}
1193b4be Poul Sander 2025-02-03 21:28 9367
1193b4be Poul Sander 2025-02-03 21:28 9368
void ImGui::PushID(const void* ptr_id)
1193b4be Poul Sander 2025-02-03 21:28 9369
{
1193b4be Poul Sander 2025-02-03 21:28 9370
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9371
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9372
    ImGuiID id = window->GetID(ptr_id);
1193b4be Poul Sander 2025-02-03 21:28 9373
    window->IDStack.push_back(id);
1193b4be Poul Sander 2025-02-03 21:28 9374
}
1193b4be Poul Sander 2025-02-03 21:28 9375
1193b4be Poul Sander 2025-02-03 21:28 9376
void ImGui::PushID(int int_id)
1193b4be Poul Sander 2025-02-03 21:28 9377
{
1193b4be Poul Sander 2025-02-03 21:28 9378
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9379
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9380
    ImGuiID id = window->GetID(int_id);
1193b4be Poul Sander 2025-02-03 21:28 9381
    window->IDStack.push_back(id);
1193b4be Poul Sander 2025-02-03 21:28 9382
}
1193b4be Poul Sander 2025-02-03 21:28 9383
1193b4be Poul Sander 2025-02-03 21:28 9384
// Push a given id value ignoring the ID stack as a seed.
1193b4be Poul Sander 2025-02-03 21:28 9385
void ImGui::PushOverrideID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 9386
{
1193b4be Poul Sander 2025-02-03 21:28 9387
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9388
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9389
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9390
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9391
        DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 9392
#endif
1193b4be Poul Sander 2025-02-03 21:28 9393
    window->IDStack.push_back(id);
1193b4be Poul Sander 2025-02-03 21:28 9394
}
1193b4be Poul Sander 2025-02-03 21:28 9395
1193b4be Poul Sander 2025-02-03 21:28 9396
// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
1193b4be Poul Sander 2025-02-03 21:28 9397
// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
1193b4be Poul Sander 2025-02-03 21:28 9398
//  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
1193b4be Poul Sander 2025-02-03 21:28 9399
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
1193b4be Poul Sander 2025-02-03 21:28 9400
{
1193b4be Poul Sander 2025-02-03 21:28 9401
    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
1193b4be Poul Sander 2025-02-03 21:28 9402
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9403
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9404
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9405
        DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
1193b4be Poul Sander 2025-02-03 21:28 9406
#endif
1193b4be Poul Sander 2025-02-03 21:28 9407
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9408
}
1193b4be Poul Sander 2025-02-03 21:28 9409
1193b4be Poul Sander 2025-02-03 21:28 9410
ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
1193b4be Poul Sander 2025-02-03 21:28 9411
{
1193b4be Poul Sander 2025-02-03 21:28 9412
    ImGuiID id = ImHashData(&n, sizeof(n), seed);
1193b4be Poul Sander 2025-02-03 21:28 9413
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 9414
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9415
    if (g.DebugHookIdInfo == id)
1193b4be Poul Sander 2025-02-03 21:28 9416
        DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
1193b4be Poul Sander 2025-02-03 21:28 9417
#endif
1193b4be Poul Sander 2025-02-03 21:28 9418
    return id;
1193b4be Poul Sander 2025-02-03 21:28 9419
}
1193b4be Poul Sander 2025-02-03 21:28 9420
1193b4be Poul Sander 2025-02-03 21:28 9421
void ImGui::PopID()
1193b4be Poul Sander 2025-02-03 21:28 9422
{
1193b4be Poul Sander 2025-02-03 21:28 9423
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9424
    if (window->IDStack.Size <= 1)
1193b4be Poul Sander 2025-02-03 21:28 9425
    {
1193b4be Poul Sander 2025-02-03 21:28 9426
        IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 9427
        return;
1193b4be Poul Sander 2025-02-03 21:28 9428
    }
1193b4be Poul Sander 2025-02-03 21:28 9429
    window->IDStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 9430
}
1193b4be Poul Sander 2025-02-03 21:28 9431
1193b4be Poul Sander 2025-02-03 21:28 9432
ImGuiID ImGui::GetID(const char* str_id)
1193b4be Poul Sander 2025-02-03 21:28 9433
{
1193b4be Poul Sander 2025-02-03 21:28 9434
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9435
    return window->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 9436
}
1193b4be Poul Sander 2025-02-03 21:28 9437
1193b4be Poul Sander 2025-02-03 21:28 9438
ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
1193b4be Poul Sander 2025-02-03 21:28 9439
{
1193b4be Poul Sander 2025-02-03 21:28 9440
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9441
    return window->GetID(str_id_begin, str_id_end);
1193b4be Poul Sander 2025-02-03 21:28 9442
}
1193b4be Poul Sander 2025-02-03 21:28 9443
1193b4be Poul Sander 2025-02-03 21:28 9444
ImGuiID ImGui::GetID(const void* ptr_id)
1193b4be Poul Sander 2025-02-03 21:28 9445
{
1193b4be Poul Sander 2025-02-03 21:28 9446
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9447
    return window->GetID(ptr_id);
1193b4be Poul Sander 2025-02-03 21:28 9448
}
1193b4be Poul Sander 2025-02-03 21:28 9449
1193b4be Poul Sander 2025-02-03 21:28 9450
ImGuiID ImGui::GetID(int int_id)
1193b4be Poul Sander 2025-02-03 21:28 9451
{
1193b4be Poul Sander 2025-02-03 21:28 9452
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 9453
    return window->GetID(int_id);
1193b4be Poul Sander 2025-02-03 21:28 9454
}
1193b4be Poul Sander 2025-02-03 21:28 9455
IM_MSVC_RUNTIME_CHECKS_RESTORE
1193b4be Poul Sander 2025-02-03 21:28 9456
1193b4be Poul Sander 2025-02-03 21:28 9457
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9458
// [SECTION] INPUTS
1193b4be Poul Sander 2025-02-03 21:28 9459
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9460
// - GetModForLRModKey() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9461
// - FixupKeyChord() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9462
// - GetKeyData() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9463
// - GetKeyIndex() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9464
// - GetKeyName()
1193b4be Poul Sander 2025-02-03 21:28 9465
// - GetKeyChordName() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9466
// - CalcTypematicRepeatAmount() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9467
// - GetTypematicRepeatRate() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9468
// - GetKeyPressedAmount() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9469
// - GetKeyMagnitude2d() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9470
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9471
// - UpdateKeyRoutingTable() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9472
// - GetRoutingIdFromOwnerId() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9473
// - GetShortcutRoutingData() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9474
// - CalcRoutingScore() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9475
// - SetShortcutRouting() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9476
// - TestShortcutRouting() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9477
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9478
// - IsKeyDown()
1193b4be Poul Sander 2025-02-03 21:28 9479
// - IsKeyPressed()
1193b4be Poul Sander 2025-02-03 21:28 9480
// - IsKeyReleased()
1193b4be Poul Sander 2025-02-03 21:28 9481
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9482
// - IsMouseDown()
1193b4be Poul Sander 2025-02-03 21:28 9483
// - IsMouseClicked()
1193b4be Poul Sander 2025-02-03 21:28 9484
// - IsMouseReleased()
1193b4be Poul Sander 2025-02-03 21:28 9485
// - IsMouseDoubleClicked()
1193b4be Poul Sander 2025-02-03 21:28 9486
// - GetMouseClickedCount()
1193b4be Poul Sander 2025-02-03 21:28 9487
// - IsMouseHoveringRect() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9488
// - IsMouseDragPastThreshold() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9489
// - IsMouseDragging()
1193b4be Poul Sander 2025-02-03 21:28 9490
// - GetMousePos()
1193b4be Poul Sander 2025-02-03 21:28 9491
// - SetMousePos() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9492
// - GetMousePosOnOpeningCurrentPopup()
1193b4be Poul Sander 2025-02-03 21:28 9493
// - IsMousePosValid()
1193b4be Poul Sander 2025-02-03 21:28 9494
// - IsAnyMouseDown()
1193b4be Poul Sander 2025-02-03 21:28 9495
// - GetMouseDragDelta()
1193b4be Poul Sander 2025-02-03 21:28 9496
// - ResetMouseDragDelta()
1193b4be Poul Sander 2025-02-03 21:28 9497
// - GetMouseCursor()
1193b4be Poul Sander 2025-02-03 21:28 9498
// - SetMouseCursor()
1193b4be Poul Sander 2025-02-03 21:28 9499
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9500
// - UpdateAliasKey()
1193b4be Poul Sander 2025-02-03 21:28 9501
// - GetMergedModsFromKeys()
1193b4be Poul Sander 2025-02-03 21:28 9502
// - UpdateKeyboardInputs()
1193b4be Poul Sander 2025-02-03 21:28 9503
// - UpdateMouseInputs()
1193b4be Poul Sander 2025-02-03 21:28 9504
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9505
// - LockWheelingWindow [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9506
// - FindBestWheelingWindow [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9507
// - UpdateMouseWheel() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9508
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9509
// - SetNextFrameWantCaptureKeyboard()
1193b4be Poul Sander 2025-02-03 21:28 9510
// - SetNextFrameWantCaptureMouse()
1193b4be Poul Sander 2025-02-03 21:28 9511
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9512
// - GetInputSourceName() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9513
// - DebugPrintInputEvent() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9514
// - UpdateInputEvents() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9515
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9516
// - GetKeyOwner() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9517
// - TestKeyOwner() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9518
// - SetKeyOwner() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9519
// - SetItemKeyOwner() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9520
// - Shortcut() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 9521
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 9522
1193b4be Poul Sander 2025-02-03 21:28 9523
static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 9524
{
1193b4be Poul Sander 2025-02-03 21:28 9525
    if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
1193b4be Poul Sander 2025-02-03 21:28 9526
        return ImGuiMod_Ctrl;
1193b4be Poul Sander 2025-02-03 21:28 9527
    if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
1193b4be Poul Sander 2025-02-03 21:28 9528
        return ImGuiMod_Shift;
1193b4be Poul Sander 2025-02-03 21:28 9529
    if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
1193b4be Poul Sander 2025-02-03 21:28 9530
        return ImGuiMod_Alt;
1193b4be Poul Sander 2025-02-03 21:28 9531
    if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
1193b4be Poul Sander 2025-02-03 21:28 9532
        return ImGuiMod_Super;
1193b4be Poul Sander 2025-02-03 21:28 9533
    return ImGuiMod_None;
1193b4be Poul Sander 2025-02-03 21:28 9534
}
1193b4be Poul Sander 2025-02-03 21:28 9535
1193b4be Poul Sander 2025-02-03 21:28 9536
ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
1193b4be Poul Sander 2025-02-03 21:28 9537
{
1193b4be Poul Sander 2025-02-03 21:28 9538
    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
1193b4be Poul Sander 2025-02-03 21:28 9539
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9540
    if (IsLRModKey(key))
1193b4be Poul Sander 2025-02-03 21:28 9541
        key_chord |= GetModForLRModKey(key);
1193b4be Poul Sander 2025-02-03 21:28 9542
    return key_chord;
1193b4be Poul Sander 2025-02-03 21:28 9543
}
1193b4be Poul Sander 2025-02-03 21:28 9544
1193b4be Poul Sander 2025-02-03 21:28 9545
ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 9546
{
1193b4be Poul Sander 2025-02-03 21:28 9547
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 9548
1193b4be Poul Sander 2025-02-03 21:28 9549
    // Special storage location for mods
1193b4be Poul Sander 2025-02-03 21:28 9550
    if (key & ImGuiMod_Mask_)
1193b4be Poul Sander 2025-02-03 21:28 9551
        key = ConvertSingleModFlagToKey(key);
1193b4be Poul Sander 2025-02-03 21:28 9552
1193b4be Poul Sander 2025-02-03 21:28 9553
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 9554
    IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
1193b4be Poul Sander 2025-02-03 21:28 9555
    if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)
1193b4be Poul Sander 2025-02-03 21:28 9556
        key = (ImGuiKey)g.IO.KeyMap[key];  // Remap native->imgui or imgui->native
1193b4be Poul Sander 2025-02-03 21:28 9557
#else
1193b4be Poul Sander 2025-02-03 21:28 9558
    IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
1193b4be Poul Sander 2025-02-03 21:28 9559
#endif
1193b4be Poul Sander 2025-02-03 21:28 9560
    return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
1193b4be Poul Sander 2025-02-03 21:28 9561
}
1193b4be Poul Sander 2025-02-03 21:28 9562
1193b4be Poul Sander 2025-02-03 21:28 9563
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 9564
// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
1193b4be Poul Sander 2025-02-03 21:28 9565
ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 9566
{
1193b4be Poul Sander 2025-02-03 21:28 9567
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9568
    IM_ASSERT(IsNamedKey(key));
1193b4be Poul Sander 2025-02-03 21:28 9569
    const ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 9570
    return (ImGuiKey)(key_data - g.IO.KeysData);
1193b4be Poul Sander 2025-02-03 21:28 9571
}
1193b4be Poul Sander 2025-02-03 21:28 9572
#endif
1193b4be Poul Sander 2025-02-03 21:28 9573
1193b4be Poul Sander 2025-02-03 21:28 9574
// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
1193b4be Poul Sander 2025-02-03 21:28 9575
static const char* const GKeyNames[] =
1193b4be Poul Sander 2025-02-03 21:28 9576
{
1193b4be Poul Sander 2025-02-03 21:28 9577
    "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
1193b4be Poul Sander 2025-02-03 21:28 9578
    "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
1193b4be Poul Sander 2025-02-03 21:28 9579
    "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
1193b4be Poul Sander 2025-02-03 21:28 9580
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
1193b4be Poul Sander 2025-02-03 21:28 9581
    "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
1193b4be Poul Sander 2025-02-03 21:28 9582
    "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
1193b4be Poul Sander 2025-02-03 21:28 9583
    "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
1193b4be Poul Sander 2025-02-03 21:28 9584
    "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
1193b4be Poul Sander 2025-02-03 21:28 9585
    "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
1193b4be Poul Sander 2025-02-03 21:28 9586
    "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
1193b4be Poul Sander 2025-02-03 21:28 9587
    "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
1193b4be Poul Sander 2025-02-03 21:28 9588
    "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
1193b4be Poul Sander 2025-02-03 21:28 9589
    "AppBack", "AppForward",
1193b4be Poul Sander 2025-02-03 21:28 9590
    "GamepadStart", "GamepadBack",
1193b4be Poul Sander 2025-02-03 21:28 9591
    "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
1193b4be Poul Sander 2025-02-03 21:28 9592
    "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
1193b4be Poul Sander 2025-02-03 21:28 9593
    "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
1193b4be Poul Sander 2025-02-03 21:28 9594
    "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
1193b4be Poul Sander 2025-02-03 21:28 9595
    "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
1193b4be Poul Sander 2025-02-03 21:28 9596
    "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
1193b4be Poul Sander 2025-02-03 21:28 9597
    "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
1193b4be Poul Sander 2025-02-03 21:28 9598
};
1193b4be Poul Sander 2025-02-03 21:28 9599
IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
1193b4be Poul Sander 2025-02-03 21:28 9600
1193b4be Poul Sander 2025-02-03 21:28 9601
const char* ImGui::GetKeyName(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 9602
{
1193b4be Poul Sander 2025-02-03 21:28 9603
    if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 9604
        return "None";
1193b4be Poul Sander 2025-02-03 21:28 9605
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 9606
    IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
1193b4be Poul Sander 2025-02-03 21:28 9607
#else
1193b4be Poul Sander 2025-02-03 21:28 9608
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9609
    if (IsLegacyKey(key))
1193b4be Poul Sander 2025-02-03 21:28 9610
    {
1193b4be Poul Sander 2025-02-03 21:28 9611
        if (g.IO.KeyMap[key] == -1)
1193b4be Poul Sander 2025-02-03 21:28 9612
            return "N/A";
1193b4be Poul Sander 2025-02-03 21:28 9613
        IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));
1193b4be Poul Sander 2025-02-03 21:28 9614
        key = (ImGuiKey)g.IO.KeyMap[key];
1193b4be Poul Sander 2025-02-03 21:28 9615
    }
1193b4be Poul Sander 2025-02-03 21:28 9616
#endif
1193b4be Poul Sander 2025-02-03 21:28 9617
    if (key & ImGuiMod_Mask_)
1193b4be Poul Sander 2025-02-03 21:28 9618
        key = ConvertSingleModFlagToKey(key);
1193b4be Poul Sander 2025-02-03 21:28 9619
    if (!IsNamedKey(key))
1193b4be Poul Sander 2025-02-03 21:28 9620
        return "Unknown";
1193b4be Poul Sander 2025-02-03 21:28 9621
1193b4be Poul Sander 2025-02-03 21:28 9622
    return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
1193b4be Poul Sander 2025-02-03 21:28 9623
}
1193b4be Poul Sander 2025-02-03 21:28 9624
1193b4be Poul Sander 2025-02-03 21:28 9625
// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
1193b4be Poul Sander 2025-02-03 21:28 9626
// Lifetime of return value: valid until next call to same function.
1193b4be Poul Sander 2025-02-03 21:28 9627
const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
1193b4be Poul Sander 2025-02-03 21:28 9628
{
1193b4be Poul Sander 2025-02-03 21:28 9629
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9630
1193b4be Poul Sander 2025-02-03 21:28 9631
    const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9632
    if (IsLRModKey(key))
1193b4be Poul Sander 2025-02-03 21:28 9633
        key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
1193b4be Poul Sander 2025-02-03 21:28 9634
    ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
1193b4be Poul Sander 2025-02-03 21:28 9635
        (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
1193b4be Poul Sander 2025-02-03 21:28 9636
        (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
1193b4be Poul Sander 2025-02-03 21:28 9637
        (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
1193b4be Poul Sander 2025-02-03 21:28 9638
        (key_chord & ImGuiMod_Super) ? "Super+" : "",
1193b4be Poul Sander 2025-02-03 21:28 9639
        (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
1193b4be Poul Sander 2025-02-03 21:28 9640
    size_t len;
1193b4be Poul Sander 2025-02-03 21:28 9641
    if (key == ImGuiKey_None && key_chord != 0)
1193b4be Poul Sander 2025-02-03 21:28 9642
        if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+'
1193b4be Poul Sander 2025-02-03 21:28 9643
            g.TempKeychordName[len - 1] = 0;
1193b4be Poul Sander 2025-02-03 21:28 9644
    return g.TempKeychordName;
1193b4be Poul Sander 2025-02-03 21:28 9645
}
1193b4be Poul Sander 2025-02-03 21:28 9646
1193b4be Poul Sander 2025-02-03 21:28 9647
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
1193b4be Poul Sander 2025-02-03 21:28 9648
// t1 = current time (e.g.: g.Time)
1193b4be Poul Sander 2025-02-03 21:28 9649
// An event is triggered at:
1193b4be Poul Sander 2025-02-03 21:28 9650
//  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
1193b4be Poul Sander 2025-02-03 21:28 9651
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
1193b4be Poul Sander 2025-02-03 21:28 9652
{
1193b4be Poul Sander 2025-02-03 21:28 9653
    if (t1 == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 9654
        return 1;
1193b4be Poul Sander 2025-02-03 21:28 9655
    if (t0 >= t1)
1193b4be Poul Sander 2025-02-03 21:28 9656
        return 0;
1193b4be Poul Sander 2025-02-03 21:28 9657
    if (repeat_rate <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 9658
        return (t0 < repeat_delay) && (t1 >= repeat_delay);
1193b4be Poul Sander 2025-02-03 21:28 9659
    const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 9660
    const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 9661
    const int count = count_t1 - count_t0;
1193b4be Poul Sander 2025-02-03 21:28 9662
    return count;
1193b4be Poul Sander 2025-02-03 21:28 9663
}
1193b4be Poul Sander 2025-02-03 21:28 9664
1193b4be Poul Sander 2025-02-03 21:28 9665
void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
1193b4be Poul Sander 2025-02-03 21:28 9666
{
1193b4be Poul Sander 2025-02-03 21:28 9667
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9668
    switch (flags & ImGuiInputFlags_RepeatRateMask_)
1193b4be Poul Sander 2025-02-03 21:28 9669
    {
1193b4be Poul Sander 2025-02-03 21:28 9670
    case ImGuiInputFlags_RepeatRateNavMove:             *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
1193b4be Poul Sander 2025-02-03 21:28 9671
    case ImGuiInputFlags_RepeatRateNavTweak:            *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
1193b4be Poul Sander 2025-02-03 21:28 9672
    case ImGuiInputFlags_RepeatRateDefault: default:    *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
1193b4be Poul Sander 2025-02-03 21:28 9673
    }
1193b4be Poul Sander 2025-02-03 21:28 9674
}
1193b4be Poul Sander 2025-02-03 21:28 9675
1193b4be Poul Sander 2025-02-03 21:28 9676
// Return value representing the number of presses in the last time period, for the given repeat rate
1193b4be Poul Sander 2025-02-03 21:28 9677
// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
1193b4be Poul Sander 2025-02-03 21:28 9678
int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
1193b4be Poul Sander 2025-02-03 21:28 9679
{
1193b4be Poul Sander 2025-02-03 21:28 9680
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9681
    const ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 9682
    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
1193b4be Poul Sander 2025-02-03 21:28 9683
        return 0;
1193b4be Poul Sander 2025-02-03 21:28 9684
    const float t = key_data->DownDuration;
1193b4be Poul Sander 2025-02-03 21:28 9685
    return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 9686
}
1193b4be Poul Sander 2025-02-03 21:28 9687
1193b4be Poul Sander 2025-02-03 21:28 9688
// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
1193b4be Poul Sander 2025-02-03 21:28 9689
ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
1193b4be Poul Sander 2025-02-03 21:28 9690
{
1193b4be Poul Sander 2025-02-03 21:28 9691
    return ImVec2(
1193b4be Poul Sander 2025-02-03 21:28 9692
        GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
1193b4be Poul Sander 2025-02-03 21:28 9693
        GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
1193b4be Poul Sander 2025-02-03 21:28 9694
}
1193b4be Poul Sander 2025-02-03 21:28 9695
1193b4be Poul Sander 2025-02-03 21:28 9696
// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
1193b4be Poul Sander 2025-02-03 21:28 9697
//   Entries   D,A,B,B,A,C,B     --> A,A,B,B,B,C,D
1193b4be Poul Sander 2025-02-03 21:28 9698
//   Index     A:1 B:2 C:5 D:0   --> A:0 B:2 C:5 D:6
1193b4be Poul Sander 2025-02-03 21:28 9699
// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
1193b4be Poul Sander 2025-02-03 21:28 9700
static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
1193b4be Poul Sander 2025-02-03 21:28 9701
{
1193b4be Poul Sander 2025-02-03 21:28 9702
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9703
    rt->EntriesNext.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 9704
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 9705
    {
1193b4be Poul Sander 2025-02-03 21:28 9706
        const int new_routing_start_idx = rt->EntriesNext.Size;
1193b4be Poul Sander 2025-02-03 21:28 9707
        ImGuiKeyRoutingData* routing_entry;
1193b4be Poul Sander 2025-02-03 21:28 9708
        for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
1193b4be Poul Sander 2025-02-03 21:28 9709
        {
1193b4be Poul Sander 2025-02-03 21:28 9710
            routing_entry = &rt->Entries[old_routing_idx];
1193b4be Poul Sander 2025-02-03 21:28 9711
            routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
1193b4be Poul Sander 2025-02-03 21:28 9712
            routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
1193b4be Poul Sander 2025-02-03 21:28 9713
            routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
1193b4be Poul Sander 2025-02-03 21:28 9714
            routing_entry->RoutingNextScore = 255;
1193b4be Poul Sander 2025-02-03 21:28 9715
            if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 9716
                continue;
1193b4be Poul Sander 2025-02-03 21:28 9717
            rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
1193b4be Poul Sander 2025-02-03 21:28 9718
1193b4be Poul Sander 2025-02-03 21:28 9719
            // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
1193b4be Poul Sander 2025-02-03 21:28 9720
            // This is the result of previous frame's SetShortcutRouting() call.
1193b4be Poul Sander 2025-02-03 21:28 9721
            if (routing_entry->Mods == g.IO.KeyMods)
1193b4be Poul Sander 2025-02-03 21:28 9722
            {
1193b4be Poul Sander 2025-02-03 21:28 9723
                ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 9724
                if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 9725
                {
1193b4be Poul Sander 2025-02-03 21:28 9726
                    owner_data->OwnerCurr = routing_entry->RoutingCurr;
1193b4be Poul Sander 2025-02-03 21:28 9727
                    //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
1193b4be Poul Sander 2025-02-03 21:28 9728
                }
1193b4be Poul Sander 2025-02-03 21:28 9729
            }
1193b4be Poul Sander 2025-02-03 21:28 9730
        }
1193b4be Poul Sander 2025-02-03 21:28 9731
1193b4be Poul Sander 2025-02-03 21:28 9732
        // Rewrite linked-list
1193b4be Poul Sander 2025-02-03 21:28 9733
        rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
1193b4be Poul Sander 2025-02-03 21:28 9734
        for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 9735
            rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
1193b4be Poul Sander 2025-02-03 21:28 9736
    }
1193b4be Poul Sander 2025-02-03 21:28 9737
    rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
1193b4be Poul Sander 2025-02-03 21:28 9738
}
1193b4be Poul Sander 2025-02-03 21:28 9739
1193b4be Poul Sander 2025-02-03 21:28 9740
// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
1193b4be Poul Sander 2025-02-03 21:28 9741
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9742
{
1193b4be Poul Sander 2025-02-03 21:28 9743
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9744
    return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 9745
}
1193b4be Poul Sander 2025-02-03 21:28 9746
1193b4be Poul Sander 2025-02-03 21:28 9747
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
1193b4be Poul Sander 2025-02-03 21:28 9748
{
1193b4be Poul Sander 2025-02-03 21:28 9749
    // Majority of shortcuts will be Key + any number of Mods
1193b4be Poul Sander 2025-02-03 21:28 9750
    // We accept _Single_ mod with ImGuiKey_None.
1193b4be Poul Sander 2025-02-03 21:28 9751
    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl);                    // Legal
1193b4be Poul Sander 2025-02-03 21:28 9752
    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift);   // Legal
1193b4be Poul Sander 2025-02-03 21:28 9753
    //  - Shortcut(ImGuiMod_Ctrl);                                 // Legal
1193b4be Poul Sander 2025-02-03 21:28 9754
    //  - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift);                // Not legal
1193b4be Poul Sander 2025-02-03 21:28 9755
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9756
    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
1193b4be Poul Sander 2025-02-03 21:28 9757
    ImGuiKeyRoutingData* routing_data;
1193b4be Poul Sander 2025-02-03 21:28 9758
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9759
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9760
    if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 9761
        key = ConvertSingleModFlagToKey(mods);
1193b4be Poul Sander 2025-02-03 21:28 9762
    IM_ASSERT(IsNamedKey(key));
1193b4be Poul Sander 2025-02-03 21:28 9763
1193b4be Poul Sander 2025-02-03 21:28 9764
    // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
1193b4be Poul Sander 2025-02-03 21:28 9765
    // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
1193b4be Poul Sander 2025-02-03 21:28 9766
    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
1193b4be Poul Sander 2025-02-03 21:28 9767
    {
1193b4be Poul Sander 2025-02-03 21:28 9768
        routing_data = &rt->Entries[idx];
1193b4be Poul Sander 2025-02-03 21:28 9769
        if (routing_data->Mods == mods)
1193b4be Poul Sander 2025-02-03 21:28 9770
            return routing_data;
1193b4be Poul Sander 2025-02-03 21:28 9771
    }
1193b4be Poul Sander 2025-02-03 21:28 9772
1193b4be Poul Sander 2025-02-03 21:28 9773
    // Add to linked-list
1193b4be Poul Sander 2025-02-03 21:28 9774
    ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
1193b4be Poul Sander 2025-02-03 21:28 9775
    rt->Entries.push_back(ImGuiKeyRoutingData());
1193b4be Poul Sander 2025-02-03 21:28 9776
    routing_data = &rt->Entries[routing_data_idx];
1193b4be Poul Sander 2025-02-03 21:28 9777
    routing_data->Mods = (ImU16)mods;
1193b4be Poul Sander 2025-02-03 21:28 9778
    routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
1193b4be Poul Sander 2025-02-03 21:28 9779
    rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
1193b4be Poul Sander 2025-02-03 21:28 9780
    return routing_data;
1193b4be Poul Sander 2025-02-03 21:28 9781
}
1193b4be Poul Sander 2025-02-03 21:28 9782
1193b4be Poul Sander 2025-02-03 21:28 9783
// Current score encoding (lower is highest priority):
1193b4be Poul Sander 2025-02-03 21:28 9784
//  -   0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
1193b4be Poul Sander 2025-02-03 21:28 9785
//  -   1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
1193b4be Poul Sander 2025-02-03 21:28 9786
//  -   2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
1193b4be Poul Sander 2025-02-03 21:28 9787
//  -  3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
1193b4be Poul Sander 2025-02-03 21:28 9788
//  - 254: ImGuiInputFlags_RouteGlobal
1193b4be Poul Sander 2025-02-03 21:28 9789
//  - 255: never route
1193b4be Poul Sander 2025-02-03 21:28 9790
// 'flags' should include an explicit routing policy
1193b4be Poul Sander 2025-02-03 21:28 9791
static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 9792
{
1193b4be Poul Sander 2025-02-03 21:28 9793
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9794
    if (flags & ImGuiInputFlags_RouteFocused)
1193b4be Poul Sander 2025-02-03 21:28 9795
    {
1193b4be Poul Sander 2025-02-03 21:28 9796
        // ActiveID gets top priority
1193b4be Poul Sander 2025-02-03 21:28 9797
        // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
1193b4be Poul Sander 2025-02-03 21:28 9798
        if (owner_id != 0 && g.ActiveId == owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9799
            return 1;
1193b4be Poul Sander 2025-02-03 21:28 9800
1193b4be Poul Sander 2025-02-03 21:28 9801
        // Score based on distance to focused window (lower is better)
1193b4be Poul Sander 2025-02-03 21:28 9802
        // Assuming both windows are submitting a routing request,
1193b4be Poul Sander 2025-02-03 21:28 9803
        // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
1193b4be Poul Sander 2025-02-03 21:28 9804
        // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)
1193b4be Poul Sander 2025-02-03 21:28 9805
        // Assuming only WindowA is submitting a routing request,
1193b4be Poul Sander 2025-02-03 21:28 9806
        // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
1193b4be Poul Sander 2025-02-03 21:28 9807
        // This essentially follow the window->ParentWindowForFocusRoute chain.
1193b4be Poul Sander 2025-02-03 21:28 9808
        if (focus_scope_id == 0)
1193b4be Poul Sander 2025-02-03 21:28 9809
            return 255;
1193b4be Poul Sander 2025-02-03 21:28 9810
        for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
1193b4be Poul Sander 2025-02-03 21:28 9811
            if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
1193b4be Poul Sander 2025-02-03 21:28 9812
                return 3 + index_in_focus_path;
1193b4be Poul Sander 2025-02-03 21:28 9813
        return 255;
1193b4be Poul Sander 2025-02-03 21:28 9814
    }
1193b4be Poul Sander 2025-02-03 21:28 9815
    else if (flags & ImGuiInputFlags_RouteActive)
1193b4be Poul Sander 2025-02-03 21:28 9816
    {
1193b4be Poul Sander 2025-02-03 21:28 9817
        if (owner_id != 0 && g.ActiveId == owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9818
            return 1;
1193b4be Poul Sander 2025-02-03 21:28 9819
        return 255;
1193b4be Poul Sander 2025-02-03 21:28 9820
    }
1193b4be Poul Sander 2025-02-03 21:28 9821
    else if (flags & ImGuiInputFlags_RouteGlobal)
1193b4be Poul Sander 2025-02-03 21:28 9822
    {
1193b4be Poul Sander 2025-02-03 21:28 9823
        if (flags & ImGuiInputFlags_RouteOverActive)
1193b4be Poul Sander 2025-02-03 21:28 9824
            return 0;
1193b4be Poul Sander 2025-02-03 21:28 9825
        if (flags & ImGuiInputFlags_RouteOverFocused)
1193b4be Poul Sander 2025-02-03 21:28 9826
            return 2;
1193b4be Poul Sander 2025-02-03 21:28 9827
        return 254;
1193b4be Poul Sander 2025-02-03 21:28 9828
    }
1193b4be Poul Sander 2025-02-03 21:28 9829
    IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 9830
    return 0;
1193b4be Poul Sander 2025-02-03 21:28 9831
}
1193b4be Poul Sander 2025-02-03 21:28 9832
1193b4be Poul Sander 2025-02-03 21:28 9833
// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
1193b4be Poul Sander 2025-02-03 21:28 9834
//   e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
1193b4be Poul Sander 2025-02-03 21:28 9835
// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character.
1193b4be Poul Sander 2025-02-03 21:28 9836
static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
1193b4be Poul Sander 2025-02-03 21:28 9837
{
1193b4be Poul Sander 2025-02-03 21:28 9838
    // Mimic 'ignore_char_inputs' logic in InputText()
1193b4be Poul Sander 2025-02-03 21:28 9839
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9840
1193b4be Poul Sander 2025-02-03 21:28 9841
    // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
1193b4be Poul Sander 2025-02-03 21:28 9842
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9843
    const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
1193b4be Poul Sander 2025-02-03 21:28 9844
    if (ignore_char_inputs)
1193b4be Poul Sander 2025-02-03 21:28 9845
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9846
1193b4be Poul Sander 2025-02-03 21:28 9847
    // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
1193b4be Poul Sander 2025-02-03 21:28 9848
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9849
    if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 9850
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9851
    return g.KeysMayBeCharInput.TestBit(key);
1193b4be Poul Sander 2025-02-03 21:28 9852
}
1193b4be Poul Sander 2025-02-03 21:28 9853
1193b4be Poul Sander 2025-02-03 21:28 9854
// Request a desired route for an input chord (key + mods).
1193b4be Poul Sander 2025-02-03 21:28 9855
// Return true if the route is available this frame.
1193b4be Poul Sander 2025-02-03 21:28 9856
// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
1193b4be Poul Sander 2025-02-03 21:28 9857
//   (Conceptually this does a "Submit for next frame" + "Test for current frame".
1193b4be Poul Sander 2025-02-03 21:28 9858
//   As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
1193b4be Poul Sander 2025-02-03 21:28 9859
bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9860
{
1193b4be Poul Sander 2025-02-03 21:28 9861
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9862
    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
1193b4be Poul Sander 2025-02-03 21:28 9863
        flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
1193b4be Poul Sander 2025-02-03 21:28 9864
    else
1193b4be Poul Sander 2025-02-03 21:28 9865
        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
1193b4be Poul Sander 2025-02-03 21:28 9866
    IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
1193b4be Poul Sander 2025-02-03 21:28 9867
    if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
1193b4be Poul Sander 2025-02-03 21:28 9868
        IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
1193b4be Poul Sander 2025-02-03 21:28 9869
1193b4be Poul Sander 2025-02-03 21:28 9870
    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
1193b4be Poul Sander 2025-02-03 21:28 9871
    key_chord = FixupKeyChord(key_chord);
1193b4be Poul Sander 2025-02-03 21:28 9872
1193b4be Poul Sander 2025-02-03 21:28 9873
    // [DEBUG] Debug break requested by user
1193b4be Poul Sander 2025-02-03 21:28 9874
    if (g.DebugBreakInShortcutRouting == key_chord)
1193b4be Poul Sander 2025-02-03 21:28 9875
        IM_DEBUG_BREAK();
1193b4be Poul Sander 2025-02-03 21:28 9876
1193b4be Poul Sander 2025-02-03 21:28 9877
    if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
1193b4be Poul Sander 2025-02-03 21:28 9878
        if (g.NavWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 9879
            return false;
1193b4be Poul Sander 2025-02-03 21:28 9880
1193b4be Poul Sander 2025-02-03 21:28 9881
    // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
1193b4be Poul Sander 2025-02-03 21:28 9882
    if (flags & ImGuiInputFlags_RouteAlways)
1193b4be Poul Sander 2025-02-03 21:28 9883
    {
1193b4be Poul Sander 2025-02-03 21:28 9884
        IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 9885
        return true;
1193b4be Poul Sander 2025-02-03 21:28 9886
    }
1193b4be Poul Sander 2025-02-03 21:28 9887
1193b4be Poul Sander 2025-02-03 21:28 9888
    // Specific culling when there's an active item.
1193b4be Poul Sander 2025-02-03 21:28 9889
    if (g.ActiveId != 0 && g.ActiveId != owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9890
    {
1193b4be Poul Sander 2025-02-03 21:28 9891
        if (flags & ImGuiInputFlags_RouteActive)
1193b4be Poul Sander 2025-02-03 21:28 9892
            return false;
1193b4be Poul Sander 2025-02-03 21:28 9893
1193b4be Poul Sander 2025-02-03 21:28 9894
        // Cull shortcuts with no modifiers when it could generate a character.
1193b4be Poul Sander 2025-02-03 21:28 9895
        // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
1193b4be Poul Sander 2025-02-03 21:28 9896
        // but  Shortcut(Ctrl+G) should generally trigger when InputText() is active.
1193b4be Poul Sander 2025-02-03 21:28 9897
        // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
1193b4be Poul Sander 2025-02-03 21:28 9898
        // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
1193b4be Poul Sander 2025-02-03 21:28 9899
        if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
1193b4be Poul Sander 2025-02-03 21:28 9900
        {
1193b4be Poul Sander 2025-02-03 21:28 9901
            IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 9902
            return false;
1193b4be Poul Sander 2025-02-03 21:28 9903
        }
1193b4be Poul Sander 2025-02-03 21:28 9904
1193b4be Poul Sander 2025-02-03 21:28 9905
        // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
1193b4be Poul Sander 2025-02-03 21:28 9906
        if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
1193b4be Poul Sander 2025-02-03 21:28 9907
        {
1193b4be Poul Sander 2025-02-03 21:28 9908
            ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 9909
            if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 9910
                key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
1193b4be Poul Sander 2025-02-03 21:28 9911
            if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
1193b4be Poul Sander 2025-02-03 21:28 9912
                return false;
1193b4be Poul Sander 2025-02-03 21:28 9913
        }
1193b4be Poul Sander 2025-02-03 21:28 9914
    }
1193b4be Poul Sander 2025-02-03 21:28 9915
1193b4be Poul Sander 2025-02-03 21:28 9916
    // Where do we evaluate route for?
1193b4be Poul Sander 2025-02-03 21:28 9917
    ImGuiID focus_scope_id = g.CurrentFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 9918
    if (flags & ImGuiInputFlags_RouteFromRootWindow)
1193b4be Poul Sander 2025-02-03 21:28 9919
        focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
1193b4be Poul Sander 2025-02-03 21:28 9920
1193b4be Poul Sander 2025-02-03 21:28 9921
    const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
1193b4be Poul Sander 2025-02-03 21:28 9922
    IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
1193b4be Poul Sander 2025-02-03 21:28 9923
    if (score == 255)
1193b4be Poul Sander 2025-02-03 21:28 9924
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9925
1193b4be Poul Sander 2025-02-03 21:28 9926
    // Submit routing for NEXT frame (assuming score is sufficient)
1193b4be Poul Sander 2025-02-03 21:28 9927
    // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
1193b4be Poul Sander 2025-02-03 21:28 9928
    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
1193b4be Poul Sander 2025-02-03 21:28 9929
    //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
1193b4be Poul Sander 2025-02-03 21:28 9930
    if (score < routing_data->RoutingNextScore)
1193b4be Poul Sander 2025-02-03 21:28 9931
    {
1193b4be Poul Sander 2025-02-03 21:28 9932
        routing_data->RoutingNext = owner_id;
1193b4be Poul Sander 2025-02-03 21:28 9933
        routing_data->RoutingNextScore = (ImU8)score;
1193b4be Poul Sander 2025-02-03 21:28 9934
    }
1193b4be Poul Sander 2025-02-03 21:28 9935
1193b4be Poul Sander 2025-02-03 21:28 9936
    // Return routing state for CURRENT frame
1193b4be Poul Sander 2025-02-03 21:28 9937
    if (routing_data->RoutingCurr == owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9938
        IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
1193b4be Poul Sander 2025-02-03 21:28 9939
    return routing_data->RoutingCurr == owner_id;
1193b4be Poul Sander 2025-02-03 21:28 9940
}
1193b4be Poul Sander 2025-02-03 21:28 9941
1193b4be Poul Sander 2025-02-03 21:28 9942
// Currently unused by core (but used by tests)
1193b4be Poul Sander 2025-02-03 21:28 9943
// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
1193b4be Poul Sander 2025-02-03 21:28 9944
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9945
{
1193b4be Poul Sander 2025-02-03 21:28 9946
    const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
1193b4be Poul Sander 2025-02-03 21:28 9947
    key_chord = FixupKeyChord(key_chord);
1193b4be Poul Sander 2025-02-03 21:28 9948
    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
1193b4be Poul Sander 2025-02-03 21:28 9949
    return routing_data->RoutingCurr == routing_id;
1193b4be Poul Sander 2025-02-03 21:28 9950
}
1193b4be Poul Sander 2025-02-03 21:28 9951
1193b4be Poul Sander 2025-02-03 21:28 9952
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
1193b4be Poul Sander 2025-02-03 21:28 9953
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
1193b4be Poul Sander 2025-02-03 21:28 9954
bool ImGui::IsKeyDown(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 9955
{
1193b4be Poul Sander 2025-02-03 21:28 9956
    return IsKeyDown(key, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 9957
}
1193b4be Poul Sander 2025-02-03 21:28 9958
1193b4be Poul Sander 2025-02-03 21:28 9959
bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9960
{
1193b4be Poul Sander 2025-02-03 21:28 9961
    const ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 9962
    if (!key_data->Down)
1193b4be Poul Sander 2025-02-03 21:28 9963
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9964
    if (!TestKeyOwner(key, owner_id))
1193b4be Poul Sander 2025-02-03 21:28 9965
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9966
    return true;
1193b4be Poul Sander 2025-02-03 21:28 9967
}
1193b4be Poul Sander 2025-02-03 21:28 9968
1193b4be Poul Sander 2025-02-03 21:28 9969
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
1193b4be Poul Sander 2025-02-03 21:28 9970
{
1193b4be Poul Sander 2025-02-03 21:28 9971
    return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 9972
}
1193b4be Poul Sander 2025-02-03 21:28 9973
1193b4be Poul Sander 2025-02-03 21:28 9974
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
1193b4be Poul Sander 2025-02-03 21:28 9975
bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 9976
{
1193b4be Poul Sander 2025-02-03 21:28 9977
    const ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 9978
    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
1193b4be Poul Sander 2025-02-03 21:28 9979
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9980
    const float t = key_data->DownDuration;
1193b4be Poul Sander 2025-02-03 21:28 9981
    if (t < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 9982
        return false;
1193b4be Poul Sander 2025-02-03 21:28 9983
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
1193b4be Poul Sander 2025-02-03 21:28 9984
    if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
1193b4be Poul Sander 2025-02-03 21:28 9985
        flags |= ImGuiInputFlags_Repeat;
1193b4be Poul Sander 2025-02-03 21:28 9986
1193b4be Poul Sander 2025-02-03 21:28 9987
    bool pressed = (t == 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 9988
    if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
1193b4be Poul Sander 2025-02-03 21:28 9989
    {
1193b4be Poul Sander 2025-02-03 21:28 9990
        float repeat_delay, repeat_rate;
1193b4be Poul Sander 2025-02-03 21:28 9991
        GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 9992
        pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
1193b4be Poul Sander 2025-02-03 21:28 9993
        if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
1193b4be Poul Sander 2025-02-03 21:28 9994
        {
1193b4be Poul Sander 2025-02-03 21:28 9995
            // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
1193b4be Poul Sander 2025-02-03 21:28 9996
            // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
1193b4be Poul Sander 2025-02-03 21:28 9997
            ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 9998
            double key_pressed_time = g.Time - t + 0.00001f;
1193b4be Poul Sander 2025-02-03 21:28 9999
            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
1193b4be Poul Sander 2025-02-03 21:28 10000
                pressed = false;
1193b4be Poul Sander 2025-02-03 21:28 10001
            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
1193b4be Poul Sander 2025-02-03 21:28 10002
                pressed = false;
1193b4be Poul Sander 2025-02-03 21:28 10003
            if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
1193b4be Poul Sander 2025-02-03 21:28 10004
                pressed = false;
1193b4be Poul Sander 2025-02-03 21:28 10005
        }
1193b4be Poul Sander 2025-02-03 21:28 10006
    }
1193b4be Poul Sander 2025-02-03 21:28 10007
    if (!pressed)
1193b4be Poul Sander 2025-02-03 21:28 10008
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10009
    if (!TestKeyOwner(key, owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10010
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10011
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10012
}
1193b4be Poul Sander 2025-02-03 21:28 10013
1193b4be Poul Sander 2025-02-03 21:28 10014
bool ImGui::IsKeyReleased(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 10015
{
1193b4be Poul Sander 2025-02-03 21:28 10016
    return IsKeyReleased(key, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 10017
}
1193b4be Poul Sander 2025-02-03 21:28 10018
1193b4be Poul Sander 2025-02-03 21:28 10019
bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10020
{
1193b4be Poul Sander 2025-02-03 21:28 10021
    const ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 10022
    if (key_data->DownDurationPrev < 0.0f || key_data->Down)
1193b4be Poul Sander 2025-02-03 21:28 10023
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10024
    if (!TestKeyOwner(key, owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10025
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10026
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10027
}
1193b4be Poul Sander 2025-02-03 21:28 10028
1193b4be Poul Sander 2025-02-03 21:28 10029
bool ImGui::IsMouseDown(ImGuiMouseButton button)
1193b4be Poul Sander 2025-02-03 21:28 10030
{
1193b4be Poul Sander 2025-02-03 21:28 10031
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10032
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10033
    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
1193b4be Poul Sander 2025-02-03 21:28 10034
}
1193b4be Poul Sander 2025-02-03 21:28 10035
1193b4be Poul Sander 2025-02-03 21:28 10036
bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10037
{
1193b4be Poul Sander 2025-02-03 21:28 10038
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10039
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10040
    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
1193b4be Poul Sander 2025-02-03 21:28 10041
}
1193b4be Poul Sander 2025-02-03 21:28 10042
1193b4be Poul Sander 2025-02-03 21:28 10043
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
1193b4be Poul Sander 2025-02-03 21:28 10044
{
1193b4be Poul Sander 2025-02-03 21:28 10045
    return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 10046
}
1193b4be Poul Sander 2025-02-03 21:28 10047
1193b4be Poul Sander 2025-02-03 21:28 10048
bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10049
{
1193b4be Poul Sander 2025-02-03 21:28 10050
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10051
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10052
    if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
1193b4be Poul Sander 2025-02-03 21:28 10053
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10054
    const float t = g.IO.MouseDownDuration[button];
1193b4be Poul Sander 2025-02-03 21:28 10055
    if (t < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10056
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10057
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
1193b4be Poul Sander 2025-02-03 21:28 10058
1193b4be Poul Sander 2025-02-03 21:28 10059
    const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10060
    const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
1193b4be Poul Sander 2025-02-03 21:28 10061
    if (!pressed)
1193b4be Poul Sander 2025-02-03 21:28 10062
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10063
1193b4be Poul Sander 2025-02-03 21:28 10064
    if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10065
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10066
1193b4be Poul Sander 2025-02-03 21:28 10067
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10068
}
1193b4be Poul Sander 2025-02-03 21:28 10069
1193b4be Poul Sander 2025-02-03 21:28 10070
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
1193b4be Poul Sander 2025-02-03 21:28 10071
{
1193b4be Poul Sander 2025-02-03 21:28 10072
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10073
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10074
    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
1193b4be Poul Sander 2025-02-03 21:28 10075
}
1193b4be Poul Sander 2025-02-03 21:28 10076
1193b4be Poul Sander 2025-02-03 21:28 10077
bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10078
{
1193b4be Poul Sander 2025-02-03 21:28 10079
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10080
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10081
    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10082
}
1193b4be Poul Sander 2025-02-03 21:28 10083
1193b4be Poul Sander 2025-02-03 21:28 10084
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
1193b4be Poul Sander 2025-02-03 21:28 10085
{
1193b4be Poul Sander 2025-02-03 21:28 10086
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10087
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10088
    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 10089
}
1193b4be Poul Sander 2025-02-03 21:28 10090
1193b4be Poul Sander 2025-02-03 21:28 10091
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10092
{
1193b4be Poul Sander 2025-02-03 21:28 10093
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10094
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10095
    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
1193b4be Poul Sander 2025-02-03 21:28 10096
}
1193b4be Poul Sander 2025-02-03 21:28 10097
1193b4be Poul Sander 2025-02-03 21:28 10098
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
1193b4be Poul Sander 2025-02-03 21:28 10099
{
1193b4be Poul Sander 2025-02-03 21:28 10100
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10101
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10102
    return g.IO.MouseClickedCount[button];
1193b4be Poul Sander 2025-02-03 21:28 10103
}
1193b4be Poul Sander 2025-02-03 21:28 10104
1193b4be Poul Sander 2025-02-03 21:28 10105
// Test if mouse cursor is hovering given rectangle
1193b4be Poul Sander 2025-02-03 21:28 10106
// NB- Rectangle is clipped by our current clip setting
1193b4be Poul Sander 2025-02-03 21:28 10107
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
1193b4be Poul Sander 2025-02-03 21:28 10108
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
1193b4be Poul Sander 2025-02-03 21:28 10109
{
1193b4be Poul Sander 2025-02-03 21:28 10110
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10111
1193b4be Poul Sander 2025-02-03 21:28 10112
    // Clip
1193b4be Poul Sander 2025-02-03 21:28 10113
    ImRect rect_clipped(r_min, r_max);
1193b4be Poul Sander 2025-02-03 21:28 10114
    if (clip)
1193b4be Poul Sander 2025-02-03 21:28 10115
        rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
1193b4be Poul Sander 2025-02-03 21:28 10116
1193b4be Poul Sander 2025-02-03 21:28 10117
    // Hit testing, expanded for touch input
1193b4be Poul Sander 2025-02-03 21:28 10118
    if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
1193b4be Poul Sander 2025-02-03 21:28 10119
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10120
    if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
1193b4be Poul Sander 2025-02-03 21:28 10121
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10122
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10123
}
1193b4be Poul Sander 2025-02-03 21:28 10124
1193b4be Poul Sander 2025-02-03 21:28 10125
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
1193b4be Poul Sander 2025-02-03 21:28 10126
// [Internal] This doesn't test if the button is pressed
1193b4be Poul Sander 2025-02-03 21:28 10127
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
1193b4be Poul Sander 2025-02-03 21:28 10128
{
1193b4be Poul Sander 2025-02-03 21:28 10129
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10130
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10131
    if (lock_threshold < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10132
        lock_threshold = g.IO.MouseDragThreshold;
1193b4be Poul Sander 2025-02-03 21:28 10133
    return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
1193b4be Poul Sander 2025-02-03 21:28 10134
}
1193b4be Poul Sander 2025-02-03 21:28 10135
1193b4be Poul Sander 2025-02-03 21:28 10136
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
1193b4be Poul Sander 2025-02-03 21:28 10137
{
1193b4be Poul Sander 2025-02-03 21:28 10138
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10139
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10140
    if (!g.IO.MouseDown[button])
1193b4be Poul Sander 2025-02-03 21:28 10141
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10142
    return IsMouseDragPastThreshold(button, lock_threshold);
1193b4be Poul Sander 2025-02-03 21:28 10143
}
1193b4be Poul Sander 2025-02-03 21:28 10144
1193b4be Poul Sander 2025-02-03 21:28 10145
ImVec2 ImGui::GetMousePos()
1193b4be Poul Sander 2025-02-03 21:28 10146
{
1193b4be Poul Sander 2025-02-03 21:28 10147
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10148
    return g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10149
}
1193b4be Poul Sander 2025-02-03 21:28 10150
1193b4be Poul Sander 2025-02-03 21:28 10151
// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
1193b4be Poul Sander 2025-02-03 21:28 10152
// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
1193b4be Poul Sander 2025-02-03 21:28 10153
void ImGui::TeleportMousePos(const ImVec2& pos)
1193b4be Poul Sander 2025-02-03 21:28 10154
{
1193b4be Poul Sander 2025-02-03 21:28 10155
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10156
    g.IO.MousePos = g.IO.MousePosPrev = pos;
1193b4be Poul Sander 2025-02-03 21:28 10157
    g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10158
    g.IO.WantSetMousePos = true;
1193b4be Poul Sander 2025-02-03 21:28 10159
    //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
1193b4be Poul Sander 2025-02-03 21:28 10160
}
1193b4be Poul Sander 2025-02-03 21:28 10161
1193b4be Poul Sander 2025-02-03 21:28 10162
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
1193b4be Poul Sander 2025-02-03 21:28 10163
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
1193b4be Poul Sander 2025-02-03 21:28 10164
{
1193b4be Poul Sander 2025-02-03 21:28 10165
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10166
    if (g.BeginPopupStack.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 10167
        return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
1193b4be Poul Sander 2025-02-03 21:28 10168
    return g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10169
}
1193b4be Poul Sander 2025-02-03 21:28 10170
1193b4be Poul Sander 2025-02-03 21:28 10171
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
1193b4be Poul Sander 2025-02-03 21:28 10172
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
1193b4be Poul Sander 2025-02-03 21:28 10173
{
1193b4be Poul Sander 2025-02-03 21:28 10174
    // The assert is only to silence a false-positive in XCode Static Analysis.
1193b4be Poul Sander 2025-02-03 21:28 10175
    // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
1193b4be Poul Sander 2025-02-03 21:28 10176
    IM_ASSERT(GImGui != NULL);
1193b4be Poul Sander 2025-02-03 21:28 10177
    const float MOUSE_INVALID = -256000.0f;
1193b4be Poul Sander 2025-02-03 21:28 10178
    ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10179
    return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
1193b4be Poul Sander 2025-02-03 21:28 10180
}
1193b4be Poul Sander 2025-02-03 21:28 10181
1193b4be Poul Sander 2025-02-03 21:28 10182
// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
1193b4be Poul Sander 2025-02-03 21:28 10183
bool ImGui::IsAnyMouseDown()
1193b4be Poul Sander 2025-02-03 21:28 10184
{
1193b4be Poul Sander 2025-02-03 21:28 10185
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10186
    for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
1193b4be Poul Sander 2025-02-03 21:28 10187
        if (g.IO.MouseDown[n])
1193b4be Poul Sander 2025-02-03 21:28 10188
            return true;
1193b4be Poul Sander 2025-02-03 21:28 10189
    return false;
1193b4be Poul Sander 2025-02-03 21:28 10190
}
1193b4be Poul Sander 2025-02-03 21:28 10191
1193b4be Poul Sander 2025-02-03 21:28 10192
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
1193b4be Poul Sander 2025-02-03 21:28 10193
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
1193b4be Poul Sander 2025-02-03 21:28 10194
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
1193b4be Poul Sander 2025-02-03 21:28 10195
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
1193b4be Poul Sander 2025-02-03 21:28 10196
{
1193b4be Poul Sander 2025-02-03 21:28 10197
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10198
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10199
    if (lock_threshold < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10200
        lock_threshold = g.IO.MouseDragThreshold;
1193b4be Poul Sander 2025-02-03 21:28 10201
    if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
1193b4be Poul Sander 2025-02-03 21:28 10202
        if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
1193b4be Poul Sander 2025-02-03 21:28 10203
            if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
1193b4be Poul Sander 2025-02-03 21:28 10204
                return g.IO.MousePos - g.IO.MouseClickedPos[button];
1193b4be Poul Sander 2025-02-03 21:28 10205
    return ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10206
}
1193b4be Poul Sander 2025-02-03 21:28 10207
1193b4be Poul Sander 2025-02-03 21:28 10208
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
1193b4be Poul Sander 2025-02-03 21:28 10209
{
1193b4be Poul Sander 2025-02-03 21:28 10210
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10211
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
1193b4be Poul Sander 2025-02-03 21:28 10212
    // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
1193b4be Poul Sander 2025-02-03 21:28 10213
    g.IO.MouseClickedPos[button] = g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10214
}
1193b4be Poul Sander 2025-02-03 21:28 10215
1193b4be Poul Sander 2025-02-03 21:28 10216
// Get desired mouse cursor shape.
1193b4be Poul Sander 2025-02-03 21:28 10217
// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
1193b4be Poul Sander 2025-02-03 21:28 10218
// updated during the frame, and locked in EndFrame()/Render().
1193b4be Poul Sander 2025-02-03 21:28 10219
// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
1193b4be Poul Sander 2025-02-03 21:28 10220
ImGuiMouseCursor ImGui::GetMouseCursor()
1193b4be Poul Sander 2025-02-03 21:28 10221
{
1193b4be Poul Sander 2025-02-03 21:28 10222
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10223
    return g.MouseCursor;
1193b4be Poul Sander 2025-02-03 21:28 10224
}
1193b4be Poul Sander 2025-02-03 21:28 10225
1193b4be Poul Sander 2025-02-03 21:28 10226
// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
1193b4be Poul Sander 2025-02-03 21:28 10227
// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
1193b4be Poul Sander 2025-02-03 21:28 10228
// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
1193b4be Poul Sander 2025-02-03 21:28 10229
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
1193b4be Poul Sander 2025-02-03 21:28 10230
{
1193b4be Poul Sander 2025-02-03 21:28 10231
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10232
    g.MouseCursor = cursor_type;
1193b4be Poul Sander 2025-02-03 21:28 10233
}
1193b4be Poul Sander 2025-02-03 21:28 10234
1193b4be Poul Sander 2025-02-03 21:28 10235
static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
1193b4be Poul Sander 2025-02-03 21:28 10236
{
1193b4be Poul Sander 2025-02-03 21:28 10237
    IM_ASSERT(ImGui::IsAliasKey(key));
1193b4be Poul Sander 2025-02-03 21:28 10238
    ImGuiKeyData* key_data = ImGui::GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 10239
    key_data->Down = v;
1193b4be Poul Sander 2025-02-03 21:28 10240
    key_data->AnalogValue = analog_value;
1193b4be Poul Sander 2025-02-03 21:28 10241
}
1193b4be Poul Sander 2025-02-03 21:28 10242
1193b4be Poul Sander 2025-02-03 21:28 10243
// [Internal] Do not use directly
1193b4be Poul Sander 2025-02-03 21:28 10244
static ImGuiKeyChord GetMergedModsFromKeys()
1193b4be Poul Sander 2025-02-03 21:28 10245
{
1193b4be Poul Sander 2025-02-03 21:28 10246
    ImGuiKeyChord mods = 0;
1193b4be Poul Sander 2025-02-03 21:28 10247
    if (ImGui::IsKeyDown(ImGuiMod_Ctrl))     { mods |= ImGuiMod_Ctrl; }
1193b4be Poul Sander 2025-02-03 21:28 10248
    if (ImGui::IsKeyDown(ImGuiMod_Shift))    { mods |= ImGuiMod_Shift; }
1193b4be Poul Sander 2025-02-03 21:28 10249
    if (ImGui::IsKeyDown(ImGuiMod_Alt))      { mods |= ImGuiMod_Alt; }
1193b4be Poul Sander 2025-02-03 21:28 10250
    if (ImGui::IsKeyDown(ImGuiMod_Super))    { mods |= ImGuiMod_Super; }
1193b4be Poul Sander 2025-02-03 21:28 10251
    return mods;
1193b4be Poul Sander 2025-02-03 21:28 10252
}
1193b4be Poul Sander 2025-02-03 21:28 10253
1193b4be Poul Sander 2025-02-03 21:28 10254
static void ImGui::UpdateKeyboardInputs()
1193b4be Poul Sander 2025-02-03 21:28 10255
{
1193b4be Poul Sander 2025-02-03 21:28 10256
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10257
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 10258
1193b4be Poul Sander 2025-02-03 21:28 10259
    if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
1193b4be Poul Sander 2025-02-03 21:28 10260
        io.ClearInputKeys();
1193b4be Poul Sander 2025-02-03 21:28 10261
1193b4be Poul Sander 2025-02-03 21:28 10262
    // Import legacy keys or verify they are not used
1193b4be Poul Sander 2025-02-03 21:28 10263
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 10264
    if (io.BackendUsingLegacyKeyArrays == 0)
1193b4be Poul Sander 2025-02-03 21:28 10265
    {
1193b4be Poul Sander 2025-02-03 21:28 10266
        // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
1193b4be Poul Sander 2025-02-03 21:28 10267
        for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
1193b4be Poul Sander 2025-02-03 21:28 10268
            IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1193b4be Poul Sander 2025-02-03 21:28 10269
    }
1193b4be Poul Sander 2025-02-03 21:28 10270
    else
1193b4be Poul Sander 2025-02-03 21:28 10271
    {
1193b4be Poul Sander 2025-02-03 21:28 10272
        if (g.FrameCount == 0)
1193b4be Poul Sander 2025-02-03 21:28 10273
            for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
1193b4be Poul Sander 2025-02-03 21:28 10274
                IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
1193b4be Poul Sander 2025-02-03 21:28 10275
1193b4be Poul Sander 2025-02-03 21:28 10276
        // Build reverse KeyMap (Named -> Legacy)
1193b4be Poul Sander 2025-02-03 21:28 10277
        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
1193b4be Poul Sander 2025-02-03 21:28 10278
            if (io.KeyMap[n] != -1)
1193b4be Poul Sander 2025-02-03 21:28 10279
            {
1193b4be Poul Sander 2025-02-03 21:28 10280
                IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
1193b4be Poul Sander 2025-02-03 21:28 10281
                io.KeyMap[io.KeyMap[n]] = n;
1193b4be Poul Sander 2025-02-03 21:28 10282
            }
1193b4be Poul Sander 2025-02-03 21:28 10283
1193b4be Poul Sander 2025-02-03 21:28 10284
        // Import legacy keys into new ones
1193b4be Poul Sander 2025-02-03 21:28 10285
        for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
1193b4be Poul Sander 2025-02-03 21:28 10286
            if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
1193b4be Poul Sander 2025-02-03 21:28 10287
            {
1193b4be Poul Sander 2025-02-03 21:28 10288
                const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
1193b4be Poul Sander 2025-02-03 21:28 10289
                IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
1193b4be Poul Sander 2025-02-03 21:28 10290
                io.KeysData[key].Down = io.KeysDown[n];
1193b4be Poul Sander 2025-02-03 21:28 10291
                if (key != n)
1193b4be Poul Sander 2025-02-03 21:28 10292
                    io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
1193b4be Poul Sander 2025-02-03 21:28 10293
                io.BackendUsingLegacyKeyArrays = 1;
1193b4be Poul Sander 2025-02-03 21:28 10294
            }
1193b4be Poul Sander 2025-02-03 21:28 10295
        if (io.BackendUsingLegacyKeyArrays == 1)
1193b4be Poul Sander 2025-02-03 21:28 10296
        {
1193b4be Poul Sander 2025-02-03 21:28 10297
            GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;
1193b4be Poul Sander 2025-02-03 21:28 10298
            GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;
1193b4be Poul Sander 2025-02-03 21:28 10299
            GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;
1193b4be Poul Sander 2025-02-03 21:28 10300
            GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
1193b4be Poul Sander 2025-02-03 21:28 10301
        }
1193b4be Poul Sander 2025-02-03 21:28 10302
    }
1193b4be Poul Sander 2025-02-03 21:28 10303
#endif
1193b4be Poul Sander 2025-02-03 21:28 10304
1193b4be Poul Sander 2025-02-03 21:28 10305
    // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
1193b4be Poul Sander 2025-02-03 21:28 10306
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 10307
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10308
    if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
1193b4be Poul Sander 2025-02-03 21:28 10309
    {
1193b4be Poul Sander 2025-02-03 21:28 10310
        #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1)           do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)
1193b4be Poul Sander 2025-02-03 21:28 10311
        #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2)    do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)
1193b4be Poul Sander 2025-02-03 21:28 10312
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
1193b4be Poul Sander 2025-02-03 21:28 10313
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
1193b4be Poul Sander 2025-02-03 21:28 10314
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
1193b4be Poul Sander 2025-02-03 21:28 10315
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
1193b4be Poul Sander 2025-02-03 21:28 10316
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
1193b4be Poul Sander 2025-02-03 21:28 10317
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
1193b4be Poul Sander 2025-02-03 21:28 10318
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
1193b4be Poul Sander 2025-02-03 21:28 10319
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
1193b4be Poul Sander 2025-02-03 21:28 10320
        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
1193b4be Poul Sander 2025-02-03 21:28 10321
        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
1193b4be Poul Sander 2025-02-03 21:28 10322
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
1193b4be Poul Sander 2025-02-03 21:28 10323
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
1193b4be Poul Sander 2025-02-03 21:28 10324
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
1193b4be Poul Sander 2025-02-03 21:28 10325
        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
1193b4be Poul Sander 2025-02-03 21:28 10326
        #undef NAV_MAP_KEY
1193b4be Poul Sander 2025-02-03 21:28 10327
    }
1193b4be Poul Sander 2025-02-03 21:28 10328
#endif
1193b4be Poul Sander 2025-02-03 21:28 10329
1193b4be Poul Sander 2025-02-03 21:28 10330
    // Update aliases
1193b4be Poul Sander 2025-02-03 21:28 10331
    for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
1193b4be Poul Sander 2025-02-03 21:28 10332
        UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10333
    UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
1193b4be Poul Sander 2025-02-03 21:28 10334
    UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
1193b4be Poul Sander 2025-02-03 21:28 10335
1193b4be Poul Sander 2025-02-03 21:28 10336
    // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
1193b4be Poul Sander 2025-02-03 21:28 10337
    // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) ->                                      -> (here) deriving io.KeyMods + io.KeyXXX from key array.
1193b4be Poul Sander 2025-02-03 21:28 10338
    // - Legacy backends:      set io.KeyXXX bools               -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
1193b4be Poul Sander 2025-02-03 21:28 10339
    // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
1193b4be Poul Sander 2025-02-03 21:28 10340
    const ImGuiKeyChord prev_key_mods = io.KeyMods;
1193b4be Poul Sander 2025-02-03 21:28 10341
    io.KeyMods = GetMergedModsFromKeys();
1193b4be Poul Sander 2025-02-03 21:28 10342
    io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10343
    io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10344
    io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10345
    io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10346
    if (prev_key_mods != io.KeyMods)
1193b4be Poul Sander 2025-02-03 21:28 10347
        g.LastKeyModsChangeTime = g.Time;
1193b4be Poul Sander 2025-02-03 21:28 10348
    if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
1193b4be Poul Sander 2025-02-03 21:28 10349
        g.LastKeyModsChangeFromNoneTime = g.Time;
1193b4be Poul Sander 2025-02-03 21:28 10350
1193b4be Poul Sander 2025-02-03 21:28 10351
    // Clear gamepad data if disabled
1193b4be Poul Sander 2025-02-03 21:28 10352
    if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
1193b4be Poul Sander 2025-02-03 21:28 10353
        for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
1193b4be Poul Sander 2025-02-03 21:28 10354
        {
1193b4be Poul Sander 2025-02-03 21:28 10355
            io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;
1193b4be Poul Sander 2025-02-03 21:28 10356
            io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10357
        }
1193b4be Poul Sander 2025-02-03 21:28 10358
1193b4be Poul Sander 2025-02-03 21:28 10359
    // Update keys
1193b4be Poul Sander 2025-02-03 21:28 10360
    for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
1193b4be Poul Sander 2025-02-03 21:28 10361
    {
1193b4be Poul Sander 2025-02-03 21:28 10362
        ImGuiKeyData* key_data = &io.KeysData[i];
1193b4be Poul Sander 2025-02-03 21:28 10363
        key_data->DownDurationPrev = key_data->DownDuration;
1193b4be Poul Sander 2025-02-03 21:28 10364
        key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 10365
        if (key_data->DownDuration == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10366
        {
1193b4be Poul Sander 2025-02-03 21:28 10367
            ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
1193b4be Poul Sander 2025-02-03 21:28 10368
            if (IsKeyboardKey(key))
1193b4be Poul Sander 2025-02-03 21:28 10369
                g.LastKeyboardKeyPressTime = g.Time;
1193b4be Poul Sander 2025-02-03 21:28 10370
            else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
1193b4be Poul Sander 2025-02-03 21:28 10371
                g.LastKeyboardKeyPressTime = g.Time;
1193b4be Poul Sander 2025-02-03 21:28 10372
        }
1193b4be Poul Sander 2025-02-03 21:28 10373
    }
1193b4be Poul Sander 2025-02-03 21:28 10374
1193b4be Poul Sander 2025-02-03 21:28 10375
    // Update keys/input owner (named keys only): one entry per key
1193b4be Poul Sander 2025-02-03 21:28 10376
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 10377
    {
1193b4be Poul Sander 2025-02-03 21:28 10378
        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
1193b4be Poul Sander 2025-02-03 21:28 10379
        ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
1193b4be Poul Sander 2025-02-03 21:28 10380
        owner_data->OwnerCurr = owner_data->OwnerNext;
1193b4be Poul Sander 2025-02-03 21:28 10381
        if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
1193b4be Poul Sander 2025-02-03 21:28 10382
            owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
1193b4be Poul Sander 2025-02-03 21:28 10383
        owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down;  // Clear LockUntilRelease when key is not Down anymore
1193b4be Poul Sander 2025-02-03 21:28 10384
    }
1193b4be Poul Sander 2025-02-03 21:28 10385
1193b4be Poul Sander 2025-02-03 21:28 10386
    // Update key routing (for e.g. shortcuts)
1193b4be Poul Sander 2025-02-03 21:28 10387
    UpdateKeyRoutingTable(&g.KeysRoutingTable);
1193b4be Poul Sander 2025-02-03 21:28 10388
}
1193b4be Poul Sander 2025-02-03 21:28 10389
1193b4be Poul Sander 2025-02-03 21:28 10390
static void ImGui::UpdateMouseInputs()
1193b4be Poul Sander 2025-02-03 21:28 10391
{
1193b4be Poul Sander 2025-02-03 21:28 10392
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10393
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 10394
1193b4be Poul Sander 2025-02-03 21:28 10395
    // Mouse Wheel swapping flag
1193b4be Poul Sander 2025-02-03 21:28 10396
    // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
1193b4be Poul Sander 2025-02-03 21:28 10397
    // - We avoid doing it on OSX as it the OS input layer handles this already.
1193b4be Poul Sander 2025-02-03 21:28 10398
    // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
1193b4be Poul Sander 2025-02-03 21:28 10399
    // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
1193b4be Poul Sander 2025-02-03 21:28 10400
    io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
1193b4be Poul Sander 2025-02-03 21:28 10401
1193b4be Poul Sander 2025-02-03 21:28 10402
    // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
1193b4be Poul Sander 2025-02-03 21:28 10403
    if (IsMousePosValid(&io.MousePos))
1193b4be Poul Sander 2025-02-03 21:28 10404
        io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
1193b4be Poul Sander 2025-02-03 21:28 10405
1193b4be Poul Sander 2025-02-03 21:28 10406
    // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
1193b4be Poul Sander 2025-02-03 21:28 10407
    if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
1193b4be Poul Sander 2025-02-03 21:28 10408
        io.MouseDelta = io.MousePos - io.MousePosPrev;
1193b4be Poul Sander 2025-02-03 21:28 10409
    else
1193b4be Poul Sander 2025-02-03 21:28 10410
        io.MouseDelta = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10411
1193b4be Poul Sander 2025-02-03 21:28 10412
    // Update stationary timer.
1193b4be Poul Sander 2025-02-03 21:28 10413
    // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
1193b4be Poul Sander 2025-02-03 21:28 10414
    const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
1193b4be Poul Sander 2025-02-03 21:28 10415
    const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
1193b4be Poul Sander 2025-02-03 21:28 10416
    g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10417
    //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
1193b4be Poul Sander 2025-02-03 21:28 10418
1193b4be Poul Sander 2025-02-03 21:28 10419
    // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
1193b4be Poul Sander 2025-02-03 21:28 10420
    if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10421
        g.NavHighlightItemUnderNav = false;
1193b4be Poul Sander 2025-02-03 21:28 10422
1193b4be Poul Sander 2025-02-03 21:28 10423
    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
1193b4be Poul Sander 2025-02-03 21:28 10424
    {
1193b4be Poul Sander 2025-02-03 21:28 10425
        io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10426
        io.MouseClickedCount[i] = 0; // Will be filled below
1193b4be Poul Sander 2025-02-03 21:28 10427
        io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10428
        io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
1193b4be Poul Sander 2025-02-03 21:28 10429
        io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
1193b4be Poul Sander 2025-02-03 21:28 10430
        if (io.MouseClicked[i])
1193b4be Poul Sander 2025-02-03 21:28 10431
        {
1193b4be Poul Sander 2025-02-03 21:28 10432
            bool is_repeated_click = false;
1193b4be Poul Sander 2025-02-03 21:28 10433
            if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
1193b4be Poul Sander 2025-02-03 21:28 10434
            {
1193b4be Poul Sander 2025-02-03 21:28 10435
                ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10436
                if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
1193b4be Poul Sander 2025-02-03 21:28 10437
                    is_repeated_click = true;
1193b4be Poul Sander 2025-02-03 21:28 10438
            }
1193b4be Poul Sander 2025-02-03 21:28 10439
            if (is_repeated_click)
1193b4be Poul Sander 2025-02-03 21:28 10440
                io.MouseClickedLastCount[i]++;
1193b4be Poul Sander 2025-02-03 21:28 10441
            else
1193b4be Poul Sander 2025-02-03 21:28 10442
                io.MouseClickedLastCount[i] = 1;
1193b4be Poul Sander 2025-02-03 21:28 10443
            io.MouseClickedTime[i] = g.Time;
1193b4be Poul Sander 2025-02-03 21:28 10444
            io.MouseClickedPos[i] = io.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10445
            io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
1193b4be Poul Sander 2025-02-03 21:28 10446
            io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10447
            io.MouseDragMaxDistanceSqr[i] = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10448
        }
1193b4be Poul Sander 2025-02-03 21:28 10449
        else if (io.MouseDown[i])
1193b4be Poul Sander 2025-02-03 21:28 10450
        {
1193b4be Poul Sander 2025-02-03 21:28 10451
            // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
1193b4be Poul Sander 2025-02-03 21:28 10452
            ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10453
            io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
1193b4be Poul Sander 2025-02-03 21:28 10454
            io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
1193b4be Poul Sander 2025-02-03 21:28 10455
            io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
1193b4be Poul Sander 2025-02-03 21:28 10456
        }
1193b4be Poul Sander 2025-02-03 21:28 10457
1193b4be Poul Sander 2025-02-03 21:28 10458
        // We provide io.MouseDoubleClicked[] as a legacy service
1193b4be Poul Sander 2025-02-03 21:28 10459
        io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
1193b4be Poul Sander 2025-02-03 21:28 10460
1193b4be Poul Sander 2025-02-03 21:28 10461
        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
1193b4be Poul Sander 2025-02-03 21:28 10462
        if (io.MouseClicked[i])
1193b4be Poul Sander 2025-02-03 21:28 10463
            g.NavHighlightItemUnderNav = false;
1193b4be Poul Sander 2025-02-03 21:28 10464
    }
1193b4be Poul Sander 2025-02-03 21:28 10465
}
1193b4be Poul Sander 2025-02-03 21:28 10466
1193b4be Poul Sander 2025-02-03 21:28 10467
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
1193b4be Poul Sander 2025-02-03 21:28 10468
{
1193b4be Poul Sander 2025-02-03 21:28 10469
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10470
    if (window)
1193b4be Poul Sander 2025-02-03 21:28 10471
        g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
1193b4be Poul Sander 2025-02-03 21:28 10472
    else
1193b4be Poul Sander 2025-02-03 21:28 10473
        g.WheelingWindowReleaseTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10474
    if (g.WheelingWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 10475
        return;
1193b4be Poul Sander 2025-02-03 21:28 10476
    IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 10477
    g.WheelingWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 10478
    g.WheelingWindowRefMousePos = g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 10479
    if (window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 10480
    {
1193b4be Poul Sander 2025-02-03 21:28 10481
        g.WheelingWindowStartFrame = -1;
1193b4be Poul Sander 2025-02-03 21:28 10482
        g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10483
    }
1193b4be Poul Sander 2025-02-03 21:28 10484
}
1193b4be Poul Sander 2025-02-03 21:28 10485
1193b4be Poul Sander 2025-02-03 21:28 10486
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
1193b4be Poul Sander 2025-02-03 21:28 10487
{
1193b4be Poul Sander 2025-02-03 21:28 10488
    // For each axis, find window in the hierarchy that may want to use scrolling
1193b4be Poul Sander 2025-02-03 21:28 10489
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10490
    ImGuiWindow* windows[2] = { NULL, NULL };
1193b4be Poul Sander 2025-02-03 21:28 10491
    for (int axis = 0; axis < 2; axis++)
1193b4be Poul Sander 2025-02-03 21:28 10492
        if (wheel[axis] != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10493
            for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 10494
            {
1193b4be Poul Sander 2025-02-03 21:28 10495
                // Bubble up into parent window if:
1193b4be Poul Sander 2025-02-03 21:28 10496
                // - a child window doesn't allow any scrolling.
1193b4be Poul Sander 2025-02-03 21:28 10497
                // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
1193b4be Poul Sander 2025-02-03 21:28 10498
                //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
1193b4be Poul Sander 2025-02-03 21:28 10499
                const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10500
                const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
1193b4be Poul Sander 2025-02-03 21:28 10501
                //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
1193b4be Poul Sander 2025-02-03 21:28 10502
                if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
1193b4be Poul Sander 2025-02-03 21:28 10503
                    break; // select this window
1193b4be Poul Sander 2025-02-03 21:28 10504
            }
1193b4be Poul Sander 2025-02-03 21:28 10505
    if (windows[0] == NULL && windows[1] == NULL)
1193b4be Poul Sander 2025-02-03 21:28 10506
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 10507
1193b4be Poul Sander 2025-02-03 21:28 10508
    // If there's only one window or only one axis then there's no ambiguity
1193b4be Poul Sander 2025-02-03 21:28 10509
    if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
1193b4be Poul Sander 2025-02-03 21:28 10510
        return windows[1] ? windows[1] : windows[0];
1193b4be Poul Sander 2025-02-03 21:28 10511
1193b4be Poul Sander 2025-02-03 21:28 10512
    // If candidate are different windows we need to decide which one to prioritize
1193b4be Poul Sander 2025-02-03 21:28 10513
    // - First frame: only find a winner if one axis is zero.
1193b4be Poul Sander 2025-02-03 21:28 10514
    // - Subsequent frames: only find a winner when one is more than the other.
1193b4be Poul Sander 2025-02-03 21:28 10515
    if (g.WheelingWindowStartFrame == -1)
1193b4be Poul Sander 2025-02-03 21:28 10516
        g.WheelingWindowStartFrame = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 10517
    if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
1193b4be Poul Sander 2025-02-03 21:28 10518
    {
1193b4be Poul Sander 2025-02-03 21:28 10519
        g.WheelingWindowWheelRemainder = wheel;
1193b4be Poul Sander 2025-02-03 21:28 10520
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 10521
    }
1193b4be Poul Sander 2025-02-03 21:28 10522
    return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
1193b4be Poul Sander 2025-02-03 21:28 10523
}
1193b4be Poul Sander 2025-02-03 21:28 10524
1193b4be Poul Sander 2025-02-03 21:28 10525
// Called by NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 10526
void ImGui::UpdateMouseWheel()
1193b4be Poul Sander 2025-02-03 21:28 10527
{
1193b4be Poul Sander 2025-02-03 21:28 10528
    // Reset the locked window if we move the mouse or after the timer elapses.
1193b4be Poul Sander 2025-02-03 21:28 10529
    // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
1193b4be Poul Sander 2025-02-03 21:28 10530
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10531
    if (g.WheelingWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 10532
    {
1193b4be Poul Sander 2025-02-03 21:28 10533
        g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 10534
        if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
1193b4be Poul Sander 2025-02-03 21:28 10535
            g.WheelingWindowReleaseTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10536
        if (g.WheelingWindowReleaseTimer <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10537
            LockWheelingWindow(NULL, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10538
    }
1193b4be Poul Sander 2025-02-03 21:28 10539
1193b4be Poul Sander 2025-02-03 21:28 10540
    ImVec2 wheel;
1193b4be Poul Sander 2025-02-03 21:28 10541
    wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10542
    wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 10543
1193b4be Poul Sander 2025-02-03 21:28 10544
    //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
1193b4be Poul Sander 2025-02-03 21:28 10545
    ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 10546
    if (!mouse_window || mouse_window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 10547
        return;
1193b4be Poul Sander 2025-02-03 21:28 10548
1193b4be Poul Sander 2025-02-03 21:28 10549
    // Zoom / Scale window
1193b4be Poul Sander 2025-02-03 21:28 10550
    // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
1193b4be Poul Sander 2025-02-03 21:28 10551
    if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
1193b4be Poul Sander 2025-02-03 21:28 10552
    {
1193b4be Poul Sander 2025-02-03 21:28 10553
        LockWheelingWindow(mouse_window, wheel.y);
1193b4be Poul Sander 2025-02-03 21:28 10554
        ImGuiWindow* window = mouse_window;
1193b4be Poul Sander 2025-02-03 21:28 10555
        const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
1193b4be Poul Sander 2025-02-03 21:28 10556
        const float scale = new_font_scale / window->FontWindowScale;
1193b4be Poul Sander 2025-02-03 21:28 10557
        window->FontWindowScale = new_font_scale;
1193b4be Poul Sander 2025-02-03 21:28 10558
        if (window == window->RootWindow)
1193b4be Poul Sander 2025-02-03 21:28 10559
        {
1193b4be Poul Sander 2025-02-03 21:28 10560
            const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
1193b4be Poul Sander 2025-02-03 21:28 10561
            SetWindowPos(window, window->Pos + offset, 0);
1193b4be Poul Sander 2025-02-03 21:28 10562
            window->Size = ImTrunc(window->Size * scale);
1193b4be Poul Sander 2025-02-03 21:28 10563
            window->SizeFull = ImTrunc(window->SizeFull * scale);
1193b4be Poul Sander 2025-02-03 21:28 10564
        }
1193b4be Poul Sander 2025-02-03 21:28 10565
        return;
1193b4be Poul Sander 2025-02-03 21:28 10566
    }
1193b4be Poul Sander 2025-02-03 21:28 10567
    if (g.IO.KeyCtrl)
1193b4be Poul Sander 2025-02-03 21:28 10568
        return;
1193b4be Poul Sander 2025-02-03 21:28 10569
1193b4be Poul Sander 2025-02-03 21:28 10570
    // Mouse wheel scrolling
1193b4be Poul Sander 2025-02-03 21:28 10571
    // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
1193b4be Poul Sander 2025-02-03 21:28 10572
    if (g.IO.MouseWheelRequestAxisSwap)
1193b4be Poul Sander 2025-02-03 21:28 10573
        wheel = ImVec2(wheel.y, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10574
1193b4be Poul Sander 2025-02-03 21:28 10575
    // Maintain a rough average of moving magnitude on both axises
1193b4be Poul Sander 2025-02-03 21:28 10576
    // FIXME: should by based on wall clock time rather than frame-counter
1193b4be Poul Sander 2025-02-03 21:28 10577
    g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
1193b4be Poul Sander 2025-02-03 21:28 10578
    g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
1193b4be Poul Sander 2025-02-03 21:28 10579
1193b4be Poul Sander 2025-02-03 21:28 10580
    // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
1193b4be Poul Sander 2025-02-03 21:28 10581
    wheel += g.WheelingWindowWheelRemainder;
1193b4be Poul Sander 2025-02-03 21:28 10582
    g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 10583
    if (wheel.x == 0.0f && wheel.y == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 10584
        return;
1193b4be Poul Sander 2025-02-03 21:28 10585
1193b4be Poul Sander 2025-02-03 21:28 10586
    // Mouse wheel scrolling: find target and apply
1193b4be Poul Sander 2025-02-03 21:28 10587
    // - don't renew lock if axis doesn't apply on the window.
1193b4be Poul Sander 2025-02-03 21:28 10588
    // - select a main axis when both axises are being moved.
1193b4be Poul Sander 2025-02-03 21:28 10589
    if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
1193b4be Poul Sander 2025-02-03 21:28 10590
        if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
1193b4be Poul Sander 2025-02-03 21:28 10591
        {
1193b4be Poul Sander 2025-02-03 21:28 10592
            bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
1193b4be Poul Sander 2025-02-03 21:28 10593
            if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
1193b4be Poul Sander 2025-02-03 21:28 10594
                do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
1193b4be Poul Sander 2025-02-03 21:28 10595
            if (do_scroll[ImGuiAxis_X])
1193b4be Poul Sander 2025-02-03 21:28 10596
            {
1193b4be Poul Sander 2025-02-03 21:28 10597
                LockWheelingWindow(window, wheel.x);
1193b4be Poul Sander 2025-02-03 21:28 10598
                float max_step = window->InnerRect.GetWidth() * 0.67f;
1193b4be Poul Sander 2025-02-03 21:28 10599
                float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
1193b4be Poul Sander 2025-02-03 21:28 10600
                SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
1193b4be Poul Sander 2025-02-03 21:28 10601
                g.WheelingWindowScrolledFrame = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 10602
            }
1193b4be Poul Sander 2025-02-03 21:28 10603
            if (do_scroll[ImGuiAxis_Y])
1193b4be Poul Sander 2025-02-03 21:28 10604
            {
1193b4be Poul Sander 2025-02-03 21:28 10605
                LockWheelingWindow(window, wheel.y);
1193b4be Poul Sander 2025-02-03 21:28 10606
                float max_step = window->InnerRect.GetHeight() * 0.67f;
1193b4be Poul Sander 2025-02-03 21:28 10607
                float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
1193b4be Poul Sander 2025-02-03 21:28 10608
                SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
1193b4be Poul Sander 2025-02-03 21:28 10609
                g.WheelingWindowScrolledFrame = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 10610
            }
1193b4be Poul Sander 2025-02-03 21:28 10611
        }
1193b4be Poul Sander 2025-02-03 21:28 10612
}
1193b4be Poul Sander 2025-02-03 21:28 10613
1193b4be Poul Sander 2025-02-03 21:28 10614
void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
1193b4be Poul Sander 2025-02-03 21:28 10615
{
1193b4be Poul Sander 2025-02-03 21:28 10616
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10617
    g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
1193b4be Poul Sander 2025-02-03 21:28 10618
}
1193b4be Poul Sander 2025-02-03 21:28 10619
1193b4be Poul Sander 2025-02-03 21:28 10620
void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
1193b4be Poul Sander 2025-02-03 21:28 10621
{
1193b4be Poul Sander 2025-02-03 21:28 10622
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10623
    g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
1193b4be Poul Sander 2025-02-03 21:28 10624
}
1193b4be Poul Sander 2025-02-03 21:28 10625
1193b4be Poul Sander 2025-02-03 21:28 10626
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 10627
static const char* GetInputSourceName(ImGuiInputSource source)
1193b4be Poul Sander 2025-02-03 21:28 10628
{
1193b4be Poul Sander 2025-02-03 21:28 10629
    const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
1193b4be Poul Sander 2025-02-03 21:28 10630
    IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 10631
    return input_source_names[source];
1193b4be Poul Sander 2025-02-03 21:28 10632
}
1193b4be Poul Sander 2025-02-03 21:28 10633
static const char* GetMouseSourceName(ImGuiMouseSource source)
1193b4be Poul Sander 2025-02-03 21:28 10634
{
1193b4be Poul Sander 2025-02-03 21:28 10635
    const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
1193b4be Poul Sander 2025-02-03 21:28 10636
    IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 10637
    return mouse_source_names[source];
1193b4be Poul Sander 2025-02-03 21:28 10638
}
1193b4be Poul Sander 2025-02-03 21:28 10639
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
1193b4be Poul Sander 2025-02-03 21:28 10640
{
1193b4be Poul Sander 2025-02-03 21:28 10641
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10642
    if (e->Type == ImGuiInputEventType_MousePos)    { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }
1193b4be Poul Sander 2025-02-03 21:28 10643
    if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; }
1193b4be Poul Sander 2025-02-03 21:28 10644
    if (e->Type == ImGuiInputEventType_MouseWheel)  { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
1193b4be Poul Sander 2025-02-03 21:28 10645
    if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; }
1193b4be Poul Sander 2025-02-03 21:28 10646
    if (e->Type == ImGuiInputEventType_Key)         { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
1193b4be Poul Sander 2025-02-03 21:28 10647
    if (e->Type == ImGuiInputEventType_Text)        { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
1193b4be Poul Sander 2025-02-03 21:28 10648
    if (e->Type == ImGuiInputEventType_Focus)       { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
1193b4be Poul Sander 2025-02-03 21:28 10649
}
1193b4be Poul Sander 2025-02-03 21:28 10650
#endif
1193b4be Poul Sander 2025-02-03 21:28 10651
1193b4be Poul Sander 2025-02-03 21:28 10652
// Process input queue
1193b4be Poul Sander 2025-02-03 21:28 10653
// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
1193b4be Poul Sander 2025-02-03 21:28 10654
// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
1193b4be Poul Sander 2025-02-03 21:28 10655
// - trickle_fast_inputs = true  : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
1193b4be Poul Sander 2025-02-03 21:28 10656
void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
1193b4be Poul Sander 2025-02-03 21:28 10657
{
1193b4be Poul Sander 2025-02-03 21:28 10658
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10659
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 10660
1193b4be Poul Sander 2025-02-03 21:28 10661
    // Only trickle chars<>key when working with InputText()
1193b4be Poul Sander 2025-02-03 21:28 10662
    // FIXME: InputText() could parse event trail?
1193b4be Poul Sander 2025-02-03 21:28 10663
    // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
1193b4be Poul Sander 2025-02-03 21:28 10664
    const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
1193b4be Poul Sander 2025-02-03 21:28 10665
1193b4be Poul Sander 2025-02-03 21:28 10666
    bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
1193b4be Poul Sander 2025-02-03 21:28 10667
    int  mouse_button_changed = 0x00;
1193b4be Poul Sander 2025-02-03 21:28 10668
    ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;
1193b4be Poul Sander 2025-02-03 21:28 10669
1193b4be Poul Sander 2025-02-03 21:28 10670
    int event_n = 0;
1193b4be Poul Sander 2025-02-03 21:28 10671
    for (; event_n < g.InputEventsQueue.Size; event_n++)
1193b4be Poul Sander 2025-02-03 21:28 10672
    {
1193b4be Poul Sander 2025-02-03 21:28 10673
        ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
1193b4be Poul Sander 2025-02-03 21:28 10674
        if (e->Type == ImGuiInputEventType_MousePos)
1193b4be Poul Sander 2025-02-03 21:28 10675
        {
1193b4be Poul Sander 2025-02-03 21:28 10676
            if (g.IO.WantSetMousePos)
1193b4be Poul Sander 2025-02-03 21:28 10677
                continue;
1193b4be Poul Sander 2025-02-03 21:28 10678
            // Trickling Rule: Stop processing queued events if we already handled a mouse button change
1193b4be Poul Sander 2025-02-03 21:28 10679
            ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
1193b4be Poul Sander 2025-02-03 21:28 10680
            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
1193b4be Poul Sander 2025-02-03 21:28 10681
                break;
1193b4be Poul Sander 2025-02-03 21:28 10682
            io.MousePos = event_pos;
1193b4be Poul Sander 2025-02-03 21:28 10683
            io.MouseSource = e->MousePos.MouseSource;
1193b4be Poul Sander 2025-02-03 21:28 10684
            mouse_moved = true;
1193b4be Poul Sander 2025-02-03 21:28 10685
        }
1193b4be Poul Sander 2025-02-03 21:28 10686
        else if (e->Type == ImGuiInputEventType_MouseButton)
1193b4be Poul Sander 2025-02-03 21:28 10687
        {
1193b4be Poul Sander 2025-02-03 21:28 10688
            // Trickling Rule: Stop processing queued events if we got multiple action on the same button
1193b4be Poul Sander 2025-02-03 21:28 10689
            const ImGuiMouseButton button = e->MouseButton.Button;
1193b4be Poul Sander 2025-02-03 21:28 10690
            IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
1193b4be Poul Sander 2025-02-03 21:28 10691
            if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
1193b4be Poul Sander 2025-02-03 21:28 10692
                break;
1193b4be Poul Sander 2025-02-03 21:28 10693
            if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
1193b4be Poul Sander 2025-02-03 21:28 10694
                break;
1193b4be Poul Sander 2025-02-03 21:28 10695
            io.MouseDown[button] = e->MouseButton.Down;
1193b4be Poul Sander 2025-02-03 21:28 10696
            io.MouseSource = e->MouseButton.MouseSource;
1193b4be Poul Sander 2025-02-03 21:28 10697
            mouse_button_changed |= (1 << button);
1193b4be Poul Sander 2025-02-03 21:28 10698
        }
1193b4be Poul Sander 2025-02-03 21:28 10699
        else if (e->Type == ImGuiInputEventType_MouseWheel)
1193b4be Poul Sander 2025-02-03 21:28 10700
        {
1193b4be Poul Sander 2025-02-03 21:28 10701
            // Trickling Rule: Stop processing queued events if we got multiple action on the event
1193b4be Poul Sander 2025-02-03 21:28 10702
            if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
1193b4be Poul Sander 2025-02-03 21:28 10703
                break;
1193b4be Poul Sander 2025-02-03 21:28 10704
            io.MouseWheelH += e->MouseWheel.WheelX;
1193b4be Poul Sander 2025-02-03 21:28 10705
            io.MouseWheel += e->MouseWheel.WheelY;
1193b4be Poul Sander 2025-02-03 21:28 10706
            io.MouseSource = e->MouseWheel.MouseSource;
1193b4be Poul Sander 2025-02-03 21:28 10707
            mouse_wheeled = true;
1193b4be Poul Sander 2025-02-03 21:28 10708
        }
1193b4be Poul Sander 2025-02-03 21:28 10709
        else if (e->Type == ImGuiInputEventType_MouseViewport)
1193b4be Poul Sander 2025-02-03 21:28 10710
        {
1193b4be Poul Sander 2025-02-03 21:28 10711
            io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;
1193b4be Poul Sander 2025-02-03 21:28 10712
        }
1193b4be Poul Sander 2025-02-03 21:28 10713
        else if (e->Type == ImGuiInputEventType_Key)
1193b4be Poul Sander 2025-02-03 21:28 10714
        {
1193b4be Poul Sander 2025-02-03 21:28 10715
            // Trickling Rule: Stop processing queued events if we got multiple action on the same button
1193b4be Poul Sander 2025-02-03 21:28 10716
            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
1193b4be Poul Sander 2025-02-03 21:28 10717
                continue;
1193b4be Poul Sander 2025-02-03 21:28 10718
            ImGuiKey key = e->Key.Key;
1193b4be Poul Sander 2025-02-03 21:28 10719
            IM_ASSERT(key != ImGuiKey_None);
1193b4be Poul Sander 2025-02-03 21:28 10720
            ImGuiKeyData* key_data = GetKeyData(key);
1193b4be Poul Sander 2025-02-03 21:28 10721
            const int key_data_index = (int)(key_data - g.IO.KeysData);
1193b4be Poul Sander 2025-02-03 21:28 10722
            if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))
1193b4be Poul Sander 2025-02-03 21:28 10723
                break;
1193b4be Poul Sander 2025-02-03 21:28 10724
1193b4be Poul Sander 2025-02-03 21:28 10725
            const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);
1193b4be Poul Sander 2025-02-03 21:28 10726
            if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
1193b4be Poul Sander 2025-02-03 21:28 10727
                break;
1193b4be Poul Sander 2025-02-03 21:28 10728
1193b4be Poul Sander 2025-02-03 21:28 10729
            key_data->Down = e->Key.Down;
1193b4be Poul Sander 2025-02-03 21:28 10730
            key_data->AnalogValue = e->Key.AnalogValue;
1193b4be Poul Sander 2025-02-03 21:28 10731
            key_changed = true;
1193b4be Poul Sander 2025-02-03 21:28 10732
            key_changed_mask.SetBit(key_data_index);
1193b4be Poul Sander 2025-02-03 21:28 10733
            if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
1193b4be Poul Sander 2025-02-03 21:28 10734
                key_changed_nonchar = true;
1193b4be Poul Sander 2025-02-03 21:28 10735
1193b4be Poul Sander 2025-02-03 21:28 10736
            // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
1193b4be Poul Sander 2025-02-03 21:28 10737
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 10738
            io.KeysDown[key_data_index] = key_data->Down;
1193b4be Poul Sander 2025-02-03 21:28 10739
            if (io.KeyMap[key_data_index] != -1)
1193b4be Poul Sander 2025-02-03 21:28 10740
                io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
1193b4be Poul Sander 2025-02-03 21:28 10741
#endif
1193b4be Poul Sander 2025-02-03 21:28 10742
        }
1193b4be Poul Sander 2025-02-03 21:28 10743
        else if (e->Type == ImGuiInputEventType_Text)
1193b4be Poul Sander 2025-02-03 21:28 10744
        {
1193b4be Poul Sander 2025-02-03 21:28 10745
            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
1193b4be Poul Sander 2025-02-03 21:28 10746
                continue;
1193b4be Poul Sander 2025-02-03 21:28 10747
            // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
1193b4be Poul Sander 2025-02-03 21:28 10748
            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
1193b4be Poul Sander 2025-02-03 21:28 10749
                break;
1193b4be Poul Sander 2025-02-03 21:28 10750
            if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
1193b4be Poul Sander 2025-02-03 21:28 10751
                break;
1193b4be Poul Sander 2025-02-03 21:28 10752
            unsigned int c = e->Text.Char;
1193b4be Poul Sander 2025-02-03 21:28 10753
            io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1193b4be Poul Sander 2025-02-03 21:28 10754
            if (trickle_interleaved_nonchar_keys_and_text)
1193b4be Poul Sander 2025-02-03 21:28 10755
                text_inputted = true;
1193b4be Poul Sander 2025-02-03 21:28 10756
        }
1193b4be Poul Sander 2025-02-03 21:28 10757
        else if (e->Type == ImGuiInputEventType_Focus)
1193b4be Poul Sander 2025-02-03 21:28 10758
        {
1193b4be Poul Sander 2025-02-03 21:28 10759
            // We intentionally overwrite this and process in NewFrame(), in order to give a chance
1193b4be Poul Sander 2025-02-03 21:28 10760
            // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
1193b4be Poul Sander 2025-02-03 21:28 10761
            const bool focus_lost = !e->AppFocused.Focused;
1193b4be Poul Sander 2025-02-03 21:28 10762
            io.AppFocusLost = focus_lost;
1193b4be Poul Sander 2025-02-03 21:28 10763
        }
1193b4be Poul Sander 2025-02-03 21:28 10764
        else
1193b4be Poul Sander 2025-02-03 21:28 10765
        {
1193b4be Poul Sander 2025-02-03 21:28 10766
            IM_ASSERT(0 && "Unknown event!");
1193b4be Poul Sander 2025-02-03 21:28 10767
        }
1193b4be Poul Sander 2025-02-03 21:28 10768
    }
1193b4be Poul Sander 2025-02-03 21:28 10769
1193b4be Poul Sander 2025-02-03 21:28 10770
    // Record trail (for domain-specific applications wanting to access a precise trail)
1193b4be Poul Sander 2025-02-03 21:28 10771
    //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
1193b4be Poul Sander 2025-02-03 21:28 10772
    for (int n = 0; n < event_n; n++)
1193b4be Poul Sander 2025-02-03 21:28 10773
        g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
1193b4be Poul Sander 2025-02-03 21:28 10774
1193b4be Poul Sander 2025-02-03 21:28 10775
    // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 10776
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 10777
    if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
1193b4be Poul Sander 2025-02-03 21:28 10778
        for (int n = 0; n < g.InputEventsQueue.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 10779
            DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
1193b4be Poul Sander 2025-02-03 21:28 10780
#endif
1193b4be Poul Sander 2025-02-03 21:28 10781
1193b4be Poul Sander 2025-02-03 21:28 10782
    // Remaining events will be processed on the next frame
1193b4be Poul Sander 2025-02-03 21:28 10783
    if (event_n == g.InputEventsQueue.Size)
1193b4be Poul Sander 2025-02-03 21:28 10784
        g.InputEventsQueue.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 10785
    else
1193b4be Poul Sander 2025-02-03 21:28 10786
        g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
1193b4be Poul Sander 2025-02-03 21:28 10787
1193b4be Poul Sander 2025-02-03 21:28 10788
    // Clear buttons state when focus is lost
1193b4be Poul Sander 2025-02-03 21:28 10789
    // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
1193b4be Poul Sander 2025-02-03 21:28 10790
    // - we clear in EndFrame() and not now in order allow application/user code polling this flag
1193b4be Poul Sander 2025-02-03 21:28 10791
    //   (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
1193b4be Poul Sander 2025-02-03 21:28 10792
    if (g.IO.AppFocusLost)
1193b4be Poul Sander 2025-02-03 21:28 10793
    {
1193b4be Poul Sander 2025-02-03 21:28 10794
        g.IO.ClearInputKeys();
1193b4be Poul Sander 2025-02-03 21:28 10795
        g.IO.ClearInputMouse();
1193b4be Poul Sander 2025-02-03 21:28 10796
    }
1193b4be Poul Sander 2025-02-03 21:28 10797
}
1193b4be Poul Sander 2025-02-03 21:28 10798
1193b4be Poul Sander 2025-02-03 21:28 10799
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 10800
{
1193b4be Poul Sander 2025-02-03 21:28 10801
    if (!IsNamedKeyOrMod(key))
1193b4be Poul Sander 2025-02-03 21:28 10802
        return ImGuiKeyOwner_NoOwner;
1193b4be Poul Sander 2025-02-03 21:28 10803
1193b4be Poul Sander 2025-02-03 21:28 10804
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10805
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 10806
    ImGuiID owner_id = owner_data->OwnerCurr;
1193b4be Poul Sander 2025-02-03 21:28 10807
1193b4be Poul Sander 2025-02-03 21:28 10808
    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
1193b4be Poul Sander 2025-02-03 21:28 10809
        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
1193b4be Poul Sander 2025-02-03 21:28 10810
            return ImGuiKeyOwner_NoOwner;
1193b4be Poul Sander 2025-02-03 21:28 10811
1193b4be Poul Sander 2025-02-03 21:28 10812
    return owner_id;
1193b4be Poul Sander 2025-02-03 21:28 10813
}
1193b4be Poul Sander 2025-02-03 21:28 10814
1193b4be Poul Sander 2025-02-03 21:28 10815
// TestKeyOwner(..., ID)   : (owner == None || owner == ID)
1193b4be Poul Sander 2025-02-03 21:28 10816
// TestKeyOwner(..., None) : (owner == None)
1193b4be Poul Sander 2025-02-03 21:28 10817
// TestKeyOwner(..., Any)  : no owner test
1193b4be Poul Sander 2025-02-03 21:28 10818
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
1193b4be Poul Sander 2025-02-03 21:28 10819
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10820
{
1193b4be Poul Sander 2025-02-03 21:28 10821
    if (!IsNamedKeyOrMod(key))
1193b4be Poul Sander 2025-02-03 21:28 10822
        return true;
1193b4be Poul Sander 2025-02-03 21:28 10823
1193b4be Poul Sander 2025-02-03 21:28 10824
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10825
    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
1193b4be Poul Sander 2025-02-03 21:28 10826
        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
1193b4be Poul Sander 2025-02-03 21:28 10827
            return false;
1193b4be Poul Sander 2025-02-03 21:28 10828
1193b4be Poul Sander 2025-02-03 21:28 10829
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 10830
    if (owner_id == ImGuiKeyOwner_Any)
1193b4be Poul Sander 2025-02-03 21:28 10831
        return (owner_data->LockThisFrame == false);
1193b4be Poul Sander 2025-02-03 21:28 10832
1193b4be Poul Sander 2025-02-03 21:28 10833
    // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
1193b4be Poul Sander 2025-02-03 21:28 10834
    // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
1193b4be Poul Sander 2025-02-03 21:28 10835
    // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
1193b4be Poul Sander 2025-02-03 21:28 10836
    if (owner_data->OwnerCurr != owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10837
    {
1193b4be Poul Sander 2025-02-03 21:28 10838
        if (owner_data->LockThisFrame)
1193b4be Poul Sander 2025-02-03 21:28 10839
            return false;
1193b4be Poul Sander 2025-02-03 21:28 10840
        if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 10841
            return false;
1193b4be Poul Sander 2025-02-03 21:28 10842
    }
1193b4be Poul Sander 2025-02-03 21:28 10843
1193b4be Poul Sander 2025-02-03 21:28 10844
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10845
}
1193b4be Poul Sander 2025-02-03 21:28 10846
1193b4be Poul Sander 2025-02-03 21:28 10847
// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
1193b4be Poul Sander 2025-02-03 21:28 10848
// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
1193b4be Poul Sander 2025-02-03 21:28 10849
// - SetKeyOwner(..., None)              : clears owner
1193b4be Poul Sander 2025-02-03 21:28 10850
// - SetKeyOwner(..., Any, !Lock)        : illegal (assert)
1193b4be Poul Sander 2025-02-03 21:28 10851
// - SetKeyOwner(..., Any or None, Lock) : set lock
1193b4be Poul Sander 2025-02-03 21:28 10852
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 10853
{
1193b4be Poul Sander 2025-02-03 21:28 10854
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10855
    IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
1193b4be Poul Sander 2025-02-03 21:28 10856
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
1193b4be Poul Sander 2025-02-03 21:28 10857
    //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
1193b4be Poul Sander 2025-02-03 21:28 10858
1193b4be Poul Sander 2025-02-03 21:28 10859
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 10860
    owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
1193b4be Poul Sander 2025-02-03 21:28 10861
1193b4be Poul Sander 2025-02-03 21:28 10862
    // We cannot lock by default as it would likely break lots of legacy code.
1193b4be Poul Sander 2025-02-03 21:28 10863
    // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
1193b4be Poul Sander 2025-02-03 21:28 10864
    owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
1193b4be Poul Sander 2025-02-03 21:28 10865
    owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
1193b4be Poul Sander 2025-02-03 21:28 10866
}
1193b4be Poul Sander 2025-02-03 21:28 10867
1193b4be Poul Sander 2025-02-03 21:28 10868
// Rarely used helper
1193b4be Poul Sander 2025-02-03 21:28 10869
void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 10870
{
1193b4be Poul Sander 2025-02-03 21:28 10871
    if (key_chord & ImGuiMod_Ctrl)      { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
1193b4be Poul Sander 2025-02-03 21:28 10872
    if (key_chord & ImGuiMod_Shift)     { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
1193b4be Poul Sander 2025-02-03 21:28 10873
    if (key_chord & ImGuiMod_Alt)       { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
1193b4be Poul Sander 2025-02-03 21:28 10874
    if (key_chord & ImGuiMod_Super)     { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
1193b4be Poul Sander 2025-02-03 21:28 10875
    if (key_chord & ~ImGuiMod_Mask_)    { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
1193b4be Poul Sander 2025-02-03 21:28 10876
}
1193b4be Poul Sander 2025-02-03 21:28 10877
1193b4be Poul Sander 2025-02-03 21:28 10878
// This is more or less equivalent to:
1193b4be Poul Sander 2025-02-03 21:28 10879
//   if (IsItemHovered() || IsItemActive())
1193b4be Poul Sander 2025-02-03 21:28 10880
//       SetKeyOwner(key, GetItemID());
1193b4be Poul Sander 2025-02-03 21:28 10881
// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
1193b4be Poul Sander 2025-02-03 21:28 10882
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
1193b4be Poul Sander 2025-02-03 21:28 10883
// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
1193b4be Poul Sander 2025-02-03 21:28 10884
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 10885
{
1193b4be Poul Sander 2025-02-03 21:28 10886
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10887
    ImGuiID id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 10888
    if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
1193b4be Poul Sander 2025-02-03 21:28 10889
        return;
1193b4be Poul Sander 2025-02-03 21:28 10890
    if ((flags & ImGuiInputFlags_CondMask_) == 0)
1193b4be Poul Sander 2025-02-03 21:28 10891
        flags |= ImGuiInputFlags_CondDefault_;
1193b4be Poul Sander 2025-02-03 21:28 10892
    if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
1193b4be Poul Sander 2025-02-03 21:28 10893
    {
1193b4be Poul Sander 2025-02-03 21:28 10894
        IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
1193b4be Poul Sander 2025-02-03 21:28 10895
        SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
1193b4be Poul Sander 2025-02-03 21:28 10896
    }
1193b4be Poul Sander 2025-02-03 21:28 10897
}
1193b4be Poul Sander 2025-02-03 21:28 10898
1193b4be Poul Sander 2025-02-03 21:28 10899
void ImGui::SetItemKeyOwner(ImGuiKey key)
1193b4be Poul Sander 2025-02-03 21:28 10900
{
1193b4be Poul Sander 2025-02-03 21:28 10901
    SetItemKeyOwner(key, ImGuiInputFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 10902
}
1193b4be Poul Sander 2025-02-03 21:28 10903
1193b4be Poul Sander 2025-02-03 21:28 10904
// This is the only public API until we expose owner_id versions of the API as replacements.
1193b4be Poul Sander 2025-02-03 21:28 10905
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
1193b4be Poul Sander 2025-02-03 21:28 10906
{
1193b4be Poul Sander 2025-02-03 21:28 10907
    return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 10908
}
1193b4be Poul Sander 2025-02-03 21:28 10909
1193b4be Poul Sander 2025-02-03 21:28 10910
// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
1193b4be Poul Sander 2025-02-03 21:28 10911
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10912
{
1193b4be Poul Sander 2025-02-03 21:28 10913
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10914
    key_chord = FixupKeyChord(key_chord);
1193b4be Poul Sander 2025-02-03 21:28 10915
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 10916
    if (g.IO.KeyMods != mods)
1193b4be Poul Sander 2025-02-03 21:28 10917
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10918
1193b4be Poul Sander 2025-02-03 21:28 10919
    // Special storage location for mods
1193b4be Poul Sander 2025-02-03 21:28 10920
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
1193b4be Poul Sander 2025-02-03 21:28 10921
    if (key == ImGuiKey_None)
1193b4be Poul Sander 2025-02-03 21:28 10922
        key = ConvertSingleModFlagToKey(mods);
1193b4be Poul Sander 2025-02-03 21:28 10923
    if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10924
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10925
    return true;
1193b4be Poul Sander 2025-02-03 21:28 10926
}
1193b4be Poul Sander 2025-02-03 21:28 10927
1193b4be Poul Sander 2025-02-03 21:28 10928
void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 10929
{
1193b4be Poul Sander 2025-02-03 21:28 10930
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10931
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
1193b4be Poul Sander 2025-02-03 21:28 10932
    g.NextItemData.Shortcut = key_chord;
1193b4be Poul Sander 2025-02-03 21:28 10933
    g.NextItemData.ShortcutFlags = flags;
1193b4be Poul Sander 2025-02-03 21:28 10934
}
1193b4be Poul Sander 2025-02-03 21:28 10935
1193b4be Poul Sander 2025-02-03 21:28 10936
// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
1193b4be Poul Sander 2025-02-03 21:28 10937
void ImGui::ItemHandleShortcut(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 10938
{
1193b4be Poul Sander 2025-02-03 21:28 10939
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10940
    ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
1193b4be Poul Sander 2025-02-03 21:28 10941
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
1193b4be Poul Sander 2025-02-03 21:28 10942
1193b4be Poul Sander 2025-02-03 21:28 10943
    if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
1193b4be Poul Sander 2025-02-03 21:28 10944
        return;
1193b4be Poul Sander 2025-02-03 21:28 10945
    if (flags & ImGuiInputFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 10946
    {
1193b4be Poul Sander 2025-02-03 21:28 10947
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
1193b4be Poul Sander 2025-02-03 21:28 10948
        g.LastItemData.Shortcut = g.NextItemData.Shortcut;
1193b4be Poul Sander 2025-02-03 21:28 10949
    }
1193b4be Poul Sander 2025-02-03 21:28 10950
    if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
1193b4be Poul Sander 2025-02-03 21:28 10951
        return;
1193b4be Poul Sander 2025-02-03 21:28 10952
1193b4be Poul Sander 2025-02-03 21:28 10953
    // FIXME: Generalize Activation queue?
1193b4be Poul Sander 2025-02-03 21:28 10954
    g.NavActivateId = id; // Will effectively disable clipping.
1193b4be Poul Sander 2025-02-03 21:28 10955
    g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
1193b4be Poul Sander 2025-02-03 21:28 10956
    //if (g.ActiveId == 0 || g.ActiveId == id)
1193b4be Poul Sander 2025-02-03 21:28 10957
    g.NavActivateDownId = g.NavActivatePressedId = id;
1193b4be Poul Sander 2025-02-03 21:28 10958
    NavHighlightActivated(id);
1193b4be Poul Sander 2025-02-03 21:28 10959
}
1193b4be Poul Sander 2025-02-03 21:28 10960
1193b4be Poul Sander 2025-02-03 21:28 10961
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 10962
{
1193b4be Poul Sander 2025-02-03 21:28 10963
    return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
1193b4be Poul Sander 2025-02-03 21:28 10964
}
1193b4be Poul Sander 2025-02-03 21:28 10965
1193b4be Poul Sander 2025-02-03 21:28 10966
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
1193b4be Poul Sander 2025-02-03 21:28 10967
{
1193b4be Poul Sander 2025-02-03 21:28 10968
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 10969
    //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 10970
1193b4be Poul Sander 2025-02-03 21:28 10971
    // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
1193b4be Poul Sander 2025-02-03 21:28 10972
    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
1193b4be Poul Sander 2025-02-03 21:28 10973
        flags |= ImGuiInputFlags_RouteFocused;
1193b4be Poul Sander 2025-02-03 21:28 10974
1193b4be Poul Sander 2025-02-03 21:28 10975
    // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
1193b4be Poul Sander 2025-02-03 21:28 10976
    // Effectively makes Shortcut() always input-owner aware.
1193b4be Poul Sander 2025-02-03 21:28 10977
    if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 10978
        owner_id = GetRoutingIdFromOwnerId(owner_id);
1193b4be Poul Sander 2025-02-03 21:28 10979
1193b4be Poul Sander 2025-02-03 21:28 10980
    if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
1193b4be Poul Sander 2025-02-03 21:28 10981
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10982
1193b4be Poul Sander 2025-02-03 21:28 10983
    // Submit route
1193b4be Poul Sander 2025-02-03 21:28 10984
    if (!SetShortcutRouting(key_chord, flags, owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10985
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10986
1193b4be Poul Sander 2025-02-03 21:28 10987
    // Default repeat behavior for Shortcut()
1193b4be Poul Sander 2025-02-03 21:28 10988
    // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
1193b4be Poul Sander 2025-02-03 21:28 10989
    if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
1193b4be Poul Sander 2025-02-03 21:28 10990
        flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
1193b4be Poul Sander 2025-02-03 21:28 10991
1193b4be Poul Sander 2025-02-03 21:28 10992
    if (!IsKeyChordPressed(key_chord, flags, owner_id))
1193b4be Poul Sander 2025-02-03 21:28 10993
        return false;
1193b4be Poul Sander 2025-02-03 21:28 10994
1193b4be Poul Sander 2025-02-03 21:28 10995
    // Claim mods during the press
1193b4be Poul Sander 2025-02-03 21:28 10996
    SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 10997
1193b4be Poul Sander 2025-02-03 21:28 10998
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
1193b4be Poul Sander 2025-02-03 21:28 10999
    return true;
1193b4be Poul Sander 2025-02-03 21:28 11000
}
1193b4be Poul Sander 2025-02-03 21:28 11001
1193b4be Poul Sander 2025-02-03 21:28 11002
1193b4be Poul Sander 2025-02-03 21:28 11003
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11004
// [SECTION] ERROR CHECKING, STATE RECOVERY
1193b4be Poul Sander 2025-02-03 21:28 11005
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11006
// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
1193b4be Poul Sander 2025-02-03 21:28 11007
// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
1193b4be Poul Sander 2025-02-03 21:28 11008
// - ErrorCheckNewFrameSanityChecks()
1193b4be Poul Sander 2025-02-03 21:28 11009
// - ErrorCheckEndFrameSanityChecks()
1193b4be Poul Sander 2025-02-03 21:28 11010
// - ErrorRecoveryStoreState()
1193b4be Poul Sander 2025-02-03 21:28 11011
// - ErrorRecoveryTryToRecoverState()
1193b4be Poul Sander 2025-02-03 21:28 11012
// - ErrorRecoveryTryToRecoverWindowState()
1193b4be Poul Sander 2025-02-03 21:28 11013
// - ErrorLog()
1193b4be Poul Sander 2025-02-03 21:28 11014
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11015
1193b4be Poul Sander 2025-02-03 21:28 11016
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
1193b4be Poul Sander 2025-02-03 21:28 11017
// Called by IMGUI_CHECKVERSION().
1193b4be Poul Sander 2025-02-03 21:28 11018
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
1193b4be Poul Sander 2025-02-03 21:28 11019
// If this triggers you have mismatched headers and compiled code versions.
1193b4be Poul Sander 2025-02-03 21:28 11020
// - It could be because of a build issue (using new headers with old compiled code)
1193b4be Poul Sander 2025-02-03 21:28 11021
// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
1193b4be Poul Sander 2025-02-03 21:28 11022
//   THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
1193b4be Poul Sander 2025-02-03 21:28 11023
//   Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
1193b4be Poul Sander 2025-02-03 21:28 11024
//   Otherwise it is possible that different compilation units would see different structure layout.
1193b4be Poul Sander 2025-02-03 21:28 11025
//   If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
1193b4be Poul Sander 2025-02-03 21:28 11026
bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
1193b4be Poul Sander 2025-02-03 21:28 11027
{
1193b4be Poul Sander 2025-02-03 21:28 11028
    bool error = false;
1193b4be Poul Sander 2025-02-03 21:28 11029
    if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
1193b4be Poul Sander 2025-02-03 21:28 11030
    if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11031
    if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11032
    if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11033
    if (sz_vec4  != sizeof(ImVec4))     { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11034
    if (sz_vert  != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11035
    if (sz_idx   != sizeof(ImDrawIdx))  { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
1193b4be Poul Sander 2025-02-03 21:28 11036
    return !error;
1193b4be Poul Sander 2025-02-03 21:28 11037
}
1193b4be Poul Sander 2025-02-03 21:28 11038
1193b4be Poul Sander 2025-02-03 21:28 11039
// Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell)
1193b4be Poul Sander 2025-02-03 21:28 11040
// This is causing issues and ambiguity and we need to retire that.
1193b4be Poul Sander 2025-02-03 21:28 11041
// See https://github.com/ocornut/imgui/issues/5548 for more details.
1193b4be Poul Sander 2025-02-03 21:28 11042
// [Scenario 1]
1193b4be Poul Sander 2025-02-03 21:28 11043
//  Previously this would make the window content size ~200x200:
1193b4be Poul Sander 2025-02-03 21:28 11044
//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();  // NOT OK
1193b4be Poul Sander 2025-02-03 21:28 11045
//  Instead, please submit an item:
1193b4be Poul Sander 2025-02-03 21:28 11046
//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
1193b4be Poul Sander 2025-02-03 21:28 11047
//  Alternative:
1193b4be Poul Sander 2025-02-03 21:28 11048
//    Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
1193b4be Poul Sander 2025-02-03 21:28 11049
// [Scenario 2]
1193b4be Poul Sander 2025-02-03 21:28 11050
//  For reference this is one of the issue what we aim to fix with this change:
1193b4be Poul Sander 2025-02-03 21:28 11051
//    BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()
1193b4be Poul Sander 2025-02-03 21:28 11052
//  The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller!
1193b4be Poul Sander 2025-02-03 21:28 11053
//  While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue.
1193b4be Poul Sander 2025-02-03 21:28 11054
void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
1193b4be Poul Sander 2025-02-03 21:28 11055
{
1193b4be Poul Sander 2025-02-03 21:28 11056
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11057
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11058
    IM_ASSERT(window->DC.IsSetPos);
1193b4be Poul Sander 2025-02-03 21:28 11059
    window->DC.IsSetPos = false;
1193b4be Poul Sander 2025-02-03 21:28 11060
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 11061
    if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
1193b4be Poul Sander 2025-02-03 21:28 11062
        return;
1193b4be Poul Sander 2025-02-03 21:28 11063
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 11064
        return;
1193b4be Poul Sander 2025-02-03 21:28 11065
    IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.");
1193b4be Poul Sander 2025-02-03 21:28 11066
#else
1193b4be Poul Sander 2025-02-03 21:28 11067
    window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
1193b4be Poul Sander 2025-02-03 21:28 11068
#endif
1193b4be Poul Sander 2025-02-03 21:28 11069
}
1193b4be Poul Sander 2025-02-03 21:28 11070
1193b4be Poul Sander 2025-02-03 21:28 11071
static void ImGui::ErrorCheckNewFrameSanityChecks()
1193b4be Poul Sander 2025-02-03 21:28 11072
{
1193b4be Poul Sander 2025-02-03 21:28 11073
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11074
1193b4be Poul Sander 2025-02-03 21:28 11075
    // Check user IM_ASSERT macro
1193b4be Poul Sander 2025-02-03 21:28 11076
    // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
1193b4be Poul Sander 2025-02-03 21:28 11077
    //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
1193b4be Poul Sander 2025-02-03 21:28 11078
    //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
1193b4be Poul Sander 2025-02-03 21:28 11079
    // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
1193b4be Poul Sander 2025-02-03 21:28 11080
    // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
1193b4be Poul Sander 2025-02-03 21:28 11081
    if (true) IM_ASSERT(1); else IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 11082
1193b4be Poul Sander 2025-02-03 21:28 11083
    // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
1193b4be Poul Sander 2025-02-03 21:28 11084
    // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
1193b4be Poul Sander 2025-02-03 21:28 11085
#ifdef __EMSCRIPTEN__
1193b4be Poul Sander 2025-02-03 21:28 11086
    if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
1193b4be Poul Sander 2025-02-03 21:28 11087
        g.IO.DeltaTime = 0.00001f;
1193b4be Poul Sander 2025-02-03 21:28 11088
#endif
1193b4be Poul Sander 2025-02-03 21:28 11089
1193b4be Poul Sander 2025-02-03 21:28 11090
    // Check user data
1193b4be Poul Sander 2025-02-03 21:28 11091
    // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
1193b4be Poul Sander 2025-02-03 21:28 11092
    IM_ASSERT(g.Initialized);
1193b4be Poul Sander 2025-02-03 21:28 11093
    IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
1193b4be Poul Sander 2025-02-03 21:28 11094
    IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
1193b4be Poul Sander 2025-02-03 21:28 11095
    IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
1193b4be Poul Sander 2025-02-03 21:28 11096
    IM_ASSERT(g.IO.Fonts->IsBuilt()                                     && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
1193b4be Poul Sander 2025-02-03 21:28 11097
    IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
1193b4be Poul Sander 2025-02-03 21:28 11098
    IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f                 && "Invalid style setting!");
1193b4be Poul Sander 2025-02-03 21:28 11099
    IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
1193b4be Poul Sander 2025-02-03 21:28 11100
    IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
1193b4be Poul Sander 2025-02-03 21:28 11101
    IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
1193b4be Poul Sander 2025-02-03 21:28 11102
    IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
1193b4be Poul Sander 2025-02-03 21:28 11103
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 11104
    for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
1193b4be Poul Sander 2025-02-03 21:28 11105
        IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
1193b4be Poul Sander 2025-02-03 21:28 11106
1193b4be Poul Sander 2025-02-03 21:28 11107
    // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
1193b4be Poul Sander 2025-02-03 21:28 11108
    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
1193b4be Poul Sander 2025-02-03 21:28 11109
        IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
1193b4be Poul Sander 2025-02-03 21:28 11110
#endif
1193b4be Poul Sander 2025-02-03 21:28 11111
1193b4be Poul Sander 2025-02-03 21:28 11112
    // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
1193b4be Poul Sander 2025-02-03 21:28 11113
    if (g.IO.ConfigErrorRecovery)
1193b4be Poul Sander 2025-02-03 21:28 11114
        IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
1193b4be Poul Sander 2025-02-03 21:28 11115
1193b4be Poul Sander 2025-02-03 21:28 11116
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 11117
    // Remap legacy names
1193b4be Poul Sander 2025-02-03 21:28 11118
    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
1193b4be Poul Sander 2025-02-03 21:28 11119
    {
1193b4be Poul Sander 2025-02-03 21:28 11120
        g.IO.ConfigNavMoveSetMousePos = true;
1193b4be Poul Sander 2025-02-03 21:28 11121
        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
1193b4be Poul Sander 2025-02-03 21:28 11122
    }
1193b4be Poul Sander 2025-02-03 21:28 11123
    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
1193b4be Poul Sander 2025-02-03 21:28 11124
    {
1193b4be Poul Sander 2025-02-03 21:28 11125
        g.IO.ConfigNavCaptureKeyboard = false;
1193b4be Poul Sander 2025-02-03 21:28 11126
        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
1193b4be Poul Sander 2025-02-03 21:28 11127
    }
1193b4be Poul Sander 2025-02-03 21:28 11128
1193b4be Poul Sander 2025-02-03 21:28 11129
    // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
1193b4be Poul Sander 2025-02-03 21:28 11130
    if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
1193b4be Poul Sander 2025-02-03 21:28 11131
        g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
1193b4be Poul Sander 2025-02-03 21:28 11132
    if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
1193b4be Poul Sander 2025-02-03 21:28 11133
        g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
1193b4be Poul Sander 2025-02-03 21:28 11134
#endif
1193b4be Poul Sander 2025-02-03 21:28 11135
1193b4be Poul Sander 2025-02-03 21:28 11136
    // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data.
1193b4be Poul Sander 2025-02-03 21:28 11137
    if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
1193b4be Poul Sander 2025-02-03 21:28 11138
        IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
1193b4be Poul Sander 2025-02-03 21:28 11139
    if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
1193b4be Poul Sander 2025-02-03 21:28 11140
        IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
1193b4be Poul Sander 2025-02-03 21:28 11141
1193b4be Poul Sander 2025-02-03 21:28 11142
    // Perform simple checks: multi-viewport and platform windows support
1193b4be Poul Sander 2025-02-03 21:28 11143
    if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
1193b4be Poul Sander 2025-02-03 21:28 11144
    {
1193b4be Poul Sander 2025-02-03 21:28 11145
        if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
1193b4be Poul Sander 2025-02-03 21:28 11146
        {
1193b4be Poul Sander 2025-02-03 21:28 11147
            IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
1193b4be Poul Sander 2025-02-03 21:28 11148
            IM_ASSERT(g.PlatformIO.Platform_CreateWindow  != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11149
            IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11150
            IM_ASSERT(g.PlatformIO.Platform_GetWindowPos  != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11151
            IM_ASSERT(g.PlatformIO.Platform_SetWindowPos  != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11152
            IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11153
            IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
1193b4be Poul Sander 2025-02-03 21:28 11154
            IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
1193b4be Poul Sander 2025-02-03 21:28 11155
            IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
1193b4be Poul Sander 2025-02-03 21:28 11156
            if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
1193b4be Poul Sander 2025-02-03 21:28 11157
                IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
1193b4be Poul Sander 2025-02-03 21:28 11158
        }
1193b4be Poul Sander 2025-02-03 21:28 11159
        else
1193b4be Poul Sander 2025-02-03 21:28 11160
        {
1193b4be Poul Sander 2025-02-03 21:28 11161
            // Disable feature, our backends do not support it
1193b4be Poul Sander 2025-02-03 21:28 11162
            g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
1193b4be Poul Sander 2025-02-03 21:28 11163
        }
1193b4be Poul Sander 2025-02-03 21:28 11164
1193b4be Poul Sander 2025-02-03 21:28 11165
        // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
1193b4be Poul Sander 2025-02-03 21:28 11166
        for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors)
1193b4be Poul Sander 2025-02-03 21:28 11167
        {
1193b4be Poul Sander 2025-02-03 21:28 11168
            IM_UNUSED(mon);
1193b4be Poul Sander 2025-02-03 21:28 11169
            IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
1193b4be Poul Sander 2025-02-03 21:28 11170
            IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them.");
1193b4be Poul Sander 2025-02-03 21:28 11171
            IM_ASSERT(mon.DpiScale != 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 11172
        }
1193b4be Poul Sander 2025-02-03 21:28 11173
    }
1193b4be Poul Sander 2025-02-03 21:28 11174
}
1193b4be Poul Sander 2025-02-03 21:28 11175
1193b4be Poul Sander 2025-02-03 21:28 11176
static void ImGui::ErrorCheckEndFrameSanityChecks()
1193b4be Poul Sander 2025-02-03 21:28 11177
{
1193b4be Poul Sander 2025-02-03 21:28 11178
    // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
1193b4be Poul Sander 2025-02-03 21:28 11179
    // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
1193b4be Poul Sander 2025-02-03 21:28 11180
    // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
1193b4be Poul Sander 2025-02-03 21:28 11181
    // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
1193b4be Poul Sander 2025-02-03 21:28 11182
    // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
1193b4be Poul Sander 2025-02-03 21:28 11183
    // while still correctly asserting on mid-frame key press events.
1193b4be Poul Sander 2025-02-03 21:28 11184
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11185
    const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
1193b4be Poul Sander 2025-02-03 21:28 11186
    IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 11187
    IM_UNUSED(key_mods);
1193b4be Poul Sander 2025-02-03 21:28 11188
    IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
1193b4be Poul Sander 2025-02-03 21:28 11189
    IM_UNUSED(key_mods);
1193b4be Poul Sander 2025-02-03 21:28 11190
1193b4be Poul Sander 2025-02-03 21:28 11191
    IM_ASSERT(g.CurrentWindowStack.Size == 1);
1193b4be Poul Sander 2025-02-03 21:28 11192
    IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
1193b4be Poul Sander 2025-02-03 21:28 11193
}
1193b4be Poul Sander 2025-02-03 21:28 11194
1193b4be Poul Sander 2025-02-03 21:28 11195
// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
1193b4be Poul Sander 2025-02-03 21:28 11196
void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
1193b4be Poul Sander 2025-02-03 21:28 11197
{
1193b4be Poul Sander 2025-02-03 21:28 11198
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11199
    state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11200
    state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11201
    state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
1193b4be Poul Sander 2025-02-03 21:28 11202
    state_out->SizeOfColorStack = (short)g.ColorStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11203
    state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11204
    state_out->SizeOfFontStack = (short)g.FontStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11205
    state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11206
    state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11207
    state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11208
    state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 11209
    state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
1193b4be Poul Sander 2025-02-03 21:28 11210
}
1193b4be Poul Sander 2025-02-03 21:28 11211
1193b4be Poul Sander 2025-02-03 21:28 11212
// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
1193b4be Poul Sander 2025-02-03 21:28 11213
// Called by e.g. EndFrame() but may be called for manual recovery.
1193b4be Poul Sander 2025-02-03 21:28 11214
// Attempt to recover full window stack.
1193b4be Poul Sander 2025-02-03 21:28 11215
void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
1193b4be Poul Sander 2025-02-03 21:28 11216
{
1193b4be Poul Sander 2025-02-03 21:28 11217
    // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
1193b4be Poul Sander 2025-02-03 21:28 11218
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11219
    while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11220
    {
1193b4be Poul Sander 2025-02-03 21:28 11221
        // Recap:
1193b4be Poul Sander 2025-02-03 21:28 11222
        // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
1193b4be Poul Sander 2025-02-03 21:28 11223
        // - Always call a matching End() for each Begin() call, regardless of its return value!
1193b4be Poul Sander 2025-02-03 21:28 11224
        // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
1193b4be Poul Sander 2025-02-03 21:28 11225
        // - We will fix that in a future major update.
1193b4be Poul Sander 2025-02-03 21:28 11226
        ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11227
        if (window->Flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 11228
        {
1193b4be Poul Sander 2025-02-03 21:28 11229
            IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
1193b4be Poul Sander 2025-02-03 21:28 11230
            EndChild();
1193b4be Poul Sander 2025-02-03 21:28 11231
        }
1193b4be Poul Sander 2025-02-03 21:28 11232
        else
1193b4be Poul Sander 2025-02-03 21:28 11233
        {
1193b4be Poul Sander 2025-02-03 21:28 11234
            IM_ASSERT_USER_ERROR(0, "Missing End()");
1193b4be Poul Sander 2025-02-03 21:28 11235
            End();
1193b4be Poul Sander 2025-02-03 21:28 11236
        }
1193b4be Poul Sander 2025-02-03 21:28 11237
    }
1193b4be Poul Sander 2025-02-03 21:28 11238
    if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
1193b4be Poul Sander 2025-02-03 21:28 11239
        ErrorRecoveryTryToRecoverWindowState(state_in);
1193b4be Poul Sander 2025-02-03 21:28 11240
}
1193b4be Poul Sander 2025-02-03 21:28 11241
1193b4be Poul Sander 2025-02-03 21:28 11242
// Called by e.g. End() but may be called for manual recovery.
1193b4be Poul Sander 2025-02-03 21:28 11243
// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
1193b4be Poul Sander 2025-02-03 21:28 11244
// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
1193b4be Poul Sander 2025-02-03 21:28 11245
void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
1193b4be Poul Sander 2025-02-03 21:28 11246
{
1193b4be Poul Sander 2025-02-03 21:28 11247
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11248
1193b4be Poul Sander 2025-02-03 21:28 11249
    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11250
    {
1193b4be Poul Sander 2025-02-03 21:28 11251
        IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
1193b4be Poul Sander 2025-02-03 21:28 11252
        EndTable();
1193b4be Poul Sander 2025-02-03 21:28 11253
    }
1193b4be Poul Sander 2025-02-03 21:28 11254
1193b4be Poul Sander 2025-02-03 21:28 11255
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11256
1193b4be Poul Sander 2025-02-03 21:28 11257
    // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
1193b4be Poul Sander 2025-02-03 21:28 11258
    while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11259
    {
1193b4be Poul Sander 2025-02-03 21:28 11260
        IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
1193b4be Poul Sander 2025-02-03 21:28 11261
        EndTabBar();
1193b4be Poul Sander 2025-02-03 21:28 11262
    }
1193b4be Poul Sander 2025-02-03 21:28 11263
    while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11264
    {
1193b4be Poul Sander 2025-02-03 21:28 11265
        IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
1193b4be Poul Sander 2025-02-03 21:28 11266
        EndMultiSelect();
1193b4be Poul Sander 2025-02-03 21:28 11267
    }
1193b4be Poul Sander 2025-02-03 21:28 11268
    while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11269
    {
1193b4be Poul Sander 2025-02-03 21:28 11270
        IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
1193b4be Poul Sander 2025-02-03 21:28 11271
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 11272
    }
1193b4be Poul Sander 2025-02-03 21:28 11273
    while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11274
    {
1193b4be Poul Sander 2025-02-03 21:28 11275
        IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
1193b4be Poul Sander 2025-02-03 21:28 11276
        EndGroup();
1193b4be Poul Sander 2025-02-03 21:28 11277
    }
1193b4be Poul Sander 2025-02-03 21:28 11278
    IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
1193b4be Poul Sander 2025-02-03 21:28 11279
    while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11280
    {
1193b4be Poul Sander 2025-02-03 21:28 11281
        IM_ASSERT_USER_ERROR(0, "Missing PopID()");
1193b4be Poul Sander 2025-02-03 21:28 11282
        PopID();
1193b4be Poul Sander 2025-02-03 21:28 11283
    }
1193b4be Poul Sander 2025-02-03 21:28 11284
    while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11285
    {
1193b4be Poul Sander 2025-02-03 21:28 11286
        IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
1193b4be Poul Sander 2025-02-03 21:28 11287
        if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
1193b4be Poul Sander 2025-02-03 21:28 11288
            EndDisabled();
1193b4be Poul Sander 2025-02-03 21:28 11289
        else
1193b4be Poul Sander 2025-02-03 21:28 11290
        {
1193b4be Poul Sander 2025-02-03 21:28 11291
            EndDisabledOverrideReenable();
1193b4be Poul Sander 2025-02-03 21:28 11292
            g.CurrentWindowStack.back().DisabledOverrideReenable = false;
1193b4be Poul Sander 2025-02-03 21:28 11293
        }
1193b4be Poul Sander 2025-02-03 21:28 11294
    }
1193b4be Poul Sander 2025-02-03 21:28 11295
    IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
1193b4be Poul Sander 2025-02-03 21:28 11296
    while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11297
    {
1193b4be Poul Sander 2025-02-03 21:28 11298
        IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
1193b4be Poul Sander 2025-02-03 21:28 11299
        PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 11300
    }
1193b4be Poul Sander 2025-02-03 21:28 11301
    while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11302
    {
1193b4be Poul Sander 2025-02-03 21:28 11303
        IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
1193b4be Poul Sander 2025-02-03 21:28 11304
        PopItemFlag();
1193b4be Poul Sander 2025-02-03 21:28 11305
    }
1193b4be Poul Sander 2025-02-03 21:28 11306
    while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11307
    {
1193b4be Poul Sander 2025-02-03 21:28 11308
        IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
1193b4be Poul Sander 2025-02-03 21:28 11309
        PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 11310
    }
1193b4be Poul Sander 2025-02-03 21:28 11311
    while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11312
    {
1193b4be Poul Sander 2025-02-03 21:28 11313
        IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
1193b4be Poul Sander 2025-02-03 21:28 11314
        PopFont();
1193b4be Poul Sander 2025-02-03 21:28 11315
    }
1193b4be Poul Sander 2025-02-03 21:28 11316
    while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
1193b4be Poul Sander 2025-02-03 21:28 11317
    {
1193b4be Poul Sander 2025-02-03 21:28 11318
        IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
1193b4be Poul Sander 2025-02-03 21:28 11319
        PopFocusScope();
1193b4be Poul Sander 2025-02-03 21:28 11320
    }
1193b4be Poul Sander 2025-02-03 21:28 11321
    //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
1193b4be Poul Sander 2025-02-03 21:28 11322
}
1193b4be Poul Sander 2025-02-03 21:28 11323
1193b4be Poul Sander 2025-02-03 21:28 11324
bool    ImGui::ErrorLog(const char* msg)
1193b4be Poul Sander 2025-02-03 21:28 11325
{
1193b4be Poul Sander 2025-02-03 21:28 11326
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11327
1193b4be Poul Sander 2025-02-03 21:28 11328
    // Output to debug log
1193b4be Poul Sander 2025-02-03 21:28 11329
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 11330
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11331
1193b4be Poul Sander 2025-02-03 21:28 11332
    if (g.IO.ConfigErrorRecoveryEnableDebugLog)
1193b4be Poul Sander 2025-02-03 21:28 11333
    {
1193b4be Poul Sander 2025-02-03 21:28 11334
        if (g.ErrorFirst)
1193b4be Poul Sander 2025-02-03 21:28 11335
            IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
1193b4be Poul Sander 2025-02-03 21:28 11336
                g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
1193b4be Poul Sander 2025-02-03 21:28 11337
        IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
1193b4be Poul Sander 2025-02-03 21:28 11338
    }
1193b4be Poul Sander 2025-02-03 21:28 11339
    g.ErrorFirst = false;
1193b4be Poul Sander 2025-02-03 21:28 11340
1193b4be Poul Sander 2025-02-03 21:28 11341
    // Output to tooltip
1193b4be Poul Sander 2025-02-03 21:28 11342
    if (g.IO.ConfigErrorRecoveryEnableTooltip)
1193b4be Poul Sander 2025-02-03 21:28 11343
    {
1193b4be Poul Sander 2025-02-03 21:28 11344
        if (BeginErrorTooltip())
1193b4be Poul Sander 2025-02-03 21:28 11345
        {
1193b4be Poul Sander 2025-02-03 21:28 11346
            if (g.ErrorCountCurrentFrame < 20)
1193b4be Poul Sander 2025-02-03 21:28 11347
            {
1193b4be Poul Sander 2025-02-03 21:28 11348
                Text("In window '%s': %s", window ? window->Name : "NULL", msg);
1193b4be Poul Sander 2025-02-03 21:28 11349
                if (window && (!window->IsFallbackWindow || window->WasActive))
1193b4be Poul Sander 2025-02-03 21:28 11350
                    GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 11351
            }
1193b4be Poul Sander 2025-02-03 21:28 11352
            if (g.ErrorCountCurrentFrame == 20)
1193b4be Poul Sander 2025-02-03 21:28 11353
                Text("(and more errors)");
1193b4be Poul Sander 2025-02-03 21:28 11354
            // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
1193b4be Poul Sander 2025-02-03 21:28 11355
            EndErrorTooltip();
1193b4be Poul Sander 2025-02-03 21:28 11356
        }
1193b4be Poul Sander 2025-02-03 21:28 11357
        g.ErrorCountCurrentFrame++;
1193b4be Poul Sander 2025-02-03 21:28 11358
    }
1193b4be Poul Sander 2025-02-03 21:28 11359
#endif
1193b4be Poul Sander 2025-02-03 21:28 11360
1193b4be Poul Sander 2025-02-03 21:28 11361
    // Output to callback
1193b4be Poul Sander 2025-02-03 21:28 11362
    if (g.ErrorCallback != NULL)
1193b4be Poul Sander 2025-02-03 21:28 11363
        g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
1193b4be Poul Sander 2025-02-03 21:28 11364
1193b4be Poul Sander 2025-02-03 21:28 11365
    // Return whether we should assert
1193b4be Poul Sander 2025-02-03 21:28 11366
    return g.IO.ConfigErrorRecoveryEnableAssert;
1193b4be Poul Sander 2025-02-03 21:28 11367
}
1193b4be Poul Sander 2025-02-03 21:28 11368
1193b4be Poul Sander 2025-02-03 21:28 11369
void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
1193b4be Poul Sander 2025-02-03 21:28 11370
{
1193b4be Poul Sander 2025-02-03 21:28 11371
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 11372
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11373
    if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
1193b4be Poul Sander 2025-02-03 21:28 11374
        g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
1193b4be Poul Sander 2025-02-03 21:28 11375
    if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
1193b4be Poul Sander 2025-02-03 21:28 11376
    {
1193b4be Poul Sander 2025-02-03 21:28 11377
        Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
1193b4be Poul Sander 2025-02-03 21:28 11378
        BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
1193b4be Poul Sander 2025-02-03 21:28 11379
        BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
1193b4be Poul Sander 2025-02-03 21:28 11380
        //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
1193b4be Poul Sander 2025-02-03 21:28 11381
        BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds.");
1193b4be Poul Sander 2025-02-03 21:28 11382
        Separator();
1193b4be Poul Sander 2025-02-03 21:28 11383
        Text("(Hold CTRL to: use");
1193b4be Poul Sander 2025-02-03 21:28 11384
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11385
        if (SmallButton("Item Picker"))
1193b4be Poul Sander 2025-02-03 21:28 11386
            DebugStartItemPicker();
1193b4be Poul Sander 2025-02-03 21:28 11387
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11388
        Text("to break in item call-stack, or");
1193b4be Poul Sander 2025-02-03 21:28 11389
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11390
        if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
1193b4be Poul Sander 2025-02-03 21:28 11391
            g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
1193b4be Poul Sander 2025-02-03 21:28 11392
        EndErrorTooltip();
1193b4be Poul Sander 2025-02-03 21:28 11393
    }
1193b4be Poul Sander 2025-02-03 21:28 11394
1193b4be Poul Sander 2025-02-03 21:28 11395
    if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
1193b4be Poul Sander 2025-02-03 21:28 11396
    {
1193b4be Poul Sander 2025-02-03 21:28 11397
        Separator();
1193b4be Poul Sander 2025-02-03 21:28 11398
        Text("(Hold CTRL to:");
1193b4be Poul Sander 2025-02-03 21:28 11399
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11400
        if (SmallButton("Enable Asserts"))
1193b4be Poul Sander 2025-02-03 21:28 11401
            g.IO.ConfigErrorRecoveryEnableAssert = true;
1193b4be Poul Sander 2025-02-03 21:28 11402
        //SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11403
        //if (SmallButton("Hide Error Tooltips"))
1193b4be Poul Sander 2025-02-03 21:28 11404
        //    g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
1193b4be Poul Sander 2025-02-03 21:28 11405
        SameLine(0, 0);
1193b4be Poul Sander 2025-02-03 21:28 11406
        Text(")");
1193b4be Poul Sander 2025-02-03 21:28 11407
        EndErrorTooltip();
1193b4be Poul Sander 2025-02-03 21:28 11408
    }
1193b4be Poul Sander 2025-02-03 21:28 11409
#endif
1193b4be Poul Sander 2025-02-03 21:28 11410
}
1193b4be Poul Sander 2025-02-03 21:28 11411
1193b4be Poul Sander 2025-02-03 21:28 11412
// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
1193b4be Poul Sander 2025-02-03 21:28 11413
bool ImGui::BeginErrorTooltip()
1193b4be Poul Sander 2025-02-03 21:28 11414
{
1193b4be Poul Sander 2025-02-03 21:28 11415
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11416
    ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
1193b4be Poul Sander 2025-02-03 21:28 11417
    const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
1193b4be Poul Sander 2025-02-03 21:28 11418
    PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
1193b4be Poul Sander 2025-02-03 21:28 11419
    if (use_locked_pos)
1193b4be Poul Sander 2025-02-03 21:28 11420
        SetNextWindowPos(g.ErrorTooltipLockedPos);
1193b4be Poul Sander 2025-02-03 21:28 11421
    bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
1193b4be Poul Sander 2025-02-03 21:28 11422
    PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 11423
    if (is_visible && g.CurrentWindow->BeginCount == 1)
1193b4be Poul Sander 2025-02-03 21:28 11424
    {
1193b4be Poul Sander 2025-02-03 21:28 11425
        SeparatorText("MESSAGE FROM DEAR IMGUI");
1193b4be Poul Sander 2025-02-03 21:28 11426
        BringWindowToDisplayFront(g.CurrentWindow);
1193b4be Poul Sander 2025-02-03 21:28 11427
        BringWindowToFocusFront(g.CurrentWindow);
1193b4be Poul Sander 2025-02-03 21:28 11428
        g.ErrorTooltipLockedPos = GetWindowPos();
1193b4be Poul Sander 2025-02-03 21:28 11429
    }
1193b4be Poul Sander 2025-02-03 21:28 11430
    else if (!is_visible)
1193b4be Poul Sander 2025-02-03 21:28 11431
    {
1193b4be Poul Sander 2025-02-03 21:28 11432
        End();
1193b4be Poul Sander 2025-02-03 21:28 11433
    }
1193b4be Poul Sander 2025-02-03 21:28 11434
    return is_visible;
1193b4be Poul Sander 2025-02-03 21:28 11435
}
1193b4be Poul Sander 2025-02-03 21:28 11436
1193b4be Poul Sander 2025-02-03 21:28 11437
void ImGui::EndErrorTooltip()
1193b4be Poul Sander 2025-02-03 21:28 11438
{
1193b4be Poul Sander 2025-02-03 21:28 11439
    End();
1193b4be Poul Sander 2025-02-03 21:28 11440
}
1193b4be Poul Sander 2025-02-03 21:28 11441
1193b4be Poul Sander 2025-02-03 21:28 11442
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11443
// [SECTION] ITEM SUBMISSION
1193b4be Poul Sander 2025-02-03 21:28 11444
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11445
// - KeepAliveID()
1193b4be Poul Sander 2025-02-03 21:28 11446
// - ItemAdd()
1193b4be Poul Sander 2025-02-03 21:28 11447
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11448
1193b4be Poul Sander 2025-02-03 21:28 11449
// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
1193b4be Poul Sander 2025-02-03 21:28 11450
void ImGui::KeepAliveID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 11451
{
1193b4be Poul Sander 2025-02-03 21:28 11452
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11453
    if (g.ActiveId == id)
1193b4be Poul Sander 2025-02-03 21:28 11454
        g.ActiveIdIsAlive = id;
1193b4be Poul Sander 2025-02-03 21:28 11455
    if (g.ActiveIdPreviousFrame == id)
1193b4be Poul Sander 2025-02-03 21:28 11456
        g.ActiveIdPreviousFrameIsAlive = true;
1193b4be Poul Sander 2025-02-03 21:28 11457
}
1193b4be Poul Sander 2025-02-03 21:28 11458
1193b4be Poul Sander 2025-02-03 21:28 11459
// Declare item bounding box for clipping and interaction.
1193b4be Poul Sander 2025-02-03 21:28 11460
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
1193b4be Poul Sander 2025-02-03 21:28 11461
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
1193b4be Poul Sander 2025-02-03 21:28 11462
// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
1193b4be Poul Sander 2025-02-03 21:28 11463
IM_MSVC_RUNTIME_CHECKS_OFF
1193b4be Poul Sander 2025-02-03 21:28 11464
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
1193b4be Poul Sander 2025-02-03 21:28 11465
{
1193b4be Poul Sander 2025-02-03 21:28 11466
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11467
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11468
1193b4be Poul Sander 2025-02-03 21:28 11469
    // Set item data
1193b4be Poul Sander 2025-02-03 21:28 11470
    // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
1193b4be Poul Sander 2025-02-03 21:28 11471
    g.LastItemData.ID = id;
1193b4be Poul Sander 2025-02-03 21:28 11472
    g.LastItemData.Rect = bb;
1193b4be Poul Sander 2025-02-03 21:28 11473
    g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
1193b4be Poul Sander 2025-02-03 21:28 11474
    g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
1193b4be Poul Sander 2025-02-03 21:28 11475
    g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 11476
    // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
1193b4be Poul Sander 2025-02-03 21:28 11477
1193b4be Poul Sander 2025-02-03 21:28 11478
    if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 11479
    {
1193b4be Poul Sander 2025-02-03 21:28 11480
        KeepAliveID(id);
1193b4be Poul Sander 2025-02-03 21:28 11481
1193b4be Poul Sander 2025-02-03 21:28 11482
        // Directional navigation processing
1193b4be Poul Sander 2025-02-03 21:28 11483
        // Runs prior to clipping early-out
1193b4be Poul Sander 2025-02-03 21:28 11484
        //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
1193b4be Poul Sander 2025-02-03 21:28 11485
        //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
1193b4be Poul Sander 2025-02-03 21:28 11486
        //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
1193b4be Poul Sander 2025-02-03 21:28 11487
        //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
1193b4be Poul Sander 2025-02-03 21:28 11488
        //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
1193b4be Poul Sander 2025-02-03 21:28 11489
        //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
1193b4be Poul Sander 2025-02-03 21:28 11490
        // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
1193b4be Poul Sander 2025-02-03 21:28 11491
        // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
1193b4be Poul Sander 2025-02-03 21:28 11492
        if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
1193b4be Poul Sander 2025-02-03 21:28 11493
        {
1193b4be Poul Sander 2025-02-03 21:28 11494
            // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
1193b4be Poul Sander 2025-02-03 21:28 11495
            window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
1193b4be Poul Sander 2025-02-03 21:28 11496
            if (g.NavId == id || g.NavAnyRequest)
1193b4be Poul Sander 2025-02-03 21:28 11497
                if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
1193b4be Poul Sander 2025-02-03 21:28 11498
                    if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
1193b4be Poul Sander 2025-02-03 21:28 11499
                        NavProcessItem();
1193b4be Poul Sander 2025-02-03 21:28 11500
        }
1193b4be Poul Sander 2025-02-03 21:28 11501
1193b4be Poul Sander 2025-02-03 21:28 11502
        if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
1193b4be Poul Sander 2025-02-03 21:28 11503
            ItemHandleShortcut(id);
1193b4be Poul Sander 2025-02-03 21:28 11504
    }
1193b4be Poul Sander 2025-02-03 21:28 11505
1193b4be Poul Sander 2025-02-03 21:28 11506
    // Lightweight clear of SetNextItemXXX data.
1193b4be Poul Sander 2025-02-03 21:28 11507
    g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 11508
    g.NextItemData.ItemFlags = ImGuiItemFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 11509
1193b4be Poul Sander 2025-02-03 21:28 11510
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 11511
    if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 11512
        IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
1193b4be Poul Sander 2025-02-03 21:28 11513
#endif
1193b4be Poul Sander 2025-02-03 21:28 11514
1193b4be Poul Sander 2025-02-03 21:28 11515
    // Clipping test
1193b4be Poul Sander 2025-02-03 21:28 11516
    // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
1193b4be Poul Sander 2025-02-03 21:28 11517
    // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
1193b4be Poul Sander 2025-02-03 21:28 11518
    const bool is_rect_visible = bb.Overlaps(window->ClipRect);
1193b4be Poul Sander 2025-02-03 21:28 11519
    if (!is_rect_visible)
1193b4be Poul Sander 2025-02-03 21:28 11520
        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
1193b4be Poul Sander 2025-02-03 21:28 11521
            if (!g.ItemUnclipByLog)
1193b4be Poul Sander 2025-02-03 21:28 11522
                return false;
1193b4be Poul Sander 2025-02-03 21:28 11523
1193b4be Poul Sander 2025-02-03 21:28 11524
    // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 11525
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 11526
    if (id != 0)
1193b4be Poul Sander 2025-02-03 21:28 11527
    {
1193b4be Poul Sander 2025-02-03 21:28 11528
        if (id == g.DebugLocateId)
1193b4be Poul Sander 2025-02-03 21:28 11529
            DebugLocateItemResolveWithLastItem();
1193b4be Poul Sander 2025-02-03 21:28 11530
1193b4be Poul Sander 2025-02-03 21:28 11531
        // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
1193b4be Poul Sander 2025-02-03 21:28 11532
        // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
1193b4be Poul Sander 2025-02-03 21:28 11533
        // READ THE FAQ: https://dearimgui.com/faq
1193b4be Poul Sander 2025-02-03 21:28 11534
        IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
1193b4be Poul Sander 2025-02-03 21:28 11535
    }
1193b4be Poul Sander 2025-02-03 21:28 11536
    //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 11537
    //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
1193b4be Poul Sander 2025-02-03 21:28 11538
    //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 11539
#endif
1193b4be Poul Sander 2025-02-03 21:28 11540
1193b4be Poul Sander 2025-02-03 21:28 11541
    // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
1193b4be Poul Sander 2025-02-03 21:28 11542
    if (is_rect_visible)
1193b4be Poul Sander 2025-02-03 21:28 11543
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
1193b4be Poul Sander 2025-02-03 21:28 11544
    if (IsMouseHoveringRect(bb.Min, bb.Max))
1193b4be Poul Sander 2025-02-03 21:28 11545
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
1193b4be Poul Sander 2025-02-03 21:28 11546
    return true;
1193b4be Poul Sander 2025-02-03 21:28 11547
}
1193b4be Poul Sander 2025-02-03 21:28 11548
IM_MSVC_RUNTIME_CHECKS_RESTORE
1193b4be Poul Sander 2025-02-03 21:28 11549
1193b4be Poul Sander 2025-02-03 21:28 11550
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11551
// [SECTION] LAYOUT
1193b4be Poul Sander 2025-02-03 21:28 11552
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11553
// - ItemSize()
1193b4be Poul Sander 2025-02-03 21:28 11554
// - SameLine()
1193b4be Poul Sander 2025-02-03 21:28 11555
// - GetCursorScreenPos()
1193b4be Poul Sander 2025-02-03 21:28 11556
// - SetCursorScreenPos()
1193b4be Poul Sander 2025-02-03 21:28 11557
// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
1193b4be Poul Sander 2025-02-03 21:28 11558
// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
1193b4be Poul Sander 2025-02-03 21:28 11559
// - GetCursorStartPos()
1193b4be Poul Sander 2025-02-03 21:28 11560
// - Indent()
1193b4be Poul Sander 2025-02-03 21:28 11561
// - Unindent()
1193b4be Poul Sander 2025-02-03 21:28 11562
// - SetNextItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11563
// - PushItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11564
// - PushMultiItemsWidths()
1193b4be Poul Sander 2025-02-03 21:28 11565
// - PopItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11566
// - CalcItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11567
// - CalcItemSize()
1193b4be Poul Sander 2025-02-03 21:28 11568
// - GetTextLineHeight()
1193b4be Poul Sander 2025-02-03 21:28 11569
// - GetTextLineHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 11570
// - GetFrameHeight()
1193b4be Poul Sander 2025-02-03 21:28 11571
// - GetFrameHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 11572
// - GetContentRegionMax()
1193b4be Poul Sander 2025-02-03 21:28 11573
// - GetContentRegionAvail(),
1193b4be Poul Sander 2025-02-03 21:28 11574
// - BeginGroup()
1193b4be Poul Sander 2025-02-03 21:28 11575
// - EndGroup()
1193b4be Poul Sander 2025-02-03 21:28 11576
// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
1193b4be Poul Sander 2025-02-03 21:28 11577
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11578
1193b4be Poul Sander 2025-02-03 21:28 11579
// Advance cursor given item size for layout.
1193b4be Poul Sander 2025-02-03 21:28 11580
// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
1193b4be Poul Sander 2025-02-03 21:28 11581
// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
1193b4be Poul Sander 2025-02-03 21:28 11582
// THIS IS IN THE PERFORMANCE CRITICAL PATH.
1193b4be Poul Sander 2025-02-03 21:28 11583
IM_MSVC_RUNTIME_CHECKS_OFF
1193b4be Poul Sander 2025-02-03 21:28 11584
void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
1193b4be Poul Sander 2025-02-03 21:28 11585
{
1193b4be Poul Sander 2025-02-03 21:28 11586
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11587
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11588
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 11589
        return;
1193b4be Poul Sander 2025-02-03 21:28 11590
1193b4be Poul Sander 2025-02-03 21:28 11591
    // We increase the height in this function to accommodate for baseline offset.
1193b4be Poul Sander 2025-02-03 21:28 11592
    // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
1193b4be Poul Sander 2025-02-03 21:28 11593
    // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
1193b4be Poul Sander 2025-02-03 21:28 11594
    const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 11595
1193b4be Poul Sander 2025-02-03 21:28 11596
    const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
1193b4be Poul Sander 2025-02-03 21:28 11597
    const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
1193b4be Poul Sander 2025-02-03 21:28 11598
1193b4be Poul Sander 2025-02-03 21:28 11599
    // Always align ourselves on pixel boundaries
1193b4be Poul Sander 2025-02-03 21:28 11600
    //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 11601
    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
1193b4be Poul Sander 2025-02-03 21:28 11602
    window->DC.CursorPosPrevLine.y = line_y1;
1193b4be Poul Sander 2025-02-03 21:28 11603
    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
1193b4be Poul Sander 2025-02-03 21:28 11604
    window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y);                       // Next line
1193b4be Poul Sander 2025-02-03 21:28 11605
    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
1193b4be Poul Sander 2025-02-03 21:28 11606
    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
1193b4be Poul Sander 2025-02-03 21:28 11607
    //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 11608
1193b4be Poul Sander 2025-02-03 21:28 11609
    window->DC.PrevLineSize.y = line_height;
1193b4be Poul Sander 2025-02-03 21:28 11610
    window->DC.CurrLineSize.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 11611
    window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
1193b4be Poul Sander 2025-02-03 21:28 11612
    window->DC.CurrLineTextBaseOffset = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 11613
    window->DC.IsSameLine = window->DC.IsSetPos = false;
1193b4be Poul Sander 2025-02-03 21:28 11614
1193b4be Poul Sander 2025-02-03 21:28 11615
    // Horizontal layout mode
1193b4be Poul Sander 2025-02-03 21:28 11616
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
1193b4be Poul Sander 2025-02-03 21:28 11617
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 11618
}
1193b4be Poul Sander 2025-02-03 21:28 11619
IM_MSVC_RUNTIME_CHECKS_RESTORE
1193b4be Poul Sander 2025-02-03 21:28 11620
1193b4be Poul Sander 2025-02-03 21:28 11621
// Gets back to previous line and continue with horizontal layout
1193b4be Poul Sander 2025-02-03 21:28 11622
//      offset_from_start_x == 0 : follow right after previous item
1193b4be Poul Sander 2025-02-03 21:28 11623
//      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
1193b4be Poul Sander 2025-02-03 21:28 11624
//      spacing_w < 0            : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
1193b4be Poul Sander 2025-02-03 21:28 11625
//      spacing_w >= 0           : enforce spacing amount
1193b4be Poul Sander 2025-02-03 21:28 11626
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
1193b4be Poul Sander 2025-02-03 21:28 11627
{
1193b4be Poul Sander 2025-02-03 21:28 11628
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11629
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11630
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 11631
        return;
1193b4be Poul Sander 2025-02-03 21:28 11632
1193b4be Poul Sander 2025-02-03 21:28 11633
    if (offset_from_start_x != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11634
    {
1193b4be Poul Sander 2025-02-03 21:28 11635
        if (spacing_w < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11636
            spacing_w = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 11637
        window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
1193b4be Poul Sander 2025-02-03 21:28 11638
        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
1193b4be Poul Sander 2025-02-03 21:28 11639
    }
1193b4be Poul Sander 2025-02-03 21:28 11640
    else
1193b4be Poul Sander 2025-02-03 21:28 11641
    {
1193b4be Poul Sander 2025-02-03 21:28 11642
        if (spacing_w < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11643
            spacing_w = g.Style.ItemSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 11644
        window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
1193b4be Poul Sander 2025-02-03 21:28 11645
        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
1193b4be Poul Sander 2025-02-03 21:28 11646
    }
1193b4be Poul Sander 2025-02-03 21:28 11647
    window->DC.CurrLineSize = window->DC.PrevLineSize;
1193b4be Poul Sander 2025-02-03 21:28 11648
    window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
1193b4be Poul Sander 2025-02-03 21:28 11649
    window->DC.IsSameLine = true;
1193b4be Poul Sander 2025-02-03 21:28 11650
}
1193b4be Poul Sander 2025-02-03 21:28 11651
1193b4be Poul Sander 2025-02-03 21:28 11652
ImVec2 ImGui::GetCursorScreenPos()
1193b4be Poul Sander 2025-02-03 21:28 11653
{
1193b4be Poul Sander 2025-02-03 21:28 11654
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 11655
    return window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 11656
}
1193b4be Poul Sander 2025-02-03 21:28 11657
1193b4be Poul Sander 2025-02-03 21:28 11658
void ImGui::SetCursorScreenPos(const ImVec2& pos)
1193b4be Poul Sander 2025-02-03 21:28 11659
{
1193b4be Poul Sander 2025-02-03 21:28 11660
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11661
    window->DC.CursorPos = pos;
1193b4be Poul Sander 2025-02-03 21:28 11662
    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
1193b4be Poul Sander 2025-02-03 21:28 11663
    window->DC.IsSetPos = true;
1193b4be Poul Sander 2025-02-03 21:28 11664
}
1193b4be Poul Sander 2025-02-03 21:28 11665
1193b4be Poul Sander 2025-02-03 21:28 11666
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
1193b4be Poul Sander 2025-02-03 21:28 11667
// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
1193b4be Poul Sander 2025-02-03 21:28 11668
ImVec2 ImGui::GetCursorPos()
1193b4be Poul Sander 2025-02-03 21:28 11669
{
1193b4be Poul Sander 2025-02-03 21:28 11670
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 11671
    return window->DC.CursorPos - window->Pos + window->Scroll;
1193b4be Poul Sander 2025-02-03 21:28 11672
}
1193b4be Poul Sander 2025-02-03 21:28 11673
1193b4be Poul Sander 2025-02-03 21:28 11674
float ImGui::GetCursorPosX()
1193b4be Poul Sander 2025-02-03 21:28 11675
{
1193b4be Poul Sander 2025-02-03 21:28 11676
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 11677
    return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
1193b4be Poul Sander 2025-02-03 21:28 11678
}
1193b4be Poul Sander 2025-02-03 21:28 11679
1193b4be Poul Sander 2025-02-03 21:28 11680
float ImGui::GetCursorPosY()
1193b4be Poul Sander 2025-02-03 21:28 11681
{
1193b4be Poul Sander 2025-02-03 21:28 11682
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 11683
    return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
1193b4be Poul Sander 2025-02-03 21:28 11684
}
1193b4be Poul Sander 2025-02-03 21:28 11685
1193b4be Poul Sander 2025-02-03 21:28 11686
void ImGui::SetCursorPos(const ImVec2& local_pos)
1193b4be Poul Sander 2025-02-03 21:28 11687
{
1193b4be Poul Sander 2025-02-03 21:28 11688
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11689
    window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
1193b4be Poul Sander 2025-02-03 21:28 11690
    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
1193b4be Poul Sander 2025-02-03 21:28 11691
    window->DC.IsSetPos = true;
1193b4be Poul Sander 2025-02-03 21:28 11692
}
1193b4be Poul Sander 2025-02-03 21:28 11693
1193b4be Poul Sander 2025-02-03 21:28 11694
void ImGui::SetCursorPosX(float x)
1193b4be Poul Sander 2025-02-03 21:28 11695
{
1193b4be Poul Sander 2025-02-03 21:28 11696
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11697
    window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
1193b4be Poul Sander 2025-02-03 21:28 11698
    //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
1193b4be Poul Sander 2025-02-03 21:28 11699
    window->DC.IsSetPos = true;
1193b4be Poul Sander 2025-02-03 21:28 11700
}
1193b4be Poul Sander 2025-02-03 21:28 11701
1193b4be Poul Sander 2025-02-03 21:28 11702
void ImGui::SetCursorPosY(float y)
1193b4be Poul Sander 2025-02-03 21:28 11703
{
1193b4be Poul Sander 2025-02-03 21:28 11704
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11705
    window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
1193b4be Poul Sander 2025-02-03 21:28 11706
    //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
1193b4be Poul Sander 2025-02-03 21:28 11707
    window->DC.IsSetPos = true;
1193b4be Poul Sander 2025-02-03 21:28 11708
}
1193b4be Poul Sander 2025-02-03 21:28 11709
1193b4be Poul Sander 2025-02-03 21:28 11710
ImVec2 ImGui::GetCursorStartPos()
1193b4be Poul Sander 2025-02-03 21:28 11711
{
1193b4be Poul Sander 2025-02-03 21:28 11712
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 11713
    return window->DC.CursorStartPos - window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 11714
}
1193b4be Poul Sander 2025-02-03 21:28 11715
1193b4be Poul Sander 2025-02-03 21:28 11716
void ImGui::Indent(float indent_w)
1193b4be Poul Sander 2025-02-03 21:28 11717
{
1193b4be Poul Sander 2025-02-03 21:28 11718
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11719
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11720
    window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
1193b4be Poul Sander 2025-02-03 21:28 11721
    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
1193b4be Poul Sander 2025-02-03 21:28 11722
}
1193b4be Poul Sander 2025-02-03 21:28 11723
1193b4be Poul Sander 2025-02-03 21:28 11724
void ImGui::Unindent(float indent_w)
1193b4be Poul Sander 2025-02-03 21:28 11725
{
1193b4be Poul Sander 2025-02-03 21:28 11726
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11727
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 11728
    window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
1193b4be Poul Sander 2025-02-03 21:28 11729
    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
1193b4be Poul Sander 2025-02-03 21:28 11730
}
1193b4be Poul Sander 2025-02-03 21:28 11731
1193b4be Poul Sander 2025-02-03 21:28 11732
// Affect large frame+labels widgets only.
1193b4be Poul Sander 2025-02-03 21:28 11733
void ImGui::SetNextItemWidth(float item_width)
1193b4be Poul Sander 2025-02-03 21:28 11734
{
1193b4be Poul Sander 2025-02-03 21:28 11735
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11736
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
1193b4be Poul Sander 2025-02-03 21:28 11737
    g.NextItemData.Width = item_width;
1193b4be Poul Sander 2025-02-03 21:28 11738
}
1193b4be Poul Sander 2025-02-03 21:28 11739
1193b4be Poul Sander 2025-02-03 21:28 11740
// FIXME: Remove the == 0.0f behavior?
1193b4be Poul Sander 2025-02-03 21:28 11741
void ImGui::PushItemWidth(float item_width)
1193b4be Poul Sander 2025-02-03 21:28 11742
{
1193b4be Poul Sander 2025-02-03 21:28 11743
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11744
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11745
    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
1193b4be Poul Sander 2025-02-03 21:28 11746
    window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
1193b4be Poul Sander 2025-02-03 21:28 11747
    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
1193b4be Poul Sander 2025-02-03 21:28 11748
}
1193b4be Poul Sander 2025-02-03 21:28 11749
1193b4be Poul Sander 2025-02-03 21:28 11750
void ImGui::PushMultiItemsWidths(int components, float w_full)
1193b4be Poul Sander 2025-02-03 21:28 11751
{
1193b4be Poul Sander 2025-02-03 21:28 11752
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11753
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11754
    IM_ASSERT(components > 0);
1193b4be Poul Sander 2025-02-03 21:28 11755
    const ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 11756
    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
1193b4be Poul Sander 2025-02-03 21:28 11757
    float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
1193b4be Poul Sander 2025-02-03 21:28 11758
    float prev_split = w_items;
1193b4be Poul Sander 2025-02-03 21:28 11759
    for (int i = components - 1; i > 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 11760
    {
1193b4be Poul Sander 2025-02-03 21:28 11761
        float next_split = IM_TRUNC(w_items * i / components);
1193b4be Poul Sander 2025-02-03 21:28 11762
        window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
1193b4be Poul Sander 2025-02-03 21:28 11763
        prev_split = next_split;
1193b4be Poul Sander 2025-02-03 21:28 11764
    }
1193b4be Poul Sander 2025-02-03 21:28 11765
    window->DC.ItemWidth = ImMax(prev_split, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 11766
    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
1193b4be Poul Sander 2025-02-03 21:28 11767
}
1193b4be Poul Sander 2025-02-03 21:28 11768
1193b4be Poul Sander 2025-02-03 21:28 11769
void ImGui::PopItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11770
{
1193b4be Poul Sander 2025-02-03 21:28 11771
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11772
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11773
    if (window->DC.ItemWidthStack.Size <= 0)
1193b4be Poul Sander 2025-02-03 21:28 11774
    {
1193b4be Poul Sander 2025-02-03 21:28 11775
        IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
1193b4be Poul Sander 2025-02-03 21:28 11776
        return;
1193b4be Poul Sander 2025-02-03 21:28 11777
    }
1193b4be Poul Sander 2025-02-03 21:28 11778
    window->DC.ItemWidth = window->DC.ItemWidthStack.back();
1193b4be Poul Sander 2025-02-03 21:28 11779
    window->DC.ItemWidthStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 11780
}
1193b4be Poul Sander 2025-02-03 21:28 11781
1193b4be Poul Sander 2025-02-03 21:28 11782
// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
1193b4be Poul Sander 2025-02-03 21:28 11783
// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
1193b4be Poul Sander 2025-02-03 21:28 11784
float ImGui::CalcItemWidth()
1193b4be Poul Sander 2025-02-03 21:28 11785
{
1193b4be Poul Sander 2025-02-03 21:28 11786
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11787
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11788
    float w;
1193b4be Poul Sander 2025-02-03 21:28 11789
    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
1193b4be Poul Sander 2025-02-03 21:28 11790
        w = g.NextItemData.Width;
1193b4be Poul Sander 2025-02-03 21:28 11791
    else
1193b4be Poul Sander 2025-02-03 21:28 11792
        w = window->DC.ItemWidth;
1193b4be Poul Sander 2025-02-03 21:28 11793
    if (w < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11794
    {
1193b4be Poul Sander 2025-02-03 21:28 11795
        float region_avail_x = GetContentRegionAvail().x;
1193b4be Poul Sander 2025-02-03 21:28 11796
        w = ImMax(1.0f, region_avail_x + w);
1193b4be Poul Sander 2025-02-03 21:28 11797
    }
1193b4be Poul Sander 2025-02-03 21:28 11798
    w = IM_TRUNC(w);
1193b4be Poul Sander 2025-02-03 21:28 11799
    return w;
1193b4be Poul Sander 2025-02-03 21:28 11800
}
1193b4be Poul Sander 2025-02-03 21:28 11801
1193b4be Poul Sander 2025-02-03 21:28 11802
// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
1193b4be Poul Sander 2025-02-03 21:28 11803
// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
1193b4be Poul Sander 2025-02-03 21:28 11804
// Note that only CalcItemWidth() is publicly exposed.
1193b4be Poul Sander 2025-02-03 21:28 11805
// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
1193b4be Poul Sander 2025-02-03 21:28 11806
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
1193b4be Poul Sander 2025-02-03 21:28 11807
{
1193b4be Poul Sander 2025-02-03 21:28 11808
    ImVec2 avail;
1193b4be Poul Sander 2025-02-03 21:28 11809
    if (size.x < 0.0f || size.y < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11810
        avail = GetContentRegionAvail();
1193b4be Poul Sander 2025-02-03 21:28 11811
1193b4be Poul Sander 2025-02-03 21:28 11812
    if (size.x == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11813
        size.x = default_w;
1193b4be Poul Sander 2025-02-03 21:28 11814
    else if (size.x < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11815
        size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
1193b4be Poul Sander 2025-02-03 21:28 11816
1193b4be Poul Sander 2025-02-03 21:28 11817
    if (size.y == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11818
        size.y = default_h;
1193b4be Poul Sander 2025-02-03 21:28 11819
    else if (size.y < 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 11820
        size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
1193b4be Poul Sander 2025-02-03 21:28 11821
1193b4be Poul Sander 2025-02-03 21:28 11822
    return size;
1193b4be Poul Sander 2025-02-03 21:28 11823
}
1193b4be Poul Sander 2025-02-03 21:28 11824
1193b4be Poul Sander 2025-02-03 21:28 11825
float ImGui::GetTextLineHeight()
1193b4be Poul Sander 2025-02-03 21:28 11826
{
1193b4be Poul Sander 2025-02-03 21:28 11827
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11828
    return g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 11829
}
1193b4be Poul Sander 2025-02-03 21:28 11830
1193b4be Poul Sander 2025-02-03 21:28 11831
float ImGui::GetTextLineHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 11832
{
1193b4be Poul Sander 2025-02-03 21:28 11833
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11834
    return g.FontSize + g.Style.ItemSpacing.y;
1193b4be Poul Sander 2025-02-03 21:28 11835
}
1193b4be Poul Sander 2025-02-03 21:28 11836
1193b4be Poul Sander 2025-02-03 21:28 11837
float ImGui::GetFrameHeight()
1193b4be Poul Sander 2025-02-03 21:28 11838
{
1193b4be Poul Sander 2025-02-03 21:28 11839
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11840
    return g.FontSize + g.Style.FramePadding.y * 2.0f;
1193b4be Poul Sander 2025-02-03 21:28 11841
}
1193b4be Poul Sander 2025-02-03 21:28 11842
1193b4be Poul Sander 2025-02-03 21:28 11843
float ImGui::GetFrameHeightWithSpacing()
1193b4be Poul Sander 2025-02-03 21:28 11844
{
1193b4be Poul Sander 2025-02-03 21:28 11845
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11846
    return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
1193b4be Poul Sander 2025-02-03 21:28 11847
}
1193b4be Poul Sander 2025-02-03 21:28 11848
1193b4be Poul Sander 2025-02-03 21:28 11849
ImVec2 ImGui::GetContentRegionAvail()
1193b4be Poul Sander 2025-02-03 21:28 11850
{
1193b4be Poul Sander 2025-02-03 21:28 11851
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11852
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11853
    ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
1193b4be Poul Sander 2025-02-03 21:28 11854
    return mx - window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 11855
}
1193b4be Poul Sander 2025-02-03 21:28 11856
1193b4be Poul Sander 2025-02-03 21:28 11857
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 11858
1193b4be Poul Sander 2025-02-03 21:28 11859
// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
1193b4be Poul Sander 2025-02-03 21:28 11860
// They are bizarre local-coordinates which don't play well with scrolling.
1193b4be Poul Sander 2025-02-03 21:28 11861
ImVec2 ImGui::GetContentRegionMax()
1193b4be Poul Sander 2025-02-03 21:28 11862
{
1193b4be Poul Sander 2025-02-03 21:28 11863
    return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
1193b4be Poul Sander 2025-02-03 21:28 11864
}
1193b4be Poul Sander 2025-02-03 21:28 11865
1193b4be Poul Sander 2025-02-03 21:28 11866
ImVec2 ImGui::GetWindowContentRegionMin()
1193b4be Poul Sander 2025-02-03 21:28 11867
{
1193b4be Poul Sander 2025-02-03 21:28 11868
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11869
    return window->ContentRegionRect.Min - window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 11870
}
1193b4be Poul Sander 2025-02-03 21:28 11871
1193b4be Poul Sander 2025-02-03 21:28 11872
ImVec2 ImGui::GetWindowContentRegionMax()
1193b4be Poul Sander 2025-02-03 21:28 11873
{
1193b4be Poul Sander 2025-02-03 21:28 11874
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11875
    return window->ContentRegionRect.Max - window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 11876
}
1193b4be Poul Sander 2025-02-03 21:28 11877
#endif
1193b4be Poul Sander 2025-02-03 21:28 11878
1193b4be Poul Sander 2025-02-03 21:28 11879
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
1193b4be Poul Sander 2025-02-03 21:28 11880
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
1193b4be Poul Sander 2025-02-03 21:28 11881
// FIXME-OPT: Could we safely early out on ->SkipItems?
1193b4be Poul Sander 2025-02-03 21:28 11882
void ImGui::BeginGroup()
1193b4be Poul Sander 2025-02-03 21:28 11883
{
1193b4be Poul Sander 2025-02-03 21:28 11884
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11885
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11886
1193b4be Poul Sander 2025-02-03 21:28 11887
    g.GroupStack.resize(g.GroupStack.Size + 1);
1193b4be Poul Sander 2025-02-03 21:28 11888
    ImGuiGroupData& group_data = g.GroupStack.back();
1193b4be Poul Sander 2025-02-03 21:28 11889
    group_data.WindowID = window->ID;
1193b4be Poul Sander 2025-02-03 21:28 11890
    group_data.BackupCursorPos = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 11891
    group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
1193b4be Poul Sander 2025-02-03 21:28 11892
    group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
1193b4be Poul Sander 2025-02-03 21:28 11893
    group_data.BackupIndent = window->DC.Indent;
1193b4be Poul Sander 2025-02-03 21:28 11894
    group_data.BackupGroupOffset = window->DC.GroupOffset;
1193b4be Poul Sander 2025-02-03 21:28 11895
    group_data.BackupCurrLineSize = window->DC.CurrLineSize;
1193b4be Poul Sander 2025-02-03 21:28 11896
    group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
1193b4be Poul Sander 2025-02-03 21:28 11897
    group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
1193b4be Poul Sander 2025-02-03 21:28 11898
    group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
1193b4be Poul Sander 2025-02-03 21:28 11899
    group_data.BackupIsSameLine = window->DC.IsSameLine;
1193b4be Poul Sander 2025-02-03 21:28 11900
    group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
1193b4be Poul Sander 2025-02-03 21:28 11901
    group_data.EmitItem = true;
1193b4be Poul Sander 2025-02-03 21:28 11902
1193b4be Poul Sander 2025-02-03 21:28 11903
    window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
1193b4be Poul Sander 2025-02-03 21:28 11904
    window->DC.Indent = window->DC.GroupOffset;
1193b4be Poul Sander 2025-02-03 21:28 11905
    window->DC.CursorMaxPos = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 11906
    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 11907
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 11908
        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
1193b4be Poul Sander 2025-02-03 21:28 11909
}
1193b4be Poul Sander 2025-02-03 21:28 11910
1193b4be Poul Sander 2025-02-03 21:28 11911
void ImGui::EndGroup()
1193b4be Poul Sander 2025-02-03 21:28 11912
{
1193b4be Poul Sander 2025-02-03 21:28 11913
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 11914
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 11915
    IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
1193b4be Poul Sander 2025-02-03 21:28 11916
1193b4be Poul Sander 2025-02-03 21:28 11917
    ImGuiGroupData& group_data = g.GroupStack.back();
1193b4be Poul Sander 2025-02-03 21:28 11918
    IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
1193b4be Poul Sander 2025-02-03 21:28 11919
1193b4be Poul Sander 2025-02-03 21:28 11920
    if (window->DC.IsSetPos)
1193b4be Poul Sander 2025-02-03 21:28 11921
        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
1193b4be Poul Sander 2025-02-03 21:28 11922
1193b4be Poul Sander 2025-02-03 21:28 11923
    // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
1193b4be Poul Sander 2025-02-03 21:28 11924
    ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
1193b4be Poul Sander 2025-02-03 21:28 11925
    window->DC.CursorPos = group_data.BackupCursorPos;
1193b4be Poul Sander 2025-02-03 21:28 11926
    window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
1193b4be Poul Sander 2025-02-03 21:28 11927
    window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
1193b4be Poul Sander 2025-02-03 21:28 11928
    window->DC.Indent = group_data.BackupIndent;
1193b4be Poul Sander 2025-02-03 21:28 11929
    window->DC.GroupOffset = group_data.BackupGroupOffset;
1193b4be Poul Sander 2025-02-03 21:28 11930
    window->DC.CurrLineSize = group_data.BackupCurrLineSize;
1193b4be Poul Sander 2025-02-03 21:28 11931
    window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
1193b4be Poul Sander 2025-02-03 21:28 11932
    window->DC.IsSameLine = group_data.BackupIsSameLine;
1193b4be Poul Sander 2025-02-03 21:28 11933
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 11934
        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
1193b4be Poul Sander 2025-02-03 21:28 11935
1193b4be Poul Sander 2025-02-03 21:28 11936
    if (!group_data.EmitItem)
1193b4be Poul Sander 2025-02-03 21:28 11937
    {
1193b4be Poul Sander 2025-02-03 21:28 11938
        g.GroupStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 11939
        return;
1193b4be Poul Sander 2025-02-03 21:28 11940
    }
1193b4be Poul Sander 2025-02-03 21:28 11941
1193b4be Poul Sander 2025-02-03 21:28 11942
    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
1193b4be Poul Sander 2025-02-03 21:28 11943
    ItemSize(group_bb.GetSize());
1193b4be Poul Sander 2025-02-03 21:28 11944
    ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
1193b4be Poul Sander 2025-02-03 21:28 11945
1193b4be Poul Sander 2025-02-03 21:28 11946
    // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
1193b4be Poul Sander 2025-02-03 21:28 11947
    // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
1193b4be Poul Sander 2025-02-03 21:28 11948
    // Also if you grep for LastItemId you'll notice it is only used in that context.
1193b4be Poul Sander 2025-02-03 21:28 11949
    // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
1193b4be Poul Sander 2025-02-03 21:28 11950
    const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
1193b4be Poul Sander 2025-02-03 21:28 11951
    const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
1193b4be Poul Sander 2025-02-03 21:28 11952
    if (group_contains_curr_active_id)
1193b4be Poul Sander 2025-02-03 21:28 11953
        g.LastItemData.ID = g.ActiveId;
1193b4be Poul Sander 2025-02-03 21:28 11954
    else if (group_contains_prev_active_id)
1193b4be Poul Sander 2025-02-03 21:28 11955
        g.LastItemData.ID = g.ActiveIdPreviousFrame;
1193b4be Poul Sander 2025-02-03 21:28 11956
    g.LastItemData.Rect = group_bb;
1193b4be Poul Sander 2025-02-03 21:28 11957
1193b4be Poul Sander 2025-02-03 21:28 11958
    // Forward Hovered flag
1193b4be Poul Sander 2025-02-03 21:28 11959
    const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
1193b4be Poul Sander 2025-02-03 21:28 11960
    if (group_contains_curr_hovered_id)
1193b4be Poul Sander 2025-02-03 21:28 11961
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 11962
1193b4be Poul Sander 2025-02-03 21:28 11963
    // Forward Edited flag
1193b4be Poul Sander 2025-02-03 21:28 11964
    if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
1193b4be Poul Sander 2025-02-03 21:28 11965
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
1193b4be Poul Sander 2025-02-03 21:28 11966
1193b4be Poul Sander 2025-02-03 21:28 11967
    // Forward Deactivated flag
1193b4be Poul Sander 2025-02-03 21:28 11968
    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
1193b4be Poul Sander 2025-02-03 21:28 11969
    if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
1193b4be Poul Sander 2025-02-03 21:28 11970
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
1193b4be Poul Sander 2025-02-03 21:28 11971
1193b4be Poul Sander 2025-02-03 21:28 11972
    g.GroupStack.pop_back();
1193b4be Poul Sander 2025-02-03 21:28 11973
    if (g.DebugShowGroupRects)
1193b4be Poul Sander 2025-02-03 21:28 11974
        window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
1193b4be Poul Sander 2025-02-03 21:28 11975
}
1193b4be Poul Sander 2025-02-03 21:28 11976
1193b4be Poul Sander 2025-02-03 21:28 11977
1193b4be Poul Sander 2025-02-03 21:28 11978
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11979
// [SECTION] SCROLLING
1193b4be Poul Sander 2025-02-03 21:28 11980
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 11981
1193b4be Poul Sander 2025-02-03 21:28 11982
// Helper to snap on edges when aiming at an item very close to the edge,
1193b4be Poul Sander 2025-02-03 21:28 11983
// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
1193b4be Poul Sander 2025-02-03 21:28 11984
// When we refactor the scrolling API this may be configurable with a flag?
1193b4be Poul Sander 2025-02-03 21:28 11985
// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
1193b4be Poul Sander 2025-02-03 21:28 11986
static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
1193b4be Poul Sander 2025-02-03 21:28 11987
{
1193b4be Poul Sander 2025-02-03 21:28 11988
    if (target <= snap_min + snap_threshold)
1193b4be Poul Sander 2025-02-03 21:28 11989
        return ImLerp(snap_min, target, center_ratio);
1193b4be Poul Sander 2025-02-03 21:28 11990
    if (target >= snap_max - snap_threshold)
1193b4be Poul Sander 2025-02-03 21:28 11991
        return ImLerp(target, snap_max, center_ratio);
1193b4be Poul Sander 2025-02-03 21:28 11992
    return target;
1193b4be Poul Sander 2025-02-03 21:28 11993
}
1193b4be Poul Sander 2025-02-03 21:28 11994
1193b4be Poul Sander 2025-02-03 21:28 11995
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 11996
{
1193b4be Poul Sander 2025-02-03 21:28 11997
    ImVec2 scroll = window->Scroll;
1193b4be Poul Sander 2025-02-03 21:28 11998
    ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
1193b4be Poul Sander 2025-02-03 21:28 11999
    for (int axis = 0; axis < 2; axis++)
1193b4be Poul Sander 2025-02-03 21:28 12000
    {
1193b4be Poul Sander 2025-02-03 21:28 12001
        if (window->ScrollTarget[axis] < FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 12002
        {
1193b4be Poul Sander 2025-02-03 21:28 12003
            float center_ratio = window->ScrollTargetCenterRatio[axis];
1193b4be Poul Sander 2025-02-03 21:28 12004
            float scroll_target = window->ScrollTarget[axis];
1193b4be Poul Sander 2025-02-03 21:28 12005
            if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 12006
            {
1193b4be Poul Sander 2025-02-03 21:28 12007
                float snap_min = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12008
                float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
1193b4be Poul Sander 2025-02-03 21:28 12009
                scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
1193b4be Poul Sander 2025-02-03 21:28 12010
            }
1193b4be Poul Sander 2025-02-03 21:28 12011
            scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 12012
        }
1193b4be Poul Sander 2025-02-03 21:28 12013
        scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 12014
        if (!window->Collapsed && !window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 12015
            scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
1193b4be Poul Sander 2025-02-03 21:28 12016
    }
1193b4be Poul Sander 2025-02-03 21:28 12017
    return scroll;
1193b4be Poul Sander 2025-02-03 21:28 12018
}
1193b4be Poul Sander 2025-02-03 21:28 12019
1193b4be Poul Sander 2025-02-03 21:28 12020
void ImGui::ScrollToItem(ImGuiScrollFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 12021
{
1193b4be Poul Sander 2025-02-03 21:28 12022
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12023
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12024
    ScrollToRectEx(window, g.LastItemData.NavRect, flags);
1193b4be Poul Sander 2025-02-03 21:28 12025
}
1193b4be Poul Sander 2025-02-03 21:28 12026
1193b4be Poul Sander 2025-02-03 21:28 12027
void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 12028
{
1193b4be Poul Sander 2025-02-03 21:28 12029
    ScrollToRectEx(window, item_rect, flags);
1193b4be Poul Sander 2025-02-03 21:28 12030
}
1193b4be Poul Sander 2025-02-03 21:28 12031
1193b4be Poul Sander 2025-02-03 21:28 12032
// Scroll to keep newly navigated item fully into view
1193b4be Poul Sander 2025-02-03 21:28 12033
ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 12034
{
1193b4be Poul Sander 2025-02-03 21:28 12035
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12036
    ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
1193b4be Poul Sander 2025-02-03 21:28 12037
    scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 12038
    scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 12039
    //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 12040
    //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 12041
1193b4be Poul Sander 2025-02-03 21:28 12042
    // Check that only one behavior is selected per axis
1193b4be Poul Sander 2025-02-03 21:28 12043
    IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
1193b4be Poul Sander 2025-02-03 21:28 12044
    IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
1193b4be Poul Sander 2025-02-03 21:28 12045
1193b4be Poul Sander 2025-02-03 21:28 12046
    // Defaults
1193b4be Poul Sander 2025-02-03 21:28 12047
    ImGuiScrollFlags in_flags = flags;
1193b4be Poul Sander 2025-02-03 21:28 12048
    if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
1193b4be Poul Sander 2025-02-03 21:28 12049
        flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
1193b4be Poul Sander 2025-02-03 21:28 12050
    if ((flags & ImGuiScrollFlags_MaskY_) == 0)
1193b4be Poul Sander 2025-02-03 21:28 12051
        flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 12052
1193b4be Poul Sander 2025-02-03 21:28 12053
    const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
1193b4be Poul Sander 2025-02-03 21:28 12054
    const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
1193b4be Poul Sander 2025-02-03 21:28 12055
    const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
1193b4be Poul Sander 2025-02-03 21:28 12056
    const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
1193b4be Poul Sander 2025-02-03 21:28 12057
1193b4be Poul Sander 2025-02-03 21:28 12058
    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
1193b4be Poul Sander 2025-02-03 21:28 12059
    {
1193b4be Poul Sander 2025-02-03 21:28 12060
        if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
1193b4be Poul Sander 2025-02-03 21:28 12061
            SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 12062
        else if (item_rect.Max.x >= scroll_rect.Max.x)
1193b4be Poul Sander 2025-02-03 21:28 12063
            SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 12064
    }
1193b4be Poul Sander 2025-02-03 21:28 12065
    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
1193b4be Poul Sander 2025-02-03 21:28 12066
    {
1193b4be Poul Sander 2025-02-03 21:28 12067
        if (can_be_fully_visible_x)
1193b4be Poul Sander 2025-02-03 21:28 12068
            SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 12069
        else
1193b4be Poul Sander 2025-02-03 21:28 12070
            SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 12071
    }
1193b4be Poul Sander 2025-02-03 21:28 12072
1193b4be Poul Sander 2025-02-03 21:28 12073
    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
1193b4be Poul Sander 2025-02-03 21:28 12074
    {
1193b4be Poul Sander 2025-02-03 21:28 12075
        if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
1193b4be Poul Sander 2025-02-03 21:28 12076
            SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 12077
        else if (item_rect.Max.y >= scroll_rect.Max.y)
1193b4be Poul Sander 2025-02-03 21:28 12078
            SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 12079
    }
1193b4be Poul Sander 2025-02-03 21:28 12080
    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
1193b4be Poul Sander 2025-02-03 21:28 12081
    {
1193b4be Poul Sander 2025-02-03 21:28 12082
        if (can_be_fully_visible_y)
1193b4be Poul Sander 2025-02-03 21:28 12083
            SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 12084
        else
1193b4be Poul Sander 2025-02-03 21:28 12085
            SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 12086
    }
1193b4be Poul Sander 2025-02-03 21:28 12087
1193b4be Poul Sander 2025-02-03 21:28 12088
    ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
1193b4be Poul Sander 2025-02-03 21:28 12089
    ImVec2 delta_scroll = next_scroll - window->Scroll;
1193b4be Poul Sander 2025-02-03 21:28 12090
1193b4be Poul Sander 2025-02-03 21:28 12091
    // Also scroll parent window to keep us into view if necessary
1193b4be Poul Sander 2025-02-03 21:28 12092
    if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 12093
    {
1193b4be Poul Sander 2025-02-03 21:28 12094
        // FIXME-SCROLL: May be an option?
1193b4be Poul Sander 2025-02-03 21:28 12095
        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 12096
            in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
1193b4be Poul Sander 2025-02-03 21:28 12097
        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 12098
            in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 12099
        delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
1193b4be Poul Sander 2025-02-03 21:28 12100
    }
1193b4be Poul Sander 2025-02-03 21:28 12101
1193b4be Poul Sander 2025-02-03 21:28 12102
    return delta_scroll;
1193b4be Poul Sander 2025-02-03 21:28 12103
}
1193b4be Poul Sander 2025-02-03 21:28 12104
1193b4be Poul Sander 2025-02-03 21:28 12105
float ImGui::GetScrollX()
1193b4be Poul Sander 2025-02-03 21:28 12106
{
1193b4be Poul Sander 2025-02-03 21:28 12107
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12108
    return window->Scroll.x;
1193b4be Poul Sander 2025-02-03 21:28 12109
}
1193b4be Poul Sander 2025-02-03 21:28 12110
1193b4be Poul Sander 2025-02-03 21:28 12111
float ImGui::GetScrollY()
1193b4be Poul Sander 2025-02-03 21:28 12112
{
1193b4be Poul Sander 2025-02-03 21:28 12113
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12114
    return window->Scroll.y;
1193b4be Poul Sander 2025-02-03 21:28 12115
}
1193b4be Poul Sander 2025-02-03 21:28 12116
1193b4be Poul Sander 2025-02-03 21:28 12117
float ImGui::GetScrollMaxX()
1193b4be Poul Sander 2025-02-03 21:28 12118
{
1193b4be Poul Sander 2025-02-03 21:28 12119
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12120
    return window->ScrollMax.x;
1193b4be Poul Sander 2025-02-03 21:28 12121
}
1193b4be Poul Sander 2025-02-03 21:28 12122
1193b4be Poul Sander 2025-02-03 21:28 12123
float ImGui::GetScrollMaxY()
1193b4be Poul Sander 2025-02-03 21:28 12124
{
1193b4be Poul Sander 2025-02-03 21:28 12125
    ImGuiWindow* window = GImGui->CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12126
    return window->ScrollMax.y;
1193b4be Poul Sander 2025-02-03 21:28 12127
}
1193b4be Poul Sander 2025-02-03 21:28 12128
1193b4be Poul Sander 2025-02-03 21:28 12129
void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
1193b4be Poul Sander 2025-02-03 21:28 12130
{
1193b4be Poul Sander 2025-02-03 21:28 12131
    window->ScrollTarget.x = scroll_x;
1193b4be Poul Sander 2025-02-03 21:28 12132
    window->ScrollTargetCenterRatio.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12133
    window->ScrollTargetEdgeSnapDist.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12134
}
1193b4be Poul Sander 2025-02-03 21:28 12135
1193b4be Poul Sander 2025-02-03 21:28 12136
void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
1193b4be Poul Sander 2025-02-03 21:28 12137
{
1193b4be Poul Sander 2025-02-03 21:28 12138
    window->ScrollTarget.y = scroll_y;
1193b4be Poul Sander 2025-02-03 21:28 12139
    window->ScrollTargetCenterRatio.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12140
    window->ScrollTargetEdgeSnapDist.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12141
}
1193b4be Poul Sander 2025-02-03 21:28 12142
1193b4be Poul Sander 2025-02-03 21:28 12143
void ImGui::SetScrollX(float scroll_x)
1193b4be Poul Sander 2025-02-03 21:28 12144
{
1193b4be Poul Sander 2025-02-03 21:28 12145
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12146
    SetScrollX(g.CurrentWindow, scroll_x);
1193b4be Poul Sander 2025-02-03 21:28 12147
}
1193b4be Poul Sander 2025-02-03 21:28 12148
1193b4be Poul Sander 2025-02-03 21:28 12149
void ImGui::SetScrollY(float scroll_y)
1193b4be Poul Sander 2025-02-03 21:28 12150
{
1193b4be Poul Sander 2025-02-03 21:28 12151
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12152
    SetScrollY(g.CurrentWindow, scroll_y);
1193b4be Poul Sander 2025-02-03 21:28 12153
}
1193b4be Poul Sander 2025-02-03 21:28 12154
1193b4be Poul Sander 2025-02-03 21:28 12155
// Note that a local position will vary depending on initial scroll value,
1193b4be Poul Sander 2025-02-03 21:28 12156
// This is a little bit confusing so bear with us:
1193b4be Poul Sander 2025-02-03 21:28 12157
//  - local_pos = (absolution_pos - window->Pos)
1193b4be Poul Sander 2025-02-03 21:28 12158
//  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
1193b4be Poul Sander 2025-02-03 21:28 12159
//    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
1193b4be Poul Sander 2025-02-03 21:28 12160
//  - They mostly exist because of legacy API.
1193b4be Poul Sander 2025-02-03 21:28 12161
// Following the rules above, when trying to work with scrolling code, consider that:
1193b4be Poul Sander 2025-02-03 21:28 12162
//  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
1193b4be Poul Sander 2025-02-03 21:28 12163
//  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
1193b4be Poul Sander 2025-02-03 21:28 12164
// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
1193b4be Poul Sander 2025-02-03 21:28 12165
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12166
{
1193b4be Poul Sander 2025-02-03 21:28 12167
    IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 12168
    window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
1193b4be Poul Sander 2025-02-03 21:28 12169
    window->ScrollTargetCenterRatio.x = center_x_ratio;
1193b4be Poul Sander 2025-02-03 21:28 12170
    window->ScrollTargetEdgeSnapDist.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12171
}
1193b4be Poul Sander 2025-02-03 21:28 12172
1193b4be Poul Sander 2025-02-03 21:28 12173
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12174
{
1193b4be Poul Sander 2025-02-03 21:28 12175
    IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 12176
    window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
1193b4be Poul Sander 2025-02-03 21:28 12177
    window->ScrollTargetCenterRatio.y = center_y_ratio;
1193b4be Poul Sander 2025-02-03 21:28 12178
    window->ScrollTargetEdgeSnapDist.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 12179
}
1193b4be Poul Sander 2025-02-03 21:28 12180
1193b4be Poul Sander 2025-02-03 21:28 12181
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12182
{
1193b4be Poul Sander 2025-02-03 21:28 12183
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12184
    SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
1193b4be Poul Sander 2025-02-03 21:28 12185
}
1193b4be Poul Sander 2025-02-03 21:28 12186
1193b4be Poul Sander 2025-02-03 21:28 12187
void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12188
{
1193b4be Poul Sander 2025-02-03 21:28 12189
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12190
    SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
1193b4be Poul Sander 2025-02-03 21:28 12191
}
1193b4be Poul Sander 2025-02-03 21:28 12192
1193b4be Poul Sander 2025-02-03 21:28 12193
// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
1193b4be Poul Sander 2025-02-03 21:28 12194
void ImGui::SetScrollHereX(float center_x_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12195
{
1193b4be Poul Sander 2025-02-03 21:28 12196
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12197
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12198
    float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
1193b4be Poul Sander 2025-02-03 21:28 12199
    float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
1193b4be Poul Sander 2025-02-03 21:28 12200
    SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
1193b4be Poul Sander 2025-02-03 21:28 12201
1193b4be Poul Sander 2025-02-03 21:28 12202
    // Tweak: snap on edges when aiming at an item very close to the edge
1193b4be Poul Sander 2025-02-03 21:28 12203
    window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
1193b4be Poul Sander 2025-02-03 21:28 12204
}
1193b4be Poul Sander 2025-02-03 21:28 12205
1193b4be Poul Sander 2025-02-03 21:28 12206
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
1193b4be Poul Sander 2025-02-03 21:28 12207
void ImGui::SetScrollHereY(float center_y_ratio)
1193b4be Poul Sander 2025-02-03 21:28 12208
{
1193b4be Poul Sander 2025-02-03 21:28 12209
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12210
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12211
    float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
1193b4be Poul Sander 2025-02-03 21:28 12212
    float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
1193b4be Poul Sander 2025-02-03 21:28 12213
    SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
1193b4be Poul Sander 2025-02-03 21:28 12214
1193b4be Poul Sander 2025-02-03 21:28 12215
    // Tweak: snap on edges when aiming at an item very close to the edge
1193b4be Poul Sander 2025-02-03 21:28 12216
    window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
1193b4be Poul Sander 2025-02-03 21:28 12217
}
1193b4be Poul Sander 2025-02-03 21:28 12218
1193b4be Poul Sander 2025-02-03 21:28 12219
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12220
// [SECTION] TOOLTIPS
1193b4be Poul Sander 2025-02-03 21:28 12221
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12222
1193b4be Poul Sander 2025-02-03 21:28 12223
bool ImGui::BeginTooltip()
1193b4be Poul Sander 2025-02-03 21:28 12224
{
1193b4be Poul Sander 2025-02-03 21:28 12225
    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 12226
}
1193b4be Poul Sander 2025-02-03 21:28 12227
1193b4be Poul Sander 2025-02-03 21:28 12228
bool ImGui::BeginItemTooltip()
1193b4be Poul Sander 2025-02-03 21:28 12229
{
1193b4be Poul Sander 2025-02-03 21:28 12230
    if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
1193b4be Poul Sander 2025-02-03 21:28 12231
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12232
    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 12233
}
1193b4be Poul Sander 2025-02-03 21:28 12234
1193b4be Poul Sander 2025-02-03 21:28 12235
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
1193b4be Poul Sander 2025-02-03 21:28 12236
{
1193b4be Poul Sander 2025-02-03 21:28 12237
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12238
1193b4be Poul Sander 2025-02-03 21:28 12239
    const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
1193b4be Poul Sander 2025-02-03 21:28 12240
    if (is_dragdrop_tooltip)
1193b4be Poul Sander 2025-02-03 21:28 12241
    {
1193b4be Poul Sander 2025-02-03 21:28 12242
        // Drag and Drop tooltips are positioning differently than other tooltips:
1193b4be Poul Sander 2025-02-03 21:28 12243
        // - offset visibility to increase visibility around mouse.
1193b4be Poul Sander 2025-02-03 21:28 12244
        // - never clamp within outer viewport boundary.
1193b4be Poul Sander 2025-02-03 21:28 12245
        // We call SetNextWindowPos() to enforce position and disable clamping.
1193b4be Poul Sander 2025-02-03 21:28 12246
        // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
1193b4be Poul Sander 2025-02-03 21:28 12247
        //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 12248
        const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
1193b4be Poul Sander 2025-02-03 21:28 12249
        if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
1193b4be Poul Sander 2025-02-03 21:28 12250
        {
1193b4be Poul Sander 2025-02-03 21:28 12251
            ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);
1193b4be Poul Sander 2025-02-03 21:28 12252
            ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 12253
            SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
1193b4be Poul Sander 2025-02-03 21:28 12254
        }
1193b4be Poul Sander 2025-02-03 21:28 12255
1193b4be Poul Sander 2025-02-03 21:28 12256
        SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
1193b4be Poul Sander 2025-02-03 21:28 12257
        //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
1193b4be Poul Sander 2025-02-03 21:28 12258
        tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
1193b4be Poul Sander 2025-02-03 21:28 12259
    }
1193b4be Poul Sander 2025-02-03 21:28 12260
1193b4be Poul Sander 2025-02-03 21:28 12261
    const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
1193b4be Poul Sander 2025-02-03 21:28 12262
    char window_name[32];
1193b4be Poul Sander 2025-02-03 21:28 12263
    ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount);
1193b4be Poul Sander 2025-02-03 21:28 12264
    if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
1193b4be Poul Sander 2025-02-03 21:28 12265
    {
1193b4be Poul Sander 2025-02-03 21:28 12266
        // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
1193b4be Poul Sander 2025-02-03 21:28 12267
        //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
1193b4be Poul Sander 2025-02-03 21:28 12268
        SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
1193b4be Poul Sander 2025-02-03 21:28 12269
        ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount);
1193b4be Poul Sander 2025-02-03 21:28 12270
    }
1193b4be Poul Sander 2025-02-03 21:28 12271
1193b4be Poul Sander 2025-02-03 21:28 12272
    ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
1193b4be Poul Sander 2025-02-03 21:28 12273
    Begin(window_name, NULL, flags | extra_window_flags);
1193b4be Poul Sander 2025-02-03 21:28 12274
    // 2023-03-09: Added bool return value to the API, but currently always returning true.
1193b4be Poul Sander 2025-02-03 21:28 12275
    // If this ever returns false we need to update BeginDragDropSource() accordingly.
1193b4be Poul Sander 2025-02-03 21:28 12276
    //if (!ret)
1193b4be Poul Sander 2025-02-03 21:28 12277
    //    End();
1193b4be Poul Sander 2025-02-03 21:28 12278
    //return ret;
1193b4be Poul Sander 2025-02-03 21:28 12279
    return true;
1193b4be Poul Sander 2025-02-03 21:28 12280
}
1193b4be Poul Sander 2025-02-03 21:28 12281
1193b4be Poul Sander 2025-02-03 21:28 12282
void ImGui::EndTooltip()
1193b4be Poul Sander 2025-02-03 21:28 12283
{
1193b4be Poul Sander 2025-02-03 21:28 12284
    IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
1193b4be Poul Sander 2025-02-03 21:28 12285
    End();
1193b4be Poul Sander 2025-02-03 21:28 12286
}
1193b4be Poul Sander 2025-02-03 21:28 12287
1193b4be Poul Sander 2025-02-03 21:28 12288
void ImGui::SetTooltip(const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 12289
{
1193b4be Poul Sander 2025-02-03 21:28 12290
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 12291
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 12292
    SetTooltipV(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 12293
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 12294
}
1193b4be Poul Sander 2025-02-03 21:28 12295
1193b4be Poul Sander 2025-02-03 21:28 12296
void ImGui::SetTooltipV(const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 12297
{
1193b4be Poul Sander 2025-02-03 21:28 12298
    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
1193b4be Poul Sander 2025-02-03 21:28 12299
        return;
1193b4be Poul Sander 2025-02-03 21:28 12300
    TextV(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 12301
    EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 12302
}
1193b4be Poul Sander 2025-02-03 21:28 12303
1193b4be Poul Sander 2025-02-03 21:28 12304
// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
1193b4be Poul Sander 2025-02-03 21:28 12305
// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
1193b4be Poul Sander 2025-02-03 21:28 12306
void ImGui::SetItemTooltip(const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 12307
{
1193b4be Poul Sander 2025-02-03 21:28 12308
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 12309
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 12310
    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
1193b4be Poul Sander 2025-02-03 21:28 12311
        SetTooltipV(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 12312
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 12313
}
1193b4be Poul Sander 2025-02-03 21:28 12314
1193b4be Poul Sander 2025-02-03 21:28 12315
void ImGui::SetItemTooltipV(const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 12316
{
1193b4be Poul Sander 2025-02-03 21:28 12317
    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
1193b4be Poul Sander 2025-02-03 21:28 12318
        SetTooltipV(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 12319
}
1193b4be Poul Sander 2025-02-03 21:28 12320
1193b4be Poul Sander 2025-02-03 21:28 12321
1193b4be Poul Sander 2025-02-03 21:28 12322
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12323
// [SECTION] POPUPS
1193b4be Poul Sander 2025-02-03 21:28 12324
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12325
1193b4be Poul Sander 2025-02-03 21:28 12326
// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
1193b4be Poul Sander 2025-02-03 21:28 12327
bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12328
{
1193b4be Poul Sander 2025-02-03 21:28 12329
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12330
    if (popup_flags & ImGuiPopupFlags_AnyPopupId)
1193b4be Poul Sander 2025-02-03 21:28 12331
    {
1193b4be Poul Sander 2025-02-03 21:28 12332
        // Return true if any popup is open at the current BeginPopup() level of the popup stack
1193b4be Poul Sander 2025-02-03 21:28 12333
        // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
1193b4be Poul Sander 2025-02-03 21:28 12334
        IM_ASSERT(id == 0);
1193b4be Poul Sander 2025-02-03 21:28 12335
        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
1193b4be Poul Sander 2025-02-03 21:28 12336
            return g.OpenPopupStack.Size > 0;
1193b4be Poul Sander 2025-02-03 21:28 12337
        else
1193b4be Poul Sander 2025-02-03 21:28 12338
            return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 12339
    }
1193b4be Poul Sander 2025-02-03 21:28 12340
    else
1193b4be Poul Sander 2025-02-03 21:28 12341
    {
1193b4be Poul Sander 2025-02-03 21:28 12342
        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
1193b4be Poul Sander 2025-02-03 21:28 12343
        {
1193b4be Poul Sander 2025-02-03 21:28 12344
            // Return true if the popup is open anywhere in the popup stack
1193b4be Poul Sander 2025-02-03 21:28 12345
            for (ImGuiPopupData& popup_data : g.OpenPopupStack)
1193b4be Poul Sander 2025-02-03 21:28 12346
                if (popup_data.PopupId == id)
1193b4be Poul Sander 2025-02-03 21:28 12347
                    return true;
1193b4be Poul Sander 2025-02-03 21:28 12348
            return false;
1193b4be Poul Sander 2025-02-03 21:28 12349
        }
1193b4be Poul Sander 2025-02-03 21:28 12350
        else
1193b4be Poul Sander 2025-02-03 21:28 12351
        {
1193b4be Poul Sander 2025-02-03 21:28 12352
            // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
1193b4be Poul Sander 2025-02-03 21:28 12353
            return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
1193b4be Poul Sander 2025-02-03 21:28 12354
        }
1193b4be Poul Sander 2025-02-03 21:28 12355
    }
1193b4be Poul Sander 2025-02-03 21:28 12356
}
1193b4be Poul Sander 2025-02-03 21:28 12357
1193b4be Poul Sander 2025-02-03 21:28 12358
bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12359
{
1193b4be Poul Sander 2025-02-03 21:28 12360
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12361
    ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 12362
    if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
1193b4be Poul Sander 2025-02-03 21:28 12363
        IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
1193b4be Poul Sander 2025-02-03 21:28 12364
    return IsPopupOpen(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12365
}
1193b4be Poul Sander 2025-02-03 21:28 12366
1193b4be Poul Sander 2025-02-03 21:28 12367
// Also see FindBlockingModal(NULL)
1193b4be Poul Sander 2025-02-03 21:28 12368
ImGuiWindow* ImGui::GetTopMostPopupModal()
1193b4be Poul Sander 2025-02-03 21:28 12369
{
1193b4be Poul Sander 2025-02-03 21:28 12370
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12371
    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 12372
        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
1193b4be Poul Sander 2025-02-03 21:28 12373
            if (popup->Flags & ImGuiWindowFlags_Modal)
1193b4be Poul Sander 2025-02-03 21:28 12374
                return popup;
1193b4be Poul Sander 2025-02-03 21:28 12375
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 12376
}
1193b4be Poul Sander 2025-02-03 21:28 12377
1193b4be Poul Sander 2025-02-03 21:28 12378
// See Demo->Stacked Modal to confirm what this is for.
1193b4be Poul Sander 2025-02-03 21:28 12379
ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
1193b4be Poul Sander 2025-02-03 21:28 12380
{
1193b4be Poul Sander 2025-02-03 21:28 12381
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12382
    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 12383
        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
1193b4be Poul Sander 2025-02-03 21:28 12384
            if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
1193b4be Poul Sander 2025-02-03 21:28 12385
                return popup;
1193b4be Poul Sander 2025-02-03 21:28 12386
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 12387
}
1193b4be Poul Sander 2025-02-03 21:28 12388
1193b4be Poul Sander 2025-02-03 21:28 12389
void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12390
{
1193b4be Poul Sander 2025-02-03 21:28 12391
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12392
    ImGuiID id = g.CurrentWindow->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 12393
    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
1193b4be Poul Sander 2025-02-03 21:28 12394
    OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12395
}
1193b4be Poul Sander 2025-02-03 21:28 12396
1193b4be Poul Sander 2025-02-03 21:28 12397
void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12398
{
1193b4be Poul Sander 2025-02-03 21:28 12399
    OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12400
}
1193b4be Poul Sander 2025-02-03 21:28 12401
1193b4be Poul Sander 2025-02-03 21:28 12402
// Mark popup as open (toggle toward open state).
1193b4be Poul Sander 2025-02-03 21:28 12403
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
1193b4be Poul Sander 2025-02-03 21:28 12404
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
1193b4be Poul Sander 2025-02-03 21:28 12405
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
1193b4be Poul Sander 2025-02-03 21:28 12406
void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12407
{
1193b4be Poul Sander 2025-02-03 21:28 12408
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12409
    ImGuiWindow* parent_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12410
    const int current_stack_size = g.BeginPopupStack.Size;
1193b4be Poul Sander 2025-02-03 21:28 12411
1193b4be Poul Sander 2025-02-03 21:28 12412
    if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
1193b4be Poul Sander 2025-02-03 21:28 12413
        if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
1193b4be Poul Sander 2025-02-03 21:28 12414
            return;
1193b4be Poul Sander 2025-02-03 21:28 12415
1193b4be Poul Sander 2025-02-03 21:28 12416
    ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
1193b4be Poul Sander 2025-02-03 21:28 12417
    popup_ref.PopupId = id;
1193b4be Poul Sander 2025-02-03 21:28 12418
    popup_ref.Window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 12419
    popup_ref.RestoreNavWindow = g.NavWindow;           // When popup closes focus may be restored to NavWindow (depend on window type).
1193b4be Poul Sander 2025-02-03 21:28 12420
    popup_ref.OpenFrameCount = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 12421
    popup_ref.OpenParentId = parent_window->IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 12422
    popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
1193b4be Poul Sander 2025-02-03 21:28 12423
    popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
1193b4be Poul Sander 2025-02-03 21:28 12424
1193b4be Poul Sander 2025-02-03 21:28 12425
    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
1193b4be Poul Sander 2025-02-03 21:28 12426
    if (g.OpenPopupStack.Size < current_stack_size + 1)
1193b4be Poul Sander 2025-02-03 21:28 12427
    {
1193b4be Poul Sander 2025-02-03 21:28 12428
        g.OpenPopupStack.push_back(popup_ref);
1193b4be Poul Sander 2025-02-03 21:28 12429
    }
1193b4be Poul Sander 2025-02-03 21:28 12430
    else
1193b4be Poul Sander 2025-02-03 21:28 12431
    {
1193b4be Poul Sander 2025-02-03 21:28 12432
        // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
1193b4be Poul Sander 2025-02-03 21:28 12433
        // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
1193b4be Poul Sander 2025-02-03 21:28 12434
        // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
1193b4be Poul Sander 2025-02-03 21:28 12435
        // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
1193b4be Poul Sander 2025-02-03 21:28 12436
        bool keep_existing = false;
1193b4be Poul Sander 2025-02-03 21:28 12437
        if (g.OpenPopupStack[current_stack_size].PopupId == id)
1193b4be Poul Sander 2025-02-03 21:28 12438
            if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
1193b4be Poul Sander 2025-02-03 21:28 12439
                keep_existing = true;
1193b4be Poul Sander 2025-02-03 21:28 12440
        if (keep_existing)
1193b4be Poul Sander 2025-02-03 21:28 12441
        {
1193b4be Poul Sander 2025-02-03 21:28 12442
            // No reopen
1193b4be Poul Sander 2025-02-03 21:28 12443
            g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
1193b4be Poul Sander 2025-02-03 21:28 12444
        }
1193b4be Poul Sander 2025-02-03 21:28 12445
        else
1193b4be Poul Sander 2025-02-03 21:28 12446
        {
1193b4be Poul Sander 2025-02-03 21:28 12447
            // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
1193b4be Poul Sander 2025-02-03 21:28 12448
            ClosePopupToLevel(current_stack_size, true);
1193b4be Poul Sander 2025-02-03 21:28 12449
            g.OpenPopupStack.push_back(popup_ref);
1193b4be Poul Sander 2025-02-03 21:28 12450
        }
1193b4be Poul Sander 2025-02-03 21:28 12451
1193b4be Poul Sander 2025-02-03 21:28 12452
        // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
1193b4be Poul Sander 2025-02-03 21:28 12453
        // This is equivalent to what ClosePopupToLevel() does.
1193b4be Poul Sander 2025-02-03 21:28 12454
        //if (g.OpenPopupStack[current_stack_size].PopupId == id)
1193b4be Poul Sander 2025-02-03 21:28 12455
        //    FocusWindow(parent_window);
1193b4be Poul Sander 2025-02-03 21:28 12456
    }
1193b4be Poul Sander 2025-02-03 21:28 12457
}
1193b4be Poul Sander 2025-02-03 21:28 12458
1193b4be Poul Sander 2025-02-03 21:28 12459
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
1193b4be Poul Sander 2025-02-03 21:28 12460
// This function closes any popups that are over 'ref_window'.
1193b4be Poul Sander 2025-02-03 21:28 12461
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
1193b4be Poul Sander 2025-02-03 21:28 12462
{
1193b4be Poul Sander 2025-02-03 21:28 12463
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12464
    if (g.OpenPopupStack.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 12465
        return;
1193b4be Poul Sander 2025-02-03 21:28 12466
1193b4be Poul Sander 2025-02-03 21:28 12467
    // Don't close our own child popup windows.
1193b4be Poul Sander 2025-02-03 21:28 12468
    //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
1193b4be Poul Sander 2025-02-03 21:28 12469
    int popup_count_to_keep = 0;
1193b4be Poul Sander 2025-02-03 21:28 12470
    if (ref_window)
1193b4be Poul Sander 2025-02-03 21:28 12471
    {
1193b4be Poul Sander 2025-02-03 21:28 12472
        // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 12473
        for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
1193b4be Poul Sander 2025-02-03 21:28 12474
        {
1193b4be Poul Sander 2025-02-03 21:28 12475
            ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
1193b4be Poul Sander 2025-02-03 21:28 12476
            if (!popup.Window)
1193b4be Poul Sander 2025-02-03 21:28 12477
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12478
            IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
1193b4be Poul Sander 2025-02-03 21:28 12479
1193b4be Poul Sander 2025-02-03 21:28 12480
            // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 12481
            // - Clicking/Focusing Window2 won't close Popup1:
1193b4be Poul Sander 2025-02-03 21:28 12482
            //     Window -> Popup1 -> Window2(Ref)
1193b4be Poul Sander 2025-02-03 21:28 12483
            // - Clicking/focusing Popup1 will close Popup2 and Popup3:
1193b4be Poul Sander 2025-02-03 21:28 12484
            //     Window -> Popup1(Ref) -> Popup2 -> Popup3
1193b4be Poul Sander 2025-02-03 21:28 12485
            // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!
1193b4be Poul Sander 2025-02-03 21:28 12486
            //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
1193b4be Poul Sander 2025-02-03 21:28 12487
            // We step through every popup from bottom to top to validate their position relative to reference window.
1193b4be Poul Sander 2025-02-03 21:28 12488
            bool ref_window_is_descendent_of_popup = false;
1193b4be Poul Sander 2025-02-03 21:28 12489
            for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 12490
                if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
1193b4be Poul Sander 2025-02-03 21:28 12491
                    //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE
1193b4be Poul Sander 2025-02-03 21:28 12492
                    if (IsWindowWithinBeginStackOf(ref_window, popup_window))
1193b4be Poul Sander 2025-02-03 21:28 12493
                    {
1193b4be Poul Sander 2025-02-03 21:28 12494
                        ref_window_is_descendent_of_popup = true;
1193b4be Poul Sander 2025-02-03 21:28 12495
                        break;
1193b4be Poul Sander 2025-02-03 21:28 12496
                    }
1193b4be Poul Sander 2025-02-03 21:28 12497
            if (!ref_window_is_descendent_of_popup)
1193b4be Poul Sander 2025-02-03 21:28 12498
                break;
1193b4be Poul Sander 2025-02-03 21:28 12499
        }
1193b4be Poul Sander 2025-02-03 21:28 12500
    }
1193b4be Poul Sander 2025-02-03 21:28 12501
    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
1193b4be Poul Sander 2025-02-03 21:28 12502
    {
1193b4be Poul Sander 2025-02-03 21:28 12503
        IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
1193b4be Poul Sander 2025-02-03 21:28 12504
        ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
1193b4be Poul Sander 2025-02-03 21:28 12505
    }
1193b4be Poul Sander 2025-02-03 21:28 12506
}
1193b4be Poul Sander 2025-02-03 21:28 12507
1193b4be Poul Sander 2025-02-03 21:28 12508
void ImGui::ClosePopupsExceptModals()
1193b4be Poul Sander 2025-02-03 21:28 12509
{
1193b4be Poul Sander 2025-02-03 21:28 12510
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12511
1193b4be Poul Sander 2025-02-03 21:28 12512
    int popup_count_to_keep;
1193b4be Poul Sander 2025-02-03 21:28 12513
    for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
1193b4be Poul Sander 2025-02-03 21:28 12514
    {
1193b4be Poul Sander 2025-02-03 21:28 12515
        ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
1193b4be Poul Sander 2025-02-03 21:28 12516
        if (!window || (window->Flags & ImGuiWindowFlags_Modal))
1193b4be Poul Sander 2025-02-03 21:28 12517
            break;
1193b4be Poul Sander 2025-02-03 21:28 12518
    }
1193b4be Poul Sander 2025-02-03 21:28 12519
    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
1193b4be Poul Sander 2025-02-03 21:28 12520
        ClosePopupToLevel(popup_count_to_keep, true);
1193b4be Poul Sander 2025-02-03 21:28 12521
}
1193b4be Poul Sander 2025-02-03 21:28 12522
1193b4be Poul Sander 2025-02-03 21:28 12523
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
1193b4be Poul Sander 2025-02-03 21:28 12524
{
1193b4be Poul Sander 2025-02-03 21:28 12525
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12526
    IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
1193b4be Poul Sander 2025-02-03 21:28 12527
    IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
1193b4be Poul Sander 2025-02-03 21:28 12528
    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
1193b4be Poul Sander 2025-02-03 21:28 12529
        for (int n = remaining; n < g.OpenPopupStack.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 12530
            IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL);
1193b4be Poul Sander 2025-02-03 21:28 12531
1193b4be Poul Sander 2025-02-03 21:28 12532
    // Trim open popup stack
1193b4be Poul Sander 2025-02-03 21:28 12533
    ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
1193b4be Poul Sander 2025-02-03 21:28 12534
    g.OpenPopupStack.resize(remaining);
1193b4be Poul Sander 2025-02-03 21:28 12535
1193b4be Poul Sander 2025-02-03 21:28 12536
    // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
1193b4be Poul Sander 2025-02-03 21:28 12537
    if (restore_focus_to_window_under_popup && prev_popup.Window)
1193b4be Poul Sander 2025-02-03 21:28 12538
    {
1193b4be Poul Sander 2025-02-03 21:28 12539
        ImGuiWindow* popup_window = prev_popup.Window;
1193b4be Poul Sander 2025-02-03 21:28 12540
        ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
1193b4be Poul Sander 2025-02-03 21:28 12541
        if (focus_window && !focus_window->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 12542
            FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
1193b4be Poul Sander 2025-02-03 21:28 12543
        else
1193b4be Poul Sander 2025-02-03 21:28 12544
            FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 12545
    }
1193b4be Poul Sander 2025-02-03 21:28 12546
}
1193b4be Poul Sander 2025-02-03 21:28 12547
1193b4be Poul Sander 2025-02-03 21:28 12548
// Close the popup we have begin-ed into.
1193b4be Poul Sander 2025-02-03 21:28 12549
void ImGui::CloseCurrentPopup()
1193b4be Poul Sander 2025-02-03 21:28 12550
{
1193b4be Poul Sander 2025-02-03 21:28 12551
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12552
    int popup_idx = g.BeginPopupStack.Size - 1;
1193b4be Poul Sander 2025-02-03 21:28 12553
    if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
1193b4be Poul Sander 2025-02-03 21:28 12554
        return;
1193b4be Poul Sander 2025-02-03 21:28 12555
1193b4be Poul Sander 2025-02-03 21:28 12556
    // Closing a menu closes its top-most parent popup (unless a modal)
1193b4be Poul Sander 2025-02-03 21:28 12557
    while (popup_idx > 0)
1193b4be Poul Sander 2025-02-03 21:28 12558
    {
1193b4be Poul Sander 2025-02-03 21:28 12559
        ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
1193b4be Poul Sander 2025-02-03 21:28 12560
        ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
1193b4be Poul Sander 2025-02-03 21:28 12561
        bool close_parent = false;
1193b4be Poul Sander 2025-02-03 21:28 12562
        if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
1193b4be Poul Sander 2025-02-03 21:28 12563
            if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
1193b4be Poul Sander 2025-02-03 21:28 12564
                close_parent = true;
1193b4be Poul Sander 2025-02-03 21:28 12565
        if (!close_parent)
1193b4be Poul Sander 2025-02-03 21:28 12566
            break;
1193b4be Poul Sander 2025-02-03 21:28 12567
        popup_idx--;
1193b4be Poul Sander 2025-02-03 21:28 12568
    }
1193b4be Poul Sander 2025-02-03 21:28 12569
    IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
1193b4be Poul Sander 2025-02-03 21:28 12570
    ClosePopupToLevel(popup_idx, true);
1193b4be Poul Sander 2025-02-03 21:28 12571
1193b4be Poul Sander 2025-02-03 21:28 12572
    // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
1193b4be Poul Sander 2025-02-03 21:28 12573
    // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
1193b4be Poul Sander 2025-02-03 21:28 12574
    // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
1193b4be Poul Sander 2025-02-03 21:28 12575
    if (ImGuiWindow* window = g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 12576
        window->DC.NavHideHighlightOneFrame = true;
1193b4be Poul Sander 2025-02-03 21:28 12577
}
1193b4be Poul Sander 2025-02-03 21:28 12578
1193b4be Poul Sander 2025-02-03 21:28 12579
// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
1193b4be Poul Sander 2025-02-03 21:28 12580
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
1193b4be Poul Sander 2025-02-03 21:28 12581
{
1193b4be Poul Sander 2025-02-03 21:28 12582
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12583
    if (!IsPopupOpen(id, ImGuiPopupFlags_None))
1193b4be Poul Sander 2025-02-03 21:28 12584
    {
1193b4be Poul Sander 2025-02-03 21:28 12585
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
1193b4be Poul Sander 2025-02-03 21:28 12586
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12587
    }
1193b4be Poul Sander 2025-02-03 21:28 12588
1193b4be Poul Sander 2025-02-03 21:28 12589
    char name[20];
1193b4be Poul Sander 2025-02-03 21:28 12590
    if (extra_window_flags & ImGuiWindowFlags_ChildMenu)
1193b4be Poul Sander 2025-02-03 21:28 12591
        ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
1193b4be Poul Sander 2025-02-03 21:28 12592
    else
1193b4be Poul Sander 2025-02-03 21:28 12593
        ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
1193b4be Poul Sander 2025-02-03 21:28 12594
1193b4be Poul Sander 2025-02-03 21:28 12595
    bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking);
1193b4be Poul Sander 2025-02-03 21:28 12596
    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
1193b4be Poul Sander 2025-02-03 21:28 12597
        EndPopup();
1193b4be Poul Sander 2025-02-03 21:28 12598
1193b4be Poul Sander 2025-02-03 21:28 12599
    //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
1193b4be Poul Sander 2025-02-03 21:28 12600
1193b4be Poul Sander 2025-02-03 21:28 12601
    return is_open;
1193b4be Poul Sander 2025-02-03 21:28 12602
}
1193b4be Poul Sander 2025-02-03 21:28 12603
1193b4be Poul Sander 2025-02-03 21:28 12604
bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 12605
{
1193b4be Poul Sander 2025-02-03 21:28 12606
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12607
    if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
1193b4be Poul Sander 2025-02-03 21:28 12608
    {
1193b4be Poul Sander 2025-02-03 21:28 12609
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
1193b4be Poul Sander 2025-02-03 21:28 12610
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12611
    }
1193b4be Poul Sander 2025-02-03 21:28 12612
    flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
1193b4be Poul Sander 2025-02-03 21:28 12613
    ImGuiID id = g.CurrentWindow->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 12614
    return BeginPopupEx(id, flags);
1193b4be Poul Sander 2025-02-03 21:28 12615
}
1193b4be Poul Sander 2025-02-03 21:28 12616
1193b4be Poul Sander 2025-02-03 21:28 12617
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
1193b4be Poul Sander 2025-02-03 21:28 12618
// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
1193b4be Poul Sander 2025-02-03 21:28 12619
// - *p_open set back to false in BeginPopupModal() when popup is not open.
1193b4be Poul Sander 2025-02-03 21:28 12620
// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
1193b4be Poul Sander 2025-02-03 21:28 12621
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 12622
{
1193b4be Poul Sander 2025-02-03 21:28 12623
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12624
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12625
    const ImGuiID id = window->GetID(name);
1193b4be Poul Sander 2025-02-03 21:28 12626
    if (!IsPopupOpen(id, ImGuiPopupFlags_None))
1193b4be Poul Sander 2025-02-03 21:28 12627
    {
1193b4be Poul Sander 2025-02-03 21:28 12628
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
1193b4be Poul Sander 2025-02-03 21:28 12629
        if (p_open && *p_open)
1193b4be Poul Sander 2025-02-03 21:28 12630
            *p_open = false;
1193b4be Poul Sander 2025-02-03 21:28 12631
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12632
    }
1193b4be Poul Sander 2025-02-03 21:28 12633
1193b4be Poul Sander 2025-02-03 21:28 12634
    // Center modal windows by default for increased visibility
1193b4be Poul Sander 2025-02-03 21:28 12635
    // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
1193b4be Poul Sander 2025-02-03 21:28 12636
    // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
1193b4be Poul Sander 2025-02-03 21:28 12637
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
1193b4be Poul Sander 2025-02-03 21:28 12638
    {
1193b4be Poul Sander 2025-02-03 21:28 12639
        const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
1193b4be Poul Sander 2025-02-03 21:28 12640
        SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
1193b4be Poul Sander 2025-02-03 21:28 12641
    }
1193b4be Poul Sander 2025-02-03 21:28 12642
1193b4be Poul Sander 2025-02-03 21:28 12643
    flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
1193b4be Poul Sander 2025-02-03 21:28 12644
    const bool is_open = Begin(name, p_open, flags);
1193b4be Poul Sander 2025-02-03 21:28 12645
    if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
1193b4be Poul Sander 2025-02-03 21:28 12646
    {
1193b4be Poul Sander 2025-02-03 21:28 12647
        EndPopup();
1193b4be Poul Sander 2025-02-03 21:28 12648
        if (is_open)
1193b4be Poul Sander 2025-02-03 21:28 12649
            ClosePopupToLevel(g.BeginPopupStack.Size, true);
1193b4be Poul Sander 2025-02-03 21:28 12650
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12651
    }
1193b4be Poul Sander 2025-02-03 21:28 12652
    return is_open;
1193b4be Poul Sander 2025-02-03 21:28 12653
}
1193b4be Poul Sander 2025-02-03 21:28 12654
1193b4be Poul Sander 2025-02-03 21:28 12655
void ImGui::EndPopup()
1193b4be Poul Sander 2025-02-03 21:28 12656
{
1193b4be Poul Sander 2025-02-03 21:28 12657
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12658
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12659
    IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls
1193b4be Poul Sander 2025-02-03 21:28 12660
    IM_ASSERT(g.BeginPopupStack.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 12661
1193b4be Poul Sander 2025-02-03 21:28 12662
    // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
1193b4be Poul Sander 2025-02-03 21:28 12663
    if (g.NavWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 12664
        NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
1193b4be Poul Sander 2025-02-03 21:28 12665
1193b4be Poul Sander 2025-02-03 21:28 12666
    // Child-popups don't need to be laid out
1193b4be Poul Sander 2025-02-03 21:28 12667
    IM_ASSERT(g.WithinEndChild == false);
1193b4be Poul Sander 2025-02-03 21:28 12668
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 12669
        g.WithinEndChild = true;
1193b4be Poul Sander 2025-02-03 21:28 12670
    End();
1193b4be Poul Sander 2025-02-03 21:28 12671
    g.WithinEndChild = false;
1193b4be Poul Sander 2025-02-03 21:28 12672
}
1193b4be Poul Sander 2025-02-03 21:28 12673
1193b4be Poul Sander 2025-02-03 21:28 12674
// Helper to open a popup if mouse button is released over the item
1193b4be Poul Sander 2025-02-03 21:28 12675
// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
1193b4be Poul Sander 2025-02-03 21:28 12676
void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12677
{
1193b4be Poul Sander 2025-02-03 21:28 12678
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12679
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12680
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
1193b4be Poul Sander 2025-02-03 21:28 12681
    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1193b4be Poul Sander 2025-02-03 21:28 12682
    {
1193b4be Poul Sander 2025-02-03 21:28 12683
        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
1193b4be Poul Sander 2025-02-03 21:28 12684
        IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
1193b4be Poul Sander 2025-02-03 21:28 12685
        OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12686
    }
1193b4be Poul Sander 2025-02-03 21:28 12687
}
1193b4be Poul Sander 2025-02-03 21:28 12688
1193b4be Poul Sander 2025-02-03 21:28 12689
// This is a helper to handle the simplest case of associating one named popup to one given widget.
1193b4be Poul Sander 2025-02-03 21:28 12690
// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
1193b4be Poul Sander 2025-02-03 21:28 12691
// - To create a popup with a specific identifier, pass it in str_id.
1193b4be Poul Sander 2025-02-03 21:28 12692
//    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
1193b4be Poul Sander 2025-02-03 21:28 12693
//    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
1193b4be Poul Sander 2025-02-03 21:28 12694
// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
1193b4be Poul Sander 2025-02-03 21:28 12695
//   This is essentially the same as:
1193b4be Poul Sander 2025-02-03 21:28 12696
//       id = str_id ? GetID(str_id) : GetItemID();
1193b4be Poul Sander 2025-02-03 21:28 12697
//       OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
1193b4be Poul Sander 2025-02-03 21:28 12698
//       return BeginPopup(id);
1193b4be Poul Sander 2025-02-03 21:28 12699
//   Which is essentially the same as:
1193b4be Poul Sander 2025-02-03 21:28 12700
//       id = str_id ? GetID(str_id) : GetItemID();
1193b4be Poul Sander 2025-02-03 21:28 12701
//       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
1193b4be Poul Sander 2025-02-03 21:28 12702
//           OpenPopup(id);
1193b4be Poul Sander 2025-02-03 21:28 12703
//       return BeginPopup(id);
1193b4be Poul Sander 2025-02-03 21:28 12704
//   The main difference being that this is tweaked to avoid computing the ID twice.
1193b4be Poul Sander 2025-02-03 21:28 12705
bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12706
{
1193b4be Poul Sander 2025-02-03 21:28 12707
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12708
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12709
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 12710
        return false;
1193b4be Poul Sander 2025-02-03 21:28 12711
    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
1193b4be Poul Sander 2025-02-03 21:28 12712
    IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
1193b4be Poul Sander 2025-02-03 21:28 12713
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
1193b4be Poul Sander 2025-02-03 21:28 12714
    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1193b4be Poul Sander 2025-02-03 21:28 12715
        OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12716
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
1193b4be Poul Sander 2025-02-03 21:28 12717
}
1193b4be Poul Sander 2025-02-03 21:28 12718
1193b4be Poul Sander 2025-02-03 21:28 12719
bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12720
{
1193b4be Poul Sander 2025-02-03 21:28 12721
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12722
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12723
    if (!str_id)
1193b4be Poul Sander 2025-02-03 21:28 12724
        str_id = "window_context";
1193b4be Poul Sander 2025-02-03 21:28 12725
    ImGuiID id = window->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 12726
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
1193b4be Poul Sander 2025-02-03 21:28 12727
    if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1193b4be Poul Sander 2025-02-03 21:28 12728
        if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 12729
            OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12730
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
1193b4be Poul Sander 2025-02-03 21:28 12731
}
1193b4be Poul Sander 2025-02-03 21:28 12732
1193b4be Poul Sander 2025-02-03 21:28 12733
bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
1193b4be Poul Sander 2025-02-03 21:28 12734
{
1193b4be Poul Sander 2025-02-03 21:28 12735
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12736
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12737
    if (!str_id)
1193b4be Poul Sander 2025-02-03 21:28 12738
        str_id = "void_context";
1193b4be Poul Sander 2025-02-03 21:28 12739
    ImGuiID id = window->GetID(str_id);
1193b4be Poul Sander 2025-02-03 21:28 12740
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
1193b4be Poul Sander 2025-02-03 21:28 12741
    if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
1193b4be Poul Sander 2025-02-03 21:28 12742
        if (GetTopMostPopupModal() == NULL)
1193b4be Poul Sander 2025-02-03 21:28 12743
            OpenPopupEx(id, popup_flags);
1193b4be Poul Sander 2025-02-03 21:28 12744
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
1193b4be Poul Sander 2025-02-03 21:28 12745
}
1193b4be Poul Sander 2025-02-03 21:28 12746
1193b4be Poul Sander 2025-02-03 21:28 12747
// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
1193b4be Poul Sander 2025-02-03 21:28 12748
// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
1193b4be Poul Sander 2025-02-03 21:28 12749
// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
1193b4be Poul Sander 2025-02-03 21:28 12750
//  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
1193b4be Poul Sander 2025-02-03 21:28 12751
//  this allows us to have tooltips/popups displayed out of the parent viewport.)
1193b4be Poul Sander 2025-02-03 21:28 12752
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
1193b4be Poul Sander 2025-02-03 21:28 12753
{
1193b4be Poul Sander 2025-02-03 21:28 12754
    ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
1193b4be Poul Sander 2025-02-03 21:28 12755
    //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
1193b4be Poul Sander 2025-02-03 21:28 12756
    //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
1193b4be Poul Sander 2025-02-03 21:28 12757
1193b4be Poul Sander 2025-02-03 21:28 12758
    // Combo Box policy (we want a connecting edge)
1193b4be Poul Sander 2025-02-03 21:28 12759
    if (policy == ImGuiPopupPositionPolicy_ComboBox)
1193b4be Poul Sander 2025-02-03 21:28 12760
    {
1193b4be Poul Sander 2025-02-03 21:28 12761
        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
1193b4be Poul Sander 2025-02-03 21:28 12762
        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
1193b4be Poul Sander 2025-02-03 21:28 12763
        {
1193b4be Poul Sander 2025-02-03 21:28 12764
            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
1193b4be Poul Sander 2025-02-03 21:28 12765
            if (n != -1 && dir == *last_dir) // Already tried this direction?
1193b4be Poul Sander 2025-02-03 21:28 12766
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12767
            ImVec2 pos;
1193b4be Poul Sander 2025-02-03 21:28 12768
            if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
1193b4be Poul Sander 2025-02-03 21:28 12769
            if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
1193b4be Poul Sander 2025-02-03 21:28 12770
            if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
1193b4be Poul Sander 2025-02-03 21:28 12771
            if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
1193b4be Poul Sander 2025-02-03 21:28 12772
            if (!r_outer.Contains(ImRect(pos, pos + size)))
1193b4be Poul Sander 2025-02-03 21:28 12773
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12774
            *last_dir = dir;
1193b4be Poul Sander 2025-02-03 21:28 12775
            return pos;
1193b4be Poul Sander 2025-02-03 21:28 12776
        }
1193b4be Poul Sander 2025-02-03 21:28 12777
    }
1193b4be Poul Sander 2025-02-03 21:28 12778
1193b4be Poul Sander 2025-02-03 21:28 12779
    // Tooltip and Default popup policy
1193b4be Poul Sander 2025-02-03 21:28 12780
    // (Always first try the direction we used on the last frame, if any)
1193b4be Poul Sander 2025-02-03 21:28 12781
    if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
1193b4be Poul Sander 2025-02-03 21:28 12782
    {
1193b4be Poul Sander 2025-02-03 21:28 12783
        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
1193b4be Poul Sander 2025-02-03 21:28 12784
        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
1193b4be Poul Sander 2025-02-03 21:28 12785
        {
1193b4be Poul Sander 2025-02-03 21:28 12786
            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
1193b4be Poul Sander 2025-02-03 21:28 12787
            if (n != -1 && dir == *last_dir) // Already tried this direction?
1193b4be Poul Sander 2025-02-03 21:28 12788
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12789
1193b4be Poul Sander 2025-02-03 21:28 12790
            const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
1193b4be Poul Sander 2025-02-03 21:28 12791
            const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
1193b4be Poul Sander 2025-02-03 21:28 12792
1193b4be Poul Sander 2025-02-03 21:28 12793
            // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
1193b4be Poul Sander 2025-02-03 21:28 12794
            if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
1193b4be Poul Sander 2025-02-03 21:28 12795
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12796
            if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
1193b4be Poul Sander 2025-02-03 21:28 12797
                continue;
1193b4be Poul Sander 2025-02-03 21:28 12798
1193b4be Poul Sander 2025-02-03 21:28 12799
            ImVec2 pos;
1193b4be Poul Sander 2025-02-03 21:28 12800
            pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
1193b4be Poul Sander 2025-02-03 21:28 12801
            pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
1193b4be Poul Sander 2025-02-03 21:28 12802
1193b4be Poul Sander 2025-02-03 21:28 12803
            // Clamp top-left corner of popup
1193b4be Poul Sander 2025-02-03 21:28 12804
            pos.x = ImMax(pos.x, r_outer.Min.x);
1193b4be Poul Sander 2025-02-03 21:28 12805
            pos.y = ImMax(pos.y, r_outer.Min.y);
1193b4be Poul Sander 2025-02-03 21:28 12806
1193b4be Poul Sander 2025-02-03 21:28 12807
            *last_dir = dir;
1193b4be Poul Sander 2025-02-03 21:28 12808
            return pos;
1193b4be Poul Sander 2025-02-03 21:28 12809
        }
1193b4be Poul Sander 2025-02-03 21:28 12810
    }
1193b4be Poul Sander 2025-02-03 21:28 12811
1193b4be Poul Sander 2025-02-03 21:28 12812
    // Fallback when not enough room:
1193b4be Poul Sander 2025-02-03 21:28 12813
    *last_dir = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 12814
1193b4be Poul Sander 2025-02-03 21:28 12815
    // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
1193b4be Poul Sander 2025-02-03 21:28 12816
    if (policy == ImGuiPopupPositionPolicy_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 12817
        return ref_pos + ImVec2(2, 2);
1193b4be Poul Sander 2025-02-03 21:28 12818
1193b4be Poul Sander 2025-02-03 21:28 12819
    // Otherwise try to keep within display
1193b4be Poul Sander 2025-02-03 21:28 12820
    ImVec2 pos = ref_pos;
1193b4be Poul Sander 2025-02-03 21:28 12821
    pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
1193b4be Poul Sander 2025-02-03 21:28 12822
    pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
1193b4be Poul Sander 2025-02-03 21:28 12823
    return pos;
1193b4be Poul Sander 2025-02-03 21:28 12824
}
1193b4be Poul Sander 2025-02-03 21:28 12825
1193b4be Poul Sander 2025-02-03 21:28 12826
// Note that this is used for popups, which can overlap the non work-area of individual viewports.
1193b4be Poul Sander 2025-02-03 21:28 12827
ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 12828
{
1193b4be Poul Sander 2025-02-03 21:28 12829
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12830
    ImRect r_screen;
1193b4be Poul Sander 2025-02-03 21:28 12831
    if (window->ViewportAllowPlatformMonitorExtend >= 0)
1193b4be Poul Sander 2025-02-03 21:28 12832
    {
1193b4be Poul Sander 2025-02-03 21:28 12833
        // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
1193b4be Poul Sander 2025-02-03 21:28 12834
        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
1193b4be Poul Sander 2025-02-03 21:28 12835
        r_screen.Min = monitor.WorkPos;
1193b4be Poul Sander 2025-02-03 21:28 12836
        r_screen.Max = monitor.WorkPos + monitor.WorkSize;
1193b4be Poul Sander 2025-02-03 21:28 12837
    }
1193b4be Poul Sander 2025-02-03 21:28 12838
    else
1193b4be Poul Sander 2025-02-03 21:28 12839
    {
1193b4be Poul Sander 2025-02-03 21:28 12840
        // Use the full viewport area (not work area) for popups
1193b4be Poul Sander 2025-02-03 21:28 12841
        r_screen = window->Viewport->GetMainRect();
1193b4be Poul Sander 2025-02-03 21:28 12842
    }
1193b4be Poul Sander 2025-02-03 21:28 12843
    ImVec2 padding = g.Style.DisplaySafeAreaPadding;
1193b4be Poul Sander 2025-02-03 21:28 12844
    r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 12845
    return r_screen;
1193b4be Poul Sander 2025-02-03 21:28 12846
}
1193b4be Poul Sander 2025-02-03 21:28 12847
1193b4be Poul Sander 2025-02-03 21:28 12848
ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 12849
{
1193b4be Poul Sander 2025-02-03 21:28 12850
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12851
1193b4be Poul Sander 2025-02-03 21:28 12852
    ImRect r_outer = GetPopupAllowedExtentRect(window);
1193b4be Poul Sander 2025-02-03 21:28 12853
    if (window->Flags & ImGuiWindowFlags_ChildMenu)
1193b4be Poul Sander 2025-02-03 21:28 12854
    {
1193b4be Poul Sander 2025-02-03 21:28 12855
        // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
1193b4be Poul Sander 2025-02-03 21:28 12856
        // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
1193b4be Poul Sander 2025-02-03 21:28 12857
        ImGuiWindow* parent_window = window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 12858
        float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
1193b4be Poul Sander 2025-02-03 21:28 12859
        ImRect r_avoid;
1193b4be Poul Sander 2025-02-03 21:28 12860
        if (parent_window->DC.MenuBarAppending)
1193b4be Poul Sander 2025-02-03 21:28 12861
            r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
1193b4be Poul Sander 2025-02-03 21:28 12862
        else
1193b4be Poul Sander 2025-02-03 21:28 12863
            r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 12864
        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
1193b4be Poul Sander 2025-02-03 21:28 12865
    }
1193b4be Poul Sander 2025-02-03 21:28 12866
    if (window->Flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 12867
    {
1193b4be Poul Sander 2025-02-03 21:28 12868
        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
1193b4be Poul Sander 2025-02-03 21:28 12869
    }
1193b4be Poul Sander 2025-02-03 21:28 12870
    if (window->Flags & ImGuiWindowFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 12871
    {
1193b4be Poul Sander 2025-02-03 21:28 12872
        // Position tooltip (always follows mouse + clamp within outer boundaries)
1193b4be Poul Sander 2025-02-03 21:28 12873
        // FIXME:
1193b4be Poul Sander 2025-02-03 21:28 12874
        // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
1193b4be Poul Sander 2025-02-03 21:28 12875
        // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
1193b4be Poul Sander 2025-02-03 21:28 12876
        // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
1193b4be Poul Sander 2025-02-03 21:28 12877
        //   as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
1193b4be Poul Sander 2025-02-03 21:28 12878
        IM_ASSERT(g.CurrentWindow == window);
1193b4be Poul Sander 2025-02-03 21:28 12879
        const float scale = g.Style.MouseCursorScale;
1193b4be Poul Sander 2025-02-03 21:28 12880
        const ImVec2 ref_pos = NavCalcPreferredRefPos();
1193b4be Poul Sander 2025-02-03 21:28 12881
1193b4be Poul Sander 2025-02-03 21:28 12882
        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
1193b4be Poul Sander 2025-02-03 21:28 12883
        {
1193b4be Poul Sander 2025-02-03 21:28 12884
            ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
1193b4be Poul Sander 2025-02-03 21:28 12885
            if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
1193b4be Poul Sander 2025-02-03 21:28 12886
                return tooltip_pos;
1193b4be Poul Sander 2025-02-03 21:28 12887
        }
1193b4be Poul Sander 2025-02-03 21:28 12888
1193b4be Poul Sander 2025-02-03 21:28 12889
        ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
1193b4be Poul Sander 2025-02-03 21:28 12890
        ImRect r_avoid;
1193b4be Poul Sander 2025-02-03 21:28 12891
        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
1193b4be Poul Sander 2025-02-03 21:28 12892
            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
1193b4be Poul Sander 2025-02-03 21:28 12893
        else
1193b4be Poul Sander 2025-02-03 21:28 12894
            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
1193b4be Poul Sander 2025-02-03 21:28 12895
        //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
1193b4be Poul Sander 2025-02-03 21:28 12896
1193b4be Poul Sander 2025-02-03 21:28 12897
        return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
1193b4be Poul Sander 2025-02-03 21:28 12898
    }
1193b4be Poul Sander 2025-02-03 21:28 12899
    IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 12900
    return window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 12901
}
1193b4be Poul Sander 2025-02-03 21:28 12902
1193b4be Poul Sander 2025-02-03 21:28 12903
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12904
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
1193b4be Poul Sander 2025-02-03 21:28 12905
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 12906
1193b4be Poul Sander 2025-02-03 21:28 12907
// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
1193b4be Poul Sander 2025-02-03 21:28 12908
// In our terminology those should be interchangeable, yet right now this is super confusing.
1193b4be Poul Sander 2025-02-03 21:28 12909
// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
1193b4be Poul Sander 2025-02-03 21:28 12910
1193b4be Poul Sander 2025-02-03 21:28 12911
void ImGui::SetNavCursorVisible(bool visible)
1193b4be Poul Sander 2025-02-03 21:28 12912
{
1193b4be Poul Sander 2025-02-03 21:28 12913
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12914
    if (g.IO.ConfigNavCursorVisibleAlways)
1193b4be Poul Sander 2025-02-03 21:28 12915
        visible = true;
1193b4be Poul Sander 2025-02-03 21:28 12916
    g.NavCursorVisible = visible;
1193b4be Poul Sander 2025-02-03 21:28 12917
}
1193b4be Poul Sander 2025-02-03 21:28 12918
1193b4be Poul Sander 2025-02-03 21:28 12919
// (was called NavRestoreHighlightAfterMove() before 1.91.4)
1193b4be Poul Sander 2025-02-03 21:28 12920
void ImGui::SetNavCursorVisibleAfterMove()
1193b4be Poul Sander 2025-02-03 21:28 12921
{
1193b4be Poul Sander 2025-02-03 21:28 12922
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12923
    if (g.IO.ConfigNavCursorVisibleAuto)
1193b4be Poul Sander 2025-02-03 21:28 12924
        g.NavCursorVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 12925
    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
1193b4be Poul Sander 2025-02-03 21:28 12926
}
1193b4be Poul Sander 2025-02-03 21:28 12927
1193b4be Poul Sander 2025-02-03 21:28 12928
void ImGui::SetNavWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 12929
{
1193b4be Poul Sander 2025-02-03 21:28 12930
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12931
    if (g.NavWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 12932
    {
1193b4be Poul Sander 2025-02-03 21:28 12933
        IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
1193b4be Poul Sander 2025-02-03 21:28 12934
        g.NavWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 12935
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
1193b4be Poul Sander 2025-02-03 21:28 12936
    }
1193b4be Poul Sander 2025-02-03 21:28 12937
    g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 12938
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 12939
}
1193b4be Poul Sander 2025-02-03 21:28 12940
1193b4be Poul Sander 2025-02-03 21:28 12941
void ImGui::NavHighlightActivated(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 12942
{
1193b4be Poul Sander 2025-02-03 21:28 12943
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12944
    g.NavHighlightActivatedId = id;
1193b4be Poul Sander 2025-02-03 21:28 12945
    g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
1193b4be Poul Sander 2025-02-03 21:28 12946
}
1193b4be Poul Sander 2025-02-03 21:28 12947
1193b4be Poul Sander 2025-02-03 21:28 12948
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
1193b4be Poul Sander 2025-02-03 21:28 12949
{
1193b4be Poul Sander 2025-02-03 21:28 12950
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12951
    g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 12952
}
1193b4be Poul Sander 2025-02-03 21:28 12953
1193b4be Poul Sander 2025-02-03 21:28 12954
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
1193b4be Poul Sander 2025-02-03 21:28 12955
{
1193b4be Poul Sander 2025-02-03 21:28 12956
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12957
    IM_ASSERT(g.NavWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 12958
    IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
1193b4be Poul Sander 2025-02-03 21:28 12959
    g.NavId = id;
1193b4be Poul Sander 2025-02-03 21:28 12960
    g.NavLayer = nav_layer;
1193b4be Poul Sander 2025-02-03 21:28 12961
    SetNavFocusScope(focus_scope_id);
1193b4be Poul Sander 2025-02-03 21:28 12962
    g.NavWindow->NavLastIds[nav_layer] = id;
1193b4be Poul Sander 2025-02-03 21:28 12963
    g.NavWindow->NavRectRel[nav_layer] = rect_rel;
1193b4be Poul Sander 2025-02-03 21:28 12964
1193b4be Poul Sander 2025-02-03 21:28 12965
    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
1193b4be Poul Sander 2025-02-03 21:28 12966
    NavClearPreferredPosForAxis(ImGuiAxis_X);
1193b4be Poul Sander 2025-02-03 21:28 12967
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
1193b4be Poul Sander 2025-02-03 21:28 12968
}
1193b4be Poul Sander 2025-02-03 21:28 12969
1193b4be Poul Sander 2025-02-03 21:28 12970
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 12971
{
1193b4be Poul Sander 2025-02-03 21:28 12972
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 12973
    IM_ASSERT(id != 0);
1193b4be Poul Sander 2025-02-03 21:28 12974
1193b4be Poul Sander 2025-02-03 21:28 12975
    if (g.NavWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 12976
       SetNavWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 12977
1193b4be Poul Sander 2025-02-03 21:28 12978
    // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
1193b4be Poul Sander 2025-02-03 21:28 12979
    // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
1193b4be Poul Sander 2025-02-03 21:28 12980
    const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
1193b4be Poul Sander 2025-02-03 21:28 12981
    g.NavId = id;
1193b4be Poul Sander 2025-02-03 21:28 12982
    g.NavLayer = nav_layer;
1193b4be Poul Sander 2025-02-03 21:28 12983
    SetNavFocusScope(g.CurrentFocusScopeId);
1193b4be Poul Sander 2025-02-03 21:28 12984
    window->NavLastIds[nav_layer] = id;
1193b4be Poul Sander 2025-02-03 21:28 12985
    if (g.LastItemData.ID == id)
1193b4be Poul Sander 2025-02-03 21:28 12986
        window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
1193b4be Poul Sander 2025-02-03 21:28 12987
1193b4be Poul Sander 2025-02-03 21:28 12988
    if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
1193b4be Poul Sander 2025-02-03 21:28 12989
        g.NavHighlightItemUnderNav = true;
1193b4be Poul Sander 2025-02-03 21:28 12990
    else if (g.IO.ConfigNavCursorVisibleAuto)
1193b4be Poul Sander 2025-02-03 21:28 12991
        g.NavCursorVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 12992
1193b4be Poul Sander 2025-02-03 21:28 12993
    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
1193b4be Poul Sander 2025-02-03 21:28 12994
    NavClearPreferredPosForAxis(ImGuiAxis_X);
1193b4be Poul Sander 2025-02-03 21:28 12995
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
1193b4be Poul Sander 2025-02-03 21:28 12996
}
1193b4be Poul Sander 2025-02-03 21:28 12997
1193b4be Poul Sander 2025-02-03 21:28 12998
static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
1193b4be Poul Sander 2025-02-03 21:28 12999
{
1193b4be Poul Sander 2025-02-03 21:28 13000
    if (ImFabs(dx) > ImFabs(dy))
1193b4be Poul Sander 2025-02-03 21:28 13001
        return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
1193b4be Poul Sander 2025-02-03 21:28 13002
    return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
1193b4be Poul Sander 2025-02-03 21:28 13003
}
1193b4be Poul Sander 2025-02-03 21:28 13004
1193b4be Poul Sander 2025-02-03 21:28 13005
static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
1193b4be Poul Sander 2025-02-03 21:28 13006
{
1193b4be Poul Sander 2025-02-03 21:28 13007
    if (cand_max < curr_min)
1193b4be Poul Sander 2025-02-03 21:28 13008
        return cand_max - curr_min;
1193b4be Poul Sander 2025-02-03 21:28 13009
    if (curr_max < cand_min)
1193b4be Poul Sander 2025-02-03 21:28 13010
        return cand_min - curr_max;
1193b4be Poul Sander 2025-02-03 21:28 13011
    return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 13012
}
1193b4be Poul Sander 2025-02-03 21:28 13013
1193b4be Poul Sander 2025-02-03 21:28 13014
// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
1193b4be Poul Sander 2025-02-03 21:28 13015
static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
1193b4be Poul Sander 2025-02-03 21:28 13016
{
1193b4be Poul Sander 2025-02-03 21:28 13017
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13018
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 13019
    if (g.NavLayer != window->DC.NavLayerCurrent)
1193b4be Poul Sander 2025-02-03 21:28 13020
        return false;
1193b4be Poul Sander 2025-02-03 21:28 13021
1193b4be Poul Sander 2025-02-03 21:28 13022
    // FIXME: Those are not good variables names
1193b4be Poul Sander 2025-02-03 21:28 13023
    ImRect cand = g.LastItemData.NavRect;   // Current item nav rectangle
1193b4be Poul Sander 2025-02-03 21:28 13024
    const ImRect curr = g.NavScoringRect;   // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
1193b4be Poul Sander 2025-02-03 21:28 13025
    g.NavScoringDebugCount++;
1193b4be Poul Sander 2025-02-03 21:28 13026
1193b4be Poul Sander 2025-02-03 21:28 13027
    // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
1193b4be Poul Sander 2025-02-03 21:28 13028
    if (window->ParentWindow == g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13029
    {
1193b4be Poul Sander 2025-02-03 21:28 13030
        IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
1193b4be Poul Sander 2025-02-03 21:28 13031
        if (!window->ClipRect.Overlaps(cand))
1193b4be Poul Sander 2025-02-03 21:28 13032
            return false;
1193b4be Poul Sander 2025-02-03 21:28 13033
        cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
1193b4be Poul Sander 2025-02-03 21:28 13034
    }
1193b4be Poul Sander 2025-02-03 21:28 13035
1193b4be Poul Sander 2025-02-03 21:28 13036
    // Compute distance between boxes
1193b4be Poul Sander 2025-02-03 21:28 13037
    // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
1193b4be Poul Sander 2025-02-03 21:28 13038
    float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 13039
    float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
1193b4be Poul Sander 2025-02-03 21:28 13040
    if (dby != 0.0f && dbx != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13041
        dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
1193b4be Poul Sander 2025-02-03 21:28 13042
    float dist_box = ImFabs(dbx) + ImFabs(dby);
1193b4be Poul Sander 2025-02-03 21:28 13043
1193b4be Poul Sander 2025-02-03 21:28 13044
    // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
1193b4be Poul Sander 2025-02-03 21:28 13045
    float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 13046
    float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 13047
    float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
1193b4be Poul Sander 2025-02-03 21:28 13048
1193b4be Poul Sander 2025-02-03 21:28 13049
    // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
1193b4be Poul Sander 2025-02-03 21:28 13050
    ImGuiDir quadrant;
1193b4be Poul Sander 2025-02-03 21:28 13051
    float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 13052
    if (dbx != 0.0f || dby != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13053
    {
1193b4be Poul Sander 2025-02-03 21:28 13054
        // For non-overlapping boxes, use distance between boxes
1193b4be Poul Sander 2025-02-03 21:28 13055
        // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
1193b4be Poul Sander 2025-02-03 21:28 13056
        // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
1193b4be Poul Sander 2025-02-03 21:28 13057
        // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
1193b4be Poul Sander 2025-02-03 21:28 13058
        dax = dbx;
1193b4be Poul Sander 2025-02-03 21:28 13059
        day = dby;
1193b4be Poul Sander 2025-02-03 21:28 13060
        dist_axial = dist_box;
1193b4be Poul Sander 2025-02-03 21:28 13061
        quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
1193b4be Poul Sander 2025-02-03 21:28 13062
    }
1193b4be Poul Sander 2025-02-03 21:28 13063
    else if (dcx != 0.0f || dcy != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13064
    {
1193b4be Poul Sander 2025-02-03 21:28 13065
        // For overlapping boxes with different centers, use distance between centers
1193b4be Poul Sander 2025-02-03 21:28 13066
        dax = dcx;
1193b4be Poul Sander 2025-02-03 21:28 13067
        day = dcy;
1193b4be Poul Sander 2025-02-03 21:28 13068
        dist_axial = dist_center;
1193b4be Poul Sander 2025-02-03 21:28 13069
        quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
1193b4be Poul Sander 2025-02-03 21:28 13070
    }
1193b4be Poul Sander 2025-02-03 21:28 13071
    else
1193b4be Poul Sander 2025-02-03 21:28 13072
    {
1193b4be Poul Sander 2025-02-03 21:28 13073
        // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
1193b4be Poul Sander 2025-02-03 21:28 13074
        quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
1193b4be Poul Sander 2025-02-03 21:28 13075
    }
1193b4be Poul Sander 2025-02-03 21:28 13076
1193b4be Poul Sander 2025-02-03 21:28 13077
    const ImGuiDir move_dir = g.NavMoveDir;
1193b4be Poul Sander 2025-02-03 21:28 13078
#if IMGUI_DEBUG_NAV_SCORING
1193b4be Poul Sander 2025-02-03 21:28 13079
    char buf[200];
1193b4be Poul Sander 2025-02-03 21:28 13080
    if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
1193b4be Poul Sander 2025-02-03 21:28 13081
    {
1193b4be Poul Sander 2025-02-03 21:28 13082
        if (quadrant == move_dir)
1193b4be Poul Sander 2025-02-03 21:28 13083
        {
1193b4be Poul Sander 2025-02-03 21:28 13084
            ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
1193b4be Poul Sander 2025-02-03 21:28 13085
            ImDrawList* draw_list = GetForegroundDrawList(window);
1193b4be Poul Sander 2025-02-03 21:28 13086
            draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
1193b4be Poul Sander 2025-02-03 21:28 13087
            draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
1193b4be Poul Sander 2025-02-03 21:28 13088
            draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
1193b4be Poul Sander 2025-02-03 21:28 13089
        }
1193b4be Poul Sander 2025-02-03 21:28 13090
    }
1193b4be Poul Sander 2025-02-03 21:28 13091
    const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
1193b4be Poul Sander 2025-02-03 21:28 13092
    const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
1193b4be Poul Sander 2025-02-03 21:28 13093
    if (debug_hovering || debug_tty)
1193b4be Poul Sander 2025-02-03 21:28 13094
    {
1193b4be Poul Sander 2025-02-03 21:28 13095
        ImFormatString(buf, IM_ARRAYSIZE(buf),
1193b4be Poul Sander 2025-02-03 21:28 13096
            "d-box    (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial  (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c",
1193b4be Poul Sander 2025-02-03 21:28 13097
            dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
1193b4be Poul Sander 2025-02-03 21:28 13098
        if (debug_hovering)
1193b4be Poul Sander 2025-02-03 21:28 13099
        {
1193b4be Poul Sander 2025-02-03 21:28 13100
            ImDrawList* draw_list = GetForegroundDrawList(window);
1193b4be Poul Sander 2025-02-03 21:28 13101
            draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
1193b4be Poul Sander 2025-02-03 21:28 13102
            draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
1193b4be Poul Sander 2025-02-03 21:28 13103
            draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
1193b4be Poul Sander 2025-02-03 21:28 13104
            draw_list->AddText(cand.Max, ~0U, buf);
1193b4be Poul Sander 2025-02-03 21:28 13105
        }
1193b4be Poul Sander 2025-02-03 21:28 13106
        if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
1193b4be Poul Sander 2025-02-03 21:28 13107
    }
1193b4be Poul Sander 2025-02-03 21:28 13108
#endif
1193b4be Poul Sander 2025-02-03 21:28 13109
1193b4be Poul Sander 2025-02-03 21:28 13110
    // Is it in the quadrant we're interested in moving to?
1193b4be Poul Sander 2025-02-03 21:28 13111
    bool new_best = false;
1193b4be Poul Sander 2025-02-03 21:28 13112
    if (quadrant == move_dir)
1193b4be Poul Sander 2025-02-03 21:28 13113
    {
1193b4be Poul Sander 2025-02-03 21:28 13114
        // Does it beat the current best candidate?
1193b4be Poul Sander 2025-02-03 21:28 13115
        if (dist_box < result->DistBox)
1193b4be Poul Sander 2025-02-03 21:28 13116
        {
1193b4be Poul Sander 2025-02-03 21:28 13117
            result->DistBox = dist_box;
1193b4be Poul Sander 2025-02-03 21:28 13118
            result->DistCenter = dist_center;
1193b4be Poul Sander 2025-02-03 21:28 13119
            return true;
1193b4be Poul Sander 2025-02-03 21:28 13120
        }
1193b4be Poul Sander 2025-02-03 21:28 13121
        if (dist_box == result->DistBox)
1193b4be Poul Sander 2025-02-03 21:28 13122
        {
1193b4be Poul Sander 2025-02-03 21:28 13123
            // Try using distance between center points to break ties
1193b4be Poul Sander 2025-02-03 21:28 13124
            if (dist_center < result->DistCenter)
1193b4be Poul Sander 2025-02-03 21:28 13125
            {
1193b4be Poul Sander 2025-02-03 21:28 13126
                result->DistCenter = dist_center;
1193b4be Poul Sander 2025-02-03 21:28 13127
                new_best = true;
1193b4be Poul Sander 2025-02-03 21:28 13128
            }
1193b4be Poul Sander 2025-02-03 21:28 13129
            else if (dist_center == result->DistCenter)
1193b4be Poul Sander 2025-02-03 21:28 13130
            {
1193b4be Poul Sander 2025-02-03 21:28 13131
                // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
1193b4be Poul Sander 2025-02-03 21:28 13132
                // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
1193b4be Poul Sander 2025-02-03 21:28 13133
                // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
1193b4be Poul Sander 2025-02-03 21:28 13134
                if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
1193b4be Poul Sander 2025-02-03 21:28 13135
                    new_best = true;
1193b4be Poul Sander 2025-02-03 21:28 13136
            }
1193b4be Poul Sander 2025-02-03 21:28 13137
        }
1193b4be Poul Sander 2025-02-03 21:28 13138
    }
1193b4be Poul Sander 2025-02-03 21:28 13139
1193b4be Poul Sander 2025-02-03 21:28 13140
    // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
1193b4be Poul Sander 2025-02-03 21:28 13141
    // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
1193b4be Poul Sander 2025-02-03 21:28 13142
    // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
1193b4be Poul Sander 2025-02-03 21:28 13143
    // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
1193b4be Poul Sander 2025-02-03 21:28 13144
    // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
1193b4be Poul Sander 2025-02-03 21:28 13145
    if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
1193b4be Poul Sander 2025-02-03 21:28 13146
        if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
1193b4be Poul Sander 2025-02-03 21:28 13147
            if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
1193b4be Poul Sander 2025-02-03 21:28 13148
            {
1193b4be Poul Sander 2025-02-03 21:28 13149
                result->DistAxial = dist_axial;
1193b4be Poul Sander 2025-02-03 21:28 13150
                new_best = true;
1193b4be Poul Sander 2025-02-03 21:28 13151
            }
1193b4be Poul Sander 2025-02-03 21:28 13152
1193b4be Poul Sander 2025-02-03 21:28 13153
    return new_best;
1193b4be Poul Sander 2025-02-03 21:28 13154
}
1193b4be Poul Sander 2025-02-03 21:28 13155
1193b4be Poul Sander 2025-02-03 21:28 13156
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
1193b4be Poul Sander 2025-02-03 21:28 13157
{
1193b4be Poul Sander 2025-02-03 21:28 13158
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13159
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 13160
    result->Window = window;
1193b4be Poul Sander 2025-02-03 21:28 13161
    result->ID = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 13162
    result->FocusScopeId = g.CurrentFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 13163
    result->ItemFlags = g.LastItemData.ItemFlags;
1193b4be Poul Sander 2025-02-03 21:28 13164
    result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
1193b4be Poul Sander 2025-02-03 21:28 13165
    if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
1193b4be Poul Sander 2025-02-03 21:28 13166
    {
1193b4be Poul Sander 2025-02-03 21:28 13167
        IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
1193b4be Poul Sander 2025-02-03 21:28 13168
        result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
1193b4be Poul Sander 2025-02-03 21:28 13169
    }
1193b4be Poul Sander 2025-02-03 21:28 13170
}
1193b4be Poul Sander 2025-02-03 21:28 13171
1193b4be Poul Sander 2025-02-03 21:28 13172
// True when current work location may be scrolled horizontally when moving left / right.
1193b4be Poul Sander 2025-02-03 21:28 13173
// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
1193b4be Poul Sander 2025-02-03 21:28 13174
void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
1193b4be Poul Sander 2025-02-03 21:28 13175
{
1193b4be Poul Sander 2025-02-03 21:28 13176
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13177
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 13178
    window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
1193b4be Poul Sander 2025-02-03 21:28 13179
}
1193b4be Poul Sander 2025-02-03 21:28 13180
1193b4be Poul Sander 2025-02-03 21:28 13181
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
1193b4be Poul Sander 2025-02-03 21:28 13182
// This is called after LastItemData is set, but NextItemData is also still valid.
1193b4be Poul Sander 2025-02-03 21:28 13183
static void ImGui::NavProcessItem()
1193b4be Poul Sander 2025-02-03 21:28 13184
{
1193b4be Poul Sander 2025-02-03 21:28 13185
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13186
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 13187
    const ImGuiID id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 13188
    const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
1193b4be Poul Sander 2025-02-03 21:28 13189
1193b4be Poul Sander 2025-02-03 21:28 13190
    // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
1193b4be Poul Sander 2025-02-03 21:28 13191
    if (window->DC.NavIsScrollPushableX == false)
1193b4be Poul Sander 2025-02-03 21:28 13192
    {
1193b4be Poul Sander 2025-02-03 21:28 13193
        g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 13194
        g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
1193b4be Poul Sander 2025-02-03 21:28 13195
    }
1193b4be Poul Sander 2025-02-03 21:28 13196
    const ImRect nav_bb = g.LastItemData.NavRect;
1193b4be Poul Sander 2025-02-03 21:28 13197
1193b4be Poul Sander 2025-02-03 21:28 13198
    // Process Init Request
1193b4be Poul Sander 2025-02-03 21:28 13199
    if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13200
    {
1193b4be Poul Sander 2025-02-03 21:28 13201
        // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
1193b4be Poul Sander 2025-02-03 21:28 13202
        const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
1193b4be Poul Sander 2025-02-03 21:28 13203
        if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
1193b4be Poul Sander 2025-02-03 21:28 13204
        {
1193b4be Poul Sander 2025-02-03 21:28 13205
            NavApplyItemToResult(&g.NavInitResult);
1193b4be Poul Sander 2025-02-03 21:28 13206
        }
1193b4be Poul Sander 2025-02-03 21:28 13207
        if (candidate_for_nav_default_focus)
1193b4be Poul Sander 2025-02-03 21:28 13208
        {
1193b4be Poul Sander 2025-02-03 21:28 13209
            g.NavInitRequest = false; // Found a match, clear request
1193b4be Poul Sander 2025-02-03 21:28 13210
            NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13211
        }
1193b4be Poul Sander 2025-02-03 21:28 13212
    }
1193b4be Poul Sander 2025-02-03 21:28 13213
1193b4be Poul Sander 2025-02-03 21:28 13214
    // Process Move Request (scoring for navigation)
1193b4be Poul Sander 2025-02-03 21:28 13215
    // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
1193b4be Poul Sander 2025-02-03 21:28 13216
    if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13217
    {
1193b4be Poul Sander 2025-02-03 21:28 13218
        if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13219
        {
1193b4be Poul Sander 2025-02-03 21:28 13220
            const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13221
            if (is_tabbing)
1193b4be Poul Sander 2025-02-03 21:28 13222
            {
1193b4be Poul Sander 2025-02-03 21:28 13223
                NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
1193b4be Poul Sander 2025-02-03 21:28 13224
            }
1193b4be Poul Sander 2025-02-03 21:28 13225
            else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
1193b4be Poul Sander 2025-02-03 21:28 13226
            {
1193b4be Poul Sander 2025-02-03 21:28 13227
                ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
1193b4be Poul Sander 2025-02-03 21:28 13228
                if (NavScoreItem(result))
1193b4be Poul Sander 2025-02-03 21:28 13229
                    NavApplyItemToResult(result);
1193b4be Poul Sander 2025-02-03 21:28 13230
1193b4be Poul Sander 2025-02-03 21:28 13231
                // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
1193b4be Poul Sander 2025-02-03 21:28 13232
                const float VISIBLE_RATIO = 0.70f;
1193b4be Poul Sander 2025-02-03 21:28 13233
                if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
1193b4be Poul Sander 2025-02-03 21:28 13234
                    if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
1193b4be Poul Sander 2025-02-03 21:28 13235
                        if (NavScoreItem(&g.NavMoveResultLocalVisible))
1193b4be Poul Sander 2025-02-03 21:28 13236
                            NavApplyItemToResult(&g.NavMoveResultLocalVisible);
1193b4be Poul Sander 2025-02-03 21:28 13237
            }
1193b4be Poul Sander 2025-02-03 21:28 13238
        }
1193b4be Poul Sander 2025-02-03 21:28 13239
    }
1193b4be Poul Sander 2025-02-03 21:28 13240
1193b4be Poul Sander 2025-02-03 21:28 13241
    // Update information for currently focused/navigated item
1193b4be Poul Sander 2025-02-03 21:28 13242
    if (g.NavId == id)
1193b4be Poul Sander 2025-02-03 21:28 13243
    {
1193b4be Poul Sander 2025-02-03 21:28 13244
        if (g.NavWindow != window)
1193b4be Poul Sander 2025-02-03 21:28 13245
            SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
1193b4be Poul Sander 2025-02-03 21:28 13246
        g.NavLayer = window->DC.NavLayerCurrent;
1193b4be Poul Sander 2025-02-03 21:28 13247
        SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
1193b4be Poul Sander 2025-02-03 21:28 13248
        g.NavFocusScopeId = g.CurrentFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 13249
        g.NavIdIsAlive = true;
1193b4be Poul Sander 2025-02-03 21:28 13250
        if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
1193b4be Poul Sander 2025-02-03 21:28 13251
        {
1193b4be Poul Sander 2025-02-03 21:28 13252
            IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
1193b4be Poul Sander 2025-02-03 21:28 13253
            g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
1193b4be Poul Sander 2025-02-03 21:28 13254
        }
1193b4be Poul Sander 2025-02-03 21:28 13255
        window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
1193b4be Poul Sander 2025-02-03 21:28 13256
    }
1193b4be Poul Sander 2025-02-03 21:28 13257
}
1193b4be Poul Sander 2025-02-03 21:28 13258
1193b4be Poul Sander 2025-02-03 21:28 13259
// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
1193b4be Poul Sander 2025-02-03 21:28 13260
// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
1193b4be Poul Sander 2025-02-03 21:28 13261
// - Case 1: no nav/active id:    set result to first eligible item, stop storing.
1193b4be Poul Sander 2025-02-03 21:28 13262
// - Case 2: tab forward:         on ref id set counter, on counter elapse store result
1193b4be Poul Sander 2025-02-03 21:28 13263
// - Case 3: tab forward wrap:    set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request
1193b4be Poul Sander 2025-02-03 21:28 13264
// - Case 4: tab backward:        store all results, on ref id pick prev, stop storing
1193b4be Poul Sander 2025-02-03 21:28 13265
// - Case 5: tab backward wrap:   store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested
1193b4be Poul Sander 2025-02-03 21:28 13266
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
1193b4be Poul Sander 2025-02-03 21:28 13267
{
1193b4be Poul Sander 2025-02-03 21:28 13268
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13269
1193b4be Poul Sander 2025-02-03 21:28 13270
    if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13271
    {
1193b4be Poul Sander 2025-02-03 21:28 13272
        if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
1193b4be Poul Sander 2025-02-03 21:28 13273
            return;
1193b4be Poul Sander 2025-02-03 21:28 13274
        if (g.NavFocusScopeId != g.CurrentFocusScopeId)
1193b4be Poul Sander 2025-02-03 21:28 13275
            return;
1193b4be Poul Sander 2025-02-03 21:28 13276
    }
1193b4be Poul Sander 2025-02-03 21:28 13277
1193b4be Poul Sander 2025-02-03 21:28 13278
    // - Can always land on an item when using API call.
1193b4be Poul Sander 2025-02-03 21:28 13279
    // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
1193b4be Poul Sander 2025-02-03 21:28 13280
    // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
1193b4be Poul Sander 2025-02-03 21:28 13281
    bool can_stop;
1193b4be Poul Sander 2025-02-03 21:28 13282
    if (move_flags & ImGuiNavMoveFlags_FocusApi)
1193b4be Poul Sander 2025-02-03 21:28 13283
        can_stop = true;
1193b4be Poul Sander 2025-02-03 21:28 13284
    else
1193b4be Poul Sander 2025-02-03 21:28 13285
        can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
1193b4be Poul Sander 2025-02-03 21:28 13286
1193b4be Poul Sander 2025-02-03 21:28 13287
    // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
1193b4be Poul Sander 2025-02-03 21:28 13288
    ImGuiNavItemData* result = &g.NavMoveResultLocal;
1193b4be Poul Sander 2025-02-03 21:28 13289
    if (g.NavTabbingDir == +1)
1193b4be Poul Sander 2025-02-03 21:28 13290
    {
1193b4be Poul Sander 2025-02-03 21:28 13291
        // Tab Forward or SetKeyboardFocusHere() with >= 0
1193b4be Poul Sander 2025-02-03 21:28 13292
        if (can_stop && g.NavTabbingResultFirst.ID == 0)
1193b4be Poul Sander 2025-02-03 21:28 13293
            NavApplyItemToResult(&g.NavTabbingResultFirst);
1193b4be Poul Sander 2025-02-03 21:28 13294
        if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
1193b4be Poul Sander 2025-02-03 21:28 13295
            NavMoveRequestResolveWithLastItem(result);
1193b4be Poul Sander 2025-02-03 21:28 13296
        else if (g.NavId == id)
1193b4be Poul Sander 2025-02-03 21:28 13297
            g.NavTabbingCounter = 1;
1193b4be Poul Sander 2025-02-03 21:28 13298
    }
1193b4be Poul Sander 2025-02-03 21:28 13299
    else if (g.NavTabbingDir == -1)
1193b4be Poul Sander 2025-02-03 21:28 13300
    {
1193b4be Poul Sander 2025-02-03 21:28 13301
        // Tab Backward
1193b4be Poul Sander 2025-02-03 21:28 13302
        if (g.NavId == id)
1193b4be Poul Sander 2025-02-03 21:28 13303
        {
1193b4be Poul Sander 2025-02-03 21:28 13304
            if (result->ID)
1193b4be Poul Sander 2025-02-03 21:28 13305
            {
1193b4be Poul Sander 2025-02-03 21:28 13306
                g.NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 13307
                NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13308
            }
1193b4be Poul Sander 2025-02-03 21:28 13309
        }
1193b4be Poul Sander 2025-02-03 21:28 13310
        else if (can_stop)
1193b4be Poul Sander 2025-02-03 21:28 13311
        {
1193b4be Poul Sander 2025-02-03 21:28 13312
            // Keep applying until reaching NavId
1193b4be Poul Sander 2025-02-03 21:28 13313
            NavApplyItemToResult(result);
1193b4be Poul Sander 2025-02-03 21:28 13314
        }
1193b4be Poul Sander 2025-02-03 21:28 13315
    }
1193b4be Poul Sander 2025-02-03 21:28 13316
    else if (g.NavTabbingDir == 0)
1193b4be Poul Sander 2025-02-03 21:28 13317
    {
1193b4be Poul Sander 2025-02-03 21:28 13318
        if (can_stop && g.NavId == id)
1193b4be Poul Sander 2025-02-03 21:28 13319
            NavMoveRequestResolveWithLastItem(result);
1193b4be Poul Sander 2025-02-03 21:28 13320
        if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
1193b4be Poul Sander 2025-02-03 21:28 13321
            NavApplyItemToResult(&g.NavTabbingResultFirst);
1193b4be Poul Sander 2025-02-03 21:28 13322
    }
1193b4be Poul Sander 2025-02-03 21:28 13323
}
1193b4be Poul Sander 2025-02-03 21:28 13324
1193b4be Poul Sander 2025-02-03 21:28 13325
bool ImGui::NavMoveRequestButNoResultYet()
1193b4be Poul Sander 2025-02-03 21:28 13326
{
1193b4be Poul Sander 2025-02-03 21:28 13327
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13328
    return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
1193b4be Poul Sander 2025-02-03 21:28 13329
}
1193b4be Poul Sander 2025-02-03 21:28 13330
1193b4be Poul Sander 2025-02-03 21:28 13331
// FIXME: ScoringRect is not set
1193b4be Poul Sander 2025-02-03 21:28 13332
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
1193b4be Poul Sander 2025-02-03 21:28 13333
{
1193b4be Poul Sander 2025-02-03 21:28 13334
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13335
    IM_ASSERT(g.NavWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 13336
    //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 13337
1193b4be Poul Sander 2025-02-03 21:28 13338
    if (move_flags & ImGuiNavMoveFlags_IsTabbing)
1193b4be Poul Sander 2025-02-03 21:28 13339
        move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
1193b4be Poul Sander 2025-02-03 21:28 13340
1193b4be Poul Sander 2025-02-03 21:28 13341
    g.NavMoveSubmitted = g.NavMoveScoringItems = true;
1193b4be Poul Sander 2025-02-03 21:28 13342
    g.NavMoveDir = move_dir;
1193b4be Poul Sander 2025-02-03 21:28 13343
    g.NavMoveDirForDebug = move_dir;
1193b4be Poul Sander 2025-02-03 21:28 13344
    g.NavMoveClipDir = clip_dir;
1193b4be Poul Sander 2025-02-03 21:28 13345
    g.NavMoveFlags = move_flags;
1193b4be Poul Sander 2025-02-03 21:28 13346
    g.NavMoveScrollFlags = scroll_flags;
1193b4be Poul Sander 2025-02-03 21:28 13347
    g.NavMoveForwardToNextFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 13348
    g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
1193b4be Poul Sander 2025-02-03 21:28 13349
    g.NavMoveResultLocal.Clear();
1193b4be Poul Sander 2025-02-03 21:28 13350
    g.NavMoveResultLocalVisible.Clear();
1193b4be Poul Sander 2025-02-03 21:28 13351
    g.NavMoveResultOther.Clear();
1193b4be Poul Sander 2025-02-03 21:28 13352
    g.NavTabbingCounter = 0;
1193b4be Poul Sander 2025-02-03 21:28 13353
    g.NavTabbingResultFirst.Clear();
1193b4be Poul Sander 2025-02-03 21:28 13354
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13355
}
1193b4be Poul Sander 2025-02-03 21:28 13356
1193b4be Poul Sander 2025-02-03 21:28 13357
void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
1193b4be Poul Sander 2025-02-03 21:28 13358
{
1193b4be Poul Sander 2025-02-03 21:28 13359
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13360
    g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
1193b4be Poul Sander 2025-02-03 21:28 13361
    NavApplyItemToResult(result);
1193b4be Poul Sander 2025-02-03 21:28 13362
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13363
}
1193b4be Poul Sander 2025-02-03 21:28 13364
1193b4be Poul Sander 2025-02-03 21:28 13365
// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
1193b4be Poul Sander 2025-02-03 21:28 13366
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data)
1193b4be Poul Sander 2025-02-03 21:28 13367
{
1193b4be Poul Sander 2025-02-03 21:28 13368
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13369
    g.NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 13370
    g.LastItemData.ID = tree_node_data->ID;
1193b4be Poul Sander 2025-02-03 21:28 13371
    g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
1193b4be Poul Sander 2025-02-03 21:28 13372
    g.LastItemData.NavRect = tree_node_data->NavRect;
1193b4be Poul Sander 2025-02-03 21:28 13373
    NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
1193b4be Poul Sander 2025-02-03 21:28 13374
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
1193b4be Poul Sander 2025-02-03 21:28 13375
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13376
}
1193b4be Poul Sander 2025-02-03 21:28 13377
1193b4be Poul Sander 2025-02-03 21:28 13378
void ImGui::NavMoveRequestCancel()
1193b4be Poul Sander 2025-02-03 21:28 13379
{
1193b4be Poul Sander 2025-02-03 21:28 13380
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13381
    g.NavMoveSubmitted = g.NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 13382
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13383
}
1193b4be Poul Sander 2025-02-03 21:28 13384
1193b4be Poul Sander 2025-02-03 21:28 13385
// Forward will reuse the move request again on the next frame (generally with modifications done to it)
1193b4be Poul Sander 2025-02-03 21:28 13386
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
1193b4be Poul Sander 2025-02-03 21:28 13387
{
1193b4be Poul Sander 2025-02-03 21:28 13388
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13389
    IM_ASSERT(g.NavMoveForwardToNextFrame == false);
1193b4be Poul Sander 2025-02-03 21:28 13390
    NavMoveRequestCancel();
1193b4be Poul Sander 2025-02-03 21:28 13391
    g.NavMoveForwardToNextFrame = true;
1193b4be Poul Sander 2025-02-03 21:28 13392
    g.NavMoveDir = move_dir;
1193b4be Poul Sander 2025-02-03 21:28 13393
    g.NavMoveClipDir = clip_dir;
1193b4be Poul Sander 2025-02-03 21:28 13394
    g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
1193b4be Poul Sander 2025-02-03 21:28 13395
    g.NavMoveScrollFlags = scroll_flags;
1193b4be Poul Sander 2025-02-03 21:28 13396
}
1193b4be Poul Sander 2025-02-03 21:28 13397
1193b4be Poul Sander 2025-02-03 21:28 13398
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
1193b4be Poul Sander 2025-02-03 21:28 13399
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
1193b4be Poul Sander 2025-02-03 21:28 13400
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
1193b4be Poul Sander 2025-02-03 21:28 13401
{
1193b4be Poul Sander 2025-02-03 21:28 13402
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13403
    IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
1193b4be Poul Sander 2025-02-03 21:28 13404
1193b4be Poul Sander 2025-02-03 21:28 13405
    // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
1193b4be Poul Sander 2025-02-03 21:28 13406
    // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
1193b4be Poul Sander 2025-02-03 21:28 13407
    if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 13408
        g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
1193b4be Poul Sander 2025-02-03 21:28 13409
}
1193b4be Poul Sander 2025-02-03 21:28 13410
1193b4be Poul Sander 2025-02-03 21:28 13411
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
1193b4be Poul Sander 2025-02-03 21:28 13412
// This way we could find the last focused window among our children. It would be much less confusing this way?
1193b4be Poul Sander 2025-02-03 21:28 13413
static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
1193b4be Poul Sander 2025-02-03 21:28 13414
{
1193b4be Poul Sander 2025-02-03 21:28 13415
    ImGuiWindow* parent = nav_window;
1193b4be Poul Sander 2025-02-03 21:28 13416
    while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13417
        parent = parent->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 13418
    if (parent && parent != nav_window)
1193b4be Poul Sander 2025-02-03 21:28 13419
        parent->NavLastChildNavWindow = nav_window;
1193b4be Poul Sander 2025-02-03 21:28 13420
}
1193b4be Poul Sander 2025-02-03 21:28 13421
1193b4be Poul Sander 2025-02-03 21:28 13422
// Restore the last focused child.
1193b4be Poul Sander 2025-02-03 21:28 13423
// Call when we are expected to land on the Main Layer (0) after FocusWindow()
1193b4be Poul Sander 2025-02-03 21:28 13424
static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 13425
{
1193b4be Poul Sander 2025-02-03 21:28 13426
    if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 13427
        return window->NavLastChildNavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13428
    if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 13429
        if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
1193b4be Poul Sander 2025-02-03 21:28 13430
            return tab->Window;
1193b4be Poul Sander 2025-02-03 21:28 13431
    return window;
1193b4be Poul Sander 2025-02-03 21:28 13432
}
1193b4be Poul Sander 2025-02-03 21:28 13433
1193b4be Poul Sander 2025-02-03 21:28 13434
void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
1193b4be Poul Sander 2025-02-03 21:28 13435
{
1193b4be Poul Sander 2025-02-03 21:28 13436
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13437
    if (layer == ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 13438
    {
1193b4be Poul Sander 2025-02-03 21:28 13439
        ImGuiWindow* prev_nav_window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13440
        g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);    // FIXME-NAV: Should clear ongoing nav requests?
1193b4be Poul Sander 2025-02-03 21:28 13441
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
1193b4be Poul Sander 2025-02-03 21:28 13442
        if (prev_nav_window)
1193b4be Poul Sander 2025-02-03 21:28 13443
            IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 13444
    }
1193b4be Poul Sander 2025-02-03 21:28 13445
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13446
    if (window->NavLastIds[layer] != 0)
1193b4be Poul Sander 2025-02-03 21:28 13447
    {
1193b4be Poul Sander 2025-02-03 21:28 13448
        SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
1193b4be Poul Sander 2025-02-03 21:28 13449
    }
1193b4be Poul Sander 2025-02-03 21:28 13450
    else
1193b4be Poul Sander 2025-02-03 21:28 13451
    {
1193b4be Poul Sander 2025-02-03 21:28 13452
        g.NavLayer = layer;
1193b4be Poul Sander 2025-02-03 21:28 13453
        NavInitWindow(window, true);
1193b4be Poul Sander 2025-02-03 21:28 13454
    }
1193b4be Poul Sander 2025-02-03 21:28 13455
}
1193b4be Poul Sander 2025-02-03 21:28 13456
1193b4be Poul Sander 2025-02-03 21:28 13457
static inline void ImGui::NavUpdateAnyRequestFlag()
1193b4be Poul Sander 2025-02-03 21:28 13458
{
1193b4be Poul Sander 2025-02-03 21:28 13459
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13460
    g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 13461
    if (g.NavAnyRequest)
1193b4be Poul Sander 2025-02-03 21:28 13462
        IM_ASSERT(g.NavWindow != NULL);
1193b4be Poul Sander 2025-02-03 21:28 13463
}
1193b4be Poul Sander 2025-02-03 21:28 13464
1193b4be Poul Sander 2025-02-03 21:28 13465
// This needs to be called before we submit any widget (aka in or before Begin)
1193b4be Poul Sander 2025-02-03 21:28 13466
void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
1193b4be Poul Sander 2025-02-03 21:28 13467
{
1193b4be Poul Sander 2025-02-03 21:28 13468
    // FIXME: ChildWindow test here is wrong for docking
1193b4be Poul Sander 2025-02-03 21:28 13469
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13470
    IM_ASSERT(window == g.NavWindow);
1193b4be Poul Sander 2025-02-03 21:28 13471
1193b4be Poul Sander 2025-02-03 21:28 13472
    if (window->Flags & ImGuiWindowFlags_NoNavInputs)
1193b4be Poul Sander 2025-02-03 21:28 13473
    {
1193b4be Poul Sander 2025-02-03 21:28 13474
        g.NavId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13475
        SetNavFocusScope(window->NavRootFocusScopeId);
1193b4be Poul Sander 2025-02-03 21:28 13476
        return;
1193b4be Poul Sander 2025-02-03 21:28 13477
    }
1193b4be Poul Sander 2025-02-03 21:28 13478
1193b4be Poul Sander 2025-02-03 21:28 13479
    bool init_for_nav = false;
1193b4be Poul Sander 2025-02-03 21:28 13480
    if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
1193b4be Poul Sander 2025-02-03 21:28 13481
        init_for_nav = true;
1193b4be Poul Sander 2025-02-03 21:28 13482
    IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
1193b4be Poul Sander 2025-02-03 21:28 13483
    if (init_for_nav)
1193b4be Poul Sander 2025-02-03 21:28 13484
    {
1193b4be Poul Sander 2025-02-03 21:28 13485
        SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
1193b4be Poul Sander 2025-02-03 21:28 13486
        g.NavInitRequest = true;
1193b4be Poul Sander 2025-02-03 21:28 13487
        g.NavInitRequestFromMove = false;
1193b4be Poul Sander 2025-02-03 21:28 13488
        g.NavInitResult.ID = 0;
1193b4be Poul Sander 2025-02-03 21:28 13489
        NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13490
    }
1193b4be Poul Sander 2025-02-03 21:28 13491
    else
1193b4be Poul Sander 2025-02-03 21:28 13492
    {
1193b4be Poul Sander 2025-02-03 21:28 13493
        g.NavId = window->NavLastIds[0];
1193b4be Poul Sander 2025-02-03 21:28 13494
        SetNavFocusScope(window->NavRootFocusScopeId);
1193b4be Poul Sander 2025-02-03 21:28 13495
    }
1193b4be Poul Sander 2025-02-03 21:28 13496
}
1193b4be Poul Sander 2025-02-03 21:28 13497
1193b4be Poul Sander 2025-02-03 21:28 13498
static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
1193b4be Poul Sander 2025-02-03 21:28 13499
{
1193b4be Poul Sander 2025-02-03 21:28 13500
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13501
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13502
    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 13503
1193b4be Poul Sander 2025-02-03 21:28 13504
    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
1193b4be Poul Sander 2025-02-03 21:28 13505
    if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
1193b4be Poul Sander 2025-02-03 21:28 13506
        return ImGuiInputSource_Mouse;
1193b4be Poul Sander 2025-02-03 21:28 13507
    else
1193b4be Poul Sander 2025-02-03 21:28 13508
        return ImGuiInputSource_Keyboard; // or Nav in general
1193b4be Poul Sander 2025-02-03 21:28 13509
}
1193b4be Poul Sander 2025-02-03 21:28 13510
1193b4be Poul Sander 2025-02-03 21:28 13511
static ImVec2 ImGui::NavCalcPreferredRefPos()
1193b4be Poul Sander 2025-02-03 21:28 13512
{
1193b4be Poul Sander 2025-02-03 21:28 13513
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13514
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13515
    ImGuiInputSource source = NavCalcPreferredRefPosSource();
1193b4be Poul Sander 2025-02-03 21:28 13516
1193b4be Poul Sander 2025-02-03 21:28 13517
    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 13518
1193b4be Poul Sander 2025-02-03 21:28 13519
    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
1193b4be Poul Sander 2025-02-03 21:28 13520
    if (source == ImGuiInputSource_Mouse)
1193b4be Poul Sander 2025-02-03 21:28 13521
    {
1193b4be Poul Sander 2025-02-03 21:28 13522
        // Mouse (we need a fallback in case the mouse becomes invalid after being used)
1193b4be Poul Sander 2025-02-03 21:28 13523
        // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
1193b4be Poul Sander 2025-02-03 21:28 13524
        // In theory we could move that +1.0f offset in OpenPopupEx()
1193b4be Poul Sander 2025-02-03 21:28 13525
        ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
1193b4be Poul Sander 2025-02-03 21:28 13526
        return ImVec2(p.x + 1.0f, p.y);
1193b4be Poul Sander 2025-02-03 21:28 13527
    }
1193b4be Poul Sander 2025-02-03 21:28 13528
    else
1193b4be Poul Sander 2025-02-03 21:28 13529
    {
1193b4be Poul Sander 2025-02-03 21:28 13530
        // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
1193b4be Poul Sander 2025-02-03 21:28 13531
        ImRect ref_rect;
1193b4be Poul Sander 2025-02-03 21:28 13532
        if (activated_shortcut)
1193b4be Poul Sander 2025-02-03 21:28 13533
            ref_rect = g.LastItemData.NavRect;
1193b4be Poul Sander 2025-02-03 21:28 13534
        else
1193b4be Poul Sander 2025-02-03 21:28 13535
            ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
1193b4be Poul Sander 2025-02-03 21:28 13536
1193b4be Poul Sander 2025-02-03 21:28 13537
        // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
1193b4be Poul Sander 2025-02-03 21:28 13538
        if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
1193b4be Poul Sander 2025-02-03 21:28 13539
        {
1193b4be Poul Sander 2025-02-03 21:28 13540
            ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
1193b4be Poul Sander 2025-02-03 21:28 13541
            ref_rect.Translate(window->Scroll - next_scroll);
1193b4be Poul Sander 2025-02-03 21:28 13542
        }
1193b4be Poul Sander 2025-02-03 21:28 13543
        ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));
1193b4be Poul Sander 2025-02-03 21:28 13544
        ImGuiViewport* viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 13545
        return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
1193b4be Poul Sander 2025-02-03 21:28 13546
    }
1193b4be Poul Sander 2025-02-03 21:28 13547
}
1193b4be Poul Sander 2025-02-03 21:28 13548
1193b4be Poul Sander 2025-02-03 21:28 13549
float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
1193b4be Poul Sander 2025-02-03 21:28 13550
{
1193b4be Poul Sander 2025-02-03 21:28 13551
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13552
    float repeat_delay, repeat_rate;
1193b4be Poul Sander 2025-02-03 21:28 13553
    GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 13554
1193b4be Poul Sander 2025-02-03 21:28 13555
    ImGuiKey key_less, key_more;
1193b4be Poul Sander 2025-02-03 21:28 13556
    if (g.NavInputSource == ImGuiInputSource_Gamepad)
1193b4be Poul Sander 2025-02-03 21:28 13557
    {
1193b4be Poul Sander 2025-02-03 21:28 13558
        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
1193b4be Poul Sander 2025-02-03 21:28 13559
        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
1193b4be Poul Sander 2025-02-03 21:28 13560
    }
1193b4be Poul Sander 2025-02-03 21:28 13561
    else
1193b4be Poul Sander 2025-02-03 21:28 13562
    {
1193b4be Poul Sander 2025-02-03 21:28 13563
        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
1193b4be Poul Sander 2025-02-03 21:28 13564
        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
1193b4be Poul Sander 2025-02-03 21:28 13565
    }
1193b4be Poul Sander 2025-02-03 21:28 13566
    float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
1193b4be Poul Sander 2025-02-03 21:28 13567
    if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
1193b4be Poul Sander 2025-02-03 21:28 13568
        amount = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 13569
    return amount;
1193b4be Poul Sander 2025-02-03 21:28 13570
}
1193b4be Poul Sander 2025-02-03 21:28 13571
1193b4be Poul Sander 2025-02-03 21:28 13572
static void ImGui::NavUpdate()
1193b4be Poul Sander 2025-02-03 21:28 13573
{
1193b4be Poul Sander 2025-02-03 21:28 13574
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13575
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 13576
1193b4be Poul Sander 2025-02-03 21:28 13577
    io.WantSetMousePos = false;
1193b4be Poul Sander 2025-02-03 21:28 13578
    //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
1193b4be Poul Sander 2025-02-03 21:28 13579
1193b4be Poul Sander 2025-02-03 21:28 13580
    // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
1193b4be Poul Sander 2025-02-03 21:28 13581
    // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
1193b4be Poul Sander 2025-02-03 21:28 13582
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13583
    const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
1193b4be Poul Sander 2025-02-03 21:28 13584
    if (nav_gamepad_active)
1193b4be Poul Sander 2025-02-03 21:28 13585
        for (ImGuiKey key : nav_gamepad_keys_to_change_source)
1193b4be Poul Sander 2025-02-03 21:28 13586
            if (IsKeyDown(key))
1193b4be Poul Sander 2025-02-03 21:28 13587
                g.NavInputSource = ImGuiInputSource_Gamepad;
1193b4be Poul Sander 2025-02-03 21:28 13588
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13589
    const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
1193b4be Poul Sander 2025-02-03 21:28 13590
    if (nav_keyboard_active)
1193b4be Poul Sander 2025-02-03 21:28 13591
        for (ImGuiKey key : nav_keyboard_keys_to_change_source)
1193b4be Poul Sander 2025-02-03 21:28 13592
            if (IsKeyDown(key))
1193b4be Poul Sander 2025-02-03 21:28 13593
                g.NavInputSource = ImGuiInputSource_Keyboard;
1193b4be Poul Sander 2025-02-03 21:28 13594
1193b4be Poul Sander 2025-02-03 21:28 13595
    // Process navigation init request (select first/default focus)
1193b4be Poul Sander 2025-02-03 21:28 13596
    g.NavJustMovedToId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13597
    g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13598
    if (g.NavInitResult.ID != 0)
1193b4be Poul Sander 2025-02-03 21:28 13599
        NavInitRequestApplyResult();
1193b4be Poul Sander 2025-02-03 21:28 13600
    g.NavInitRequest = false;
1193b4be Poul Sander 2025-02-03 21:28 13601
    g.NavInitRequestFromMove = false;
1193b4be Poul Sander 2025-02-03 21:28 13602
    g.NavInitResult.ID = 0;
1193b4be Poul Sander 2025-02-03 21:28 13603
1193b4be Poul Sander 2025-02-03 21:28 13604
    // Process navigation move request
1193b4be Poul Sander 2025-02-03 21:28 13605
    if (g.NavMoveSubmitted)
1193b4be Poul Sander 2025-02-03 21:28 13606
        NavMoveRequestApplyResult();
1193b4be Poul Sander 2025-02-03 21:28 13607
    g.NavTabbingCounter = 0;
1193b4be Poul Sander 2025-02-03 21:28 13608
    g.NavMoveSubmitted = g.NavMoveScoringItems = false;
1193b4be Poul Sander 2025-02-03 21:28 13609
    if (g.NavCursorHideFrames > 0)
1193b4be Poul Sander 2025-02-03 21:28 13610
        if (--g.NavCursorHideFrames == 0)
1193b4be Poul Sander 2025-02-03 21:28 13611
            g.NavCursorVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 13612
1193b4be Poul Sander 2025-02-03 21:28 13613
    // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
1193b4be Poul Sander 2025-02-03 21:28 13614
    bool set_mouse_pos = false;
1193b4be Poul Sander 2025-02-03 21:28 13615
    if (g.NavMousePosDirty && g.NavIdIsAlive)
1193b4be Poul Sander 2025-02-03 21:28 13616
        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13617
            set_mouse_pos = true;
1193b4be Poul Sander 2025-02-03 21:28 13618
    g.NavMousePosDirty = false;
1193b4be Poul Sander 2025-02-03 21:28 13619
    IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
1193b4be Poul Sander 2025-02-03 21:28 13620
1193b4be Poul Sander 2025-02-03 21:28 13621
    // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
1193b4be Poul Sander 2025-02-03 21:28 13622
    if (g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13623
        NavSaveLastChildNavWindowIntoParent(g.NavWindow);
1193b4be Poul Sander 2025-02-03 21:28 13624
    if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 13625
        g.NavWindow->NavLastChildNavWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 13626
1193b4be Poul Sander 2025-02-03 21:28 13627
    // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
1193b4be Poul Sander 2025-02-03 21:28 13628
    NavUpdateWindowing();
1193b4be Poul Sander 2025-02-03 21:28 13629
1193b4be Poul Sander 2025-02-03 21:28 13630
    // Set output flags for user application
1193b4be Poul Sander 2025-02-03 21:28 13631
    io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
1193b4be Poul Sander 2025-02-03 21:28 13632
    io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
1193b4be Poul Sander 2025-02-03 21:28 13633
1193b4be Poul Sander 2025-02-03 21:28 13634
    // Process NavCancel input (to close a popup, get back to parent, clear focus)
1193b4be Poul Sander 2025-02-03 21:28 13635
    NavUpdateCancelRequest();
1193b4be Poul Sander 2025-02-03 21:28 13636
1193b4be Poul Sander 2025-02-03 21:28 13637
    // Process manual activation request
1193b4be Poul Sander 2025-02-03 21:28 13638
    g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13639
    g.NavActivateFlags = ImGuiActivateFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 13640
    if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 13641
    {
1193b4be Poul Sander 2025-02-03 21:28 13642
        const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
1193b4be Poul Sander 2025-02-03 21:28 13643
        const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
1193b4be Poul Sander 2025-02-03 21:28 13644
        const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner));
1193b4be Poul Sander 2025-02-03 21:28 13645
        const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner)));
1193b4be Poul Sander 2025-02-03 21:28 13646
        if (g.ActiveId == 0 && activate_pressed)
1193b4be Poul Sander 2025-02-03 21:28 13647
        {
1193b4be Poul Sander 2025-02-03 21:28 13648
            g.NavActivateId = g.NavId;
1193b4be Poul Sander 2025-02-03 21:28 13649
            g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
1193b4be Poul Sander 2025-02-03 21:28 13650
        }
1193b4be Poul Sander 2025-02-03 21:28 13651
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
1193b4be Poul Sander 2025-02-03 21:28 13652
        {
1193b4be Poul Sander 2025-02-03 21:28 13653
            g.NavActivateId = g.NavId;
1193b4be Poul Sander 2025-02-03 21:28 13654
            g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
1193b4be Poul Sander 2025-02-03 21:28 13655
        }
1193b4be Poul Sander 2025-02-03 21:28 13656
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
1193b4be Poul Sander 2025-02-03 21:28 13657
            g.NavActivateDownId = g.NavId;
1193b4be Poul Sander 2025-02-03 21:28 13658
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
1193b4be Poul Sander 2025-02-03 21:28 13659
        {
1193b4be Poul Sander 2025-02-03 21:28 13660
            g.NavActivatePressedId = g.NavId;
1193b4be Poul Sander 2025-02-03 21:28 13661
            NavHighlightActivated(g.NavId);
1193b4be Poul Sander 2025-02-03 21:28 13662
        }
1193b4be Poul Sander 2025-02-03 21:28 13663
    }
1193b4be Poul Sander 2025-02-03 21:28 13664
    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 13665
        g.NavCursorVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 13666
    else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
1193b4be Poul Sander 2025-02-03 21:28 13667
        g.NavCursorVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 13668
    if (g.NavActivateId != 0)
1193b4be Poul Sander 2025-02-03 21:28 13669
        IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
1193b4be Poul Sander 2025-02-03 21:28 13670
1193b4be Poul Sander 2025-02-03 21:28 13671
    // Highlight
1193b4be Poul Sander 2025-02-03 21:28 13672
    if (g.NavHighlightActivatedTimer > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13673
        g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
1193b4be Poul Sander 2025-02-03 21:28 13674
    if (g.NavHighlightActivatedTimer == 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13675
        g.NavHighlightActivatedId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13676
1193b4be Poul Sander 2025-02-03 21:28 13677
    // Process programmatic activation request
1193b4be Poul Sander 2025-02-03 21:28 13678
    // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
1193b4be Poul Sander 2025-02-03 21:28 13679
    if (g.NavNextActivateId != 0)
1193b4be Poul Sander 2025-02-03 21:28 13680
    {
1193b4be Poul Sander 2025-02-03 21:28 13681
        g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
1193b4be Poul Sander 2025-02-03 21:28 13682
        g.NavActivateFlags = g.NavNextActivateFlags;
1193b4be Poul Sander 2025-02-03 21:28 13683
    }
1193b4be Poul Sander 2025-02-03 21:28 13684
    g.NavNextActivateId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13685
1193b4be Poul Sander 2025-02-03 21:28 13686
    // Process move requests
1193b4be Poul Sander 2025-02-03 21:28 13687
    NavUpdateCreateMoveRequest();
1193b4be Poul Sander 2025-02-03 21:28 13688
    if (g.NavMoveDir == ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 13689
        NavUpdateCreateTabbingRequest();
1193b4be Poul Sander 2025-02-03 21:28 13690
    NavUpdateAnyRequestFlag();
1193b4be Poul Sander 2025-02-03 21:28 13691
    g.NavIdIsAlive = false;
1193b4be Poul Sander 2025-02-03 21:28 13692
1193b4be Poul Sander 2025-02-03 21:28 13693
    // Scrolling
1193b4be Poul Sander 2025-02-03 21:28 13694
    if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
1193b4be Poul Sander 2025-02-03 21:28 13695
    {
1193b4be Poul Sander 2025-02-03 21:28 13696
        // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
1193b4be Poul Sander 2025-02-03 21:28 13697
        ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13698
        const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
1193b4be Poul Sander 2025-02-03 21:28 13699
        const ImGuiDir move_dir = g.NavMoveDir;
1193b4be Poul Sander 2025-02-03 21:28 13700
        if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 13701
        {
1193b4be Poul Sander 2025-02-03 21:28 13702
            if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
1193b4be Poul Sander 2025-02-03 21:28 13703
                SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
1193b4be Poul Sander 2025-02-03 21:28 13704
            if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
1193b4be Poul Sander 2025-02-03 21:28 13705
                SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
1193b4be Poul Sander 2025-02-03 21:28 13706
        }
1193b4be Poul Sander 2025-02-03 21:28 13707
1193b4be Poul Sander 2025-02-03 21:28 13708
        // *Normal* Manual scroll with LStick
1193b4be Poul Sander 2025-02-03 21:28 13709
        // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
1193b4be Poul Sander 2025-02-03 21:28 13710
        if (nav_gamepad_active)
1193b4be Poul Sander 2025-02-03 21:28 13711
        {
1193b4be Poul Sander 2025-02-03 21:28 13712
            const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
1193b4be Poul Sander 2025-02-03 21:28 13713
            const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 13714
            if (scroll_dir.x != 0.0f && window->ScrollbarX)
1193b4be Poul Sander 2025-02-03 21:28 13715
                SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
1193b4be Poul Sander 2025-02-03 21:28 13716
            if (scroll_dir.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13717
                SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
1193b4be Poul Sander 2025-02-03 21:28 13718
        }
1193b4be Poul Sander 2025-02-03 21:28 13719
    }
1193b4be Poul Sander 2025-02-03 21:28 13720
1193b4be Poul Sander 2025-02-03 21:28 13721
    // Always prioritize mouse highlight if navigation is disabled
1193b4be Poul Sander 2025-02-03 21:28 13722
    if (!nav_keyboard_active && !nav_gamepad_active)
1193b4be Poul Sander 2025-02-03 21:28 13723
    {
1193b4be Poul Sander 2025-02-03 21:28 13724
        g.NavCursorVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 13725
        g.NavHighlightItemUnderNav = set_mouse_pos = false;
1193b4be Poul Sander 2025-02-03 21:28 13726
    }
1193b4be Poul Sander 2025-02-03 21:28 13727
1193b4be Poul Sander 2025-02-03 21:28 13728
    // Update mouse position if requested
1193b4be Poul Sander 2025-02-03 21:28 13729
    // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
1193b4be Poul Sander 2025-02-03 21:28 13730
    if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
1193b4be Poul Sander 2025-02-03 21:28 13731
        TeleportMousePos(NavCalcPreferredRefPos());
1193b4be Poul Sander 2025-02-03 21:28 13732
1193b4be Poul Sander 2025-02-03 21:28 13733
    // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 13734
    g.NavScoringDebugCount = 0;
1193b4be Poul Sander 2025-02-03 21:28 13735
#if IMGUI_DEBUG_NAV_RECTS
1193b4be Poul Sander 2025-02-03 21:28 13736
    if (ImGuiWindow* debug_window = g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13737
    {
1193b4be Poul Sander 2025-02-03 21:28 13738
        ImDrawList* draw_list = GetForegroundDrawList(debug_window);
1193b4be Poul Sander 2025-02-03 21:28 13739
        int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }
1193b4be Poul Sander 2025-02-03 21:28 13740
        //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
1193b4be Poul Sander 2025-02-03 21:28 13741
    }
1193b4be Poul Sander 2025-02-03 21:28 13742
#endif
1193b4be Poul Sander 2025-02-03 21:28 13743
}
1193b4be Poul Sander 2025-02-03 21:28 13744
1193b4be Poul Sander 2025-02-03 21:28 13745
void ImGui::NavInitRequestApplyResult()
1193b4be Poul Sander 2025-02-03 21:28 13746
{
1193b4be Poul Sander 2025-02-03 21:28 13747
    // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
1193b4be Poul Sander 2025-02-03 21:28 13748
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13749
    if (!g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13750
        return;
1193b4be Poul Sander 2025-02-03 21:28 13751
1193b4be Poul Sander 2025-02-03 21:28 13752
    ImGuiNavItemData* result = &g.NavInitResult;
1193b4be Poul Sander 2025-02-03 21:28 13753
    if (g.NavId != result->ID)
1193b4be Poul Sander 2025-02-03 21:28 13754
    {
1193b4be Poul Sander 2025-02-03 21:28 13755
        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 13756
        g.NavJustMovedToId = result->ID;
1193b4be Poul Sander 2025-02-03 21:28 13757
        g.NavJustMovedToFocusScopeId = result->FocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 13758
        g.NavJustMovedToKeyMods = 0;
1193b4be Poul Sander 2025-02-03 21:28 13759
        g.NavJustMovedToIsTabbing = false;
1193b4be Poul Sander 2025-02-03 21:28 13760
        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13761
    }
1193b4be Poul Sander 2025-02-03 21:28 13762
1193b4be Poul Sander 2025-02-03 21:28 13763
    // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
1193b4be Poul Sander 2025-02-03 21:28 13764
    // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
1193b4be Poul Sander 2025-02-03 21:28 13765
    IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 13766
    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
1193b4be Poul Sander 2025-02-03 21:28 13767
    g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
1193b4be Poul Sander 2025-02-03 21:28 13768
    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
1193b4be Poul Sander 2025-02-03 21:28 13769
        g.NavLastValidSelectionUserData = result->SelectionUserData;
1193b4be Poul Sander 2025-02-03 21:28 13770
    if (g.NavInitRequestFromMove)
1193b4be Poul Sander 2025-02-03 21:28 13771
        SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 13772
}
1193b4be Poul Sander 2025-02-03 21:28 13773
1193b4be Poul Sander 2025-02-03 21:28 13774
// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
1193b4be Poul Sander 2025-02-03 21:28 13775
static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
1193b4be Poul Sander 2025-02-03 21:28 13776
{
1193b4be Poul Sander 2025-02-03 21:28 13777
    // Bias initial rect
1193b4be Poul Sander 2025-02-03 21:28 13778
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13779
    const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
1193b4be Poul Sander 2025-02-03 21:28 13780
1193b4be Poul Sander 2025-02-03 21:28 13781
    // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
1193b4be Poul Sander 2025-02-03 21:28 13782
    // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
1193b4be Poul Sander 2025-02-03 21:28 13783
    // - But each successful move sets new bias on one axis, only cleared when using mouse.
1193b4be Poul Sander 2025-02-03 21:28 13784
    if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13785
    {
1193b4be Poul Sander 2025-02-03 21:28 13786
        if (preferred_pos_rel.x == FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 13787
            preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
1193b4be Poul Sander 2025-02-03 21:28 13788
        if (preferred_pos_rel.y == FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 13789
            preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
1193b4be Poul Sander 2025-02-03 21:28 13790
    }
1193b4be Poul Sander 2025-02-03 21:28 13791
1193b4be Poul Sander 2025-02-03 21:28 13792
    // Apply general bias on the other axis
1193b4be Poul Sander 2025-02-03 21:28 13793
    if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 13794
        r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
1193b4be Poul Sander 2025-02-03 21:28 13795
    else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 13796
        r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
1193b4be Poul Sander 2025-02-03 21:28 13797
}
1193b4be Poul Sander 2025-02-03 21:28 13798
1193b4be Poul Sander 2025-02-03 21:28 13799
void ImGui::NavUpdateCreateMoveRequest()
1193b4be Poul Sander 2025-02-03 21:28 13800
{
1193b4be Poul Sander 2025-02-03 21:28 13801
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13802
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 13803
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13804
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13805
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13806
1193b4be Poul Sander 2025-02-03 21:28 13807
    if (g.NavMoveForwardToNextFrame && window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 13808
    {
1193b4be Poul Sander 2025-02-03 21:28 13809
        // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
1193b4be Poul Sander 2025-02-03 21:28 13810
        // (preserve most state, which were already set by the NavMoveRequestForward() function)
1193b4be Poul Sander 2025-02-03 21:28 13811
        IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 13812
        IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
1193b4be Poul Sander 2025-02-03 21:28 13813
        IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
1193b4be Poul Sander 2025-02-03 21:28 13814
    }
1193b4be Poul Sander 2025-02-03 21:28 13815
    else
1193b4be Poul Sander 2025-02-03 21:28 13816
    {
1193b4be Poul Sander 2025-02-03 21:28 13817
        // Initiate directional inputs request
1193b4be Poul Sander 2025-02-03 21:28 13818
        g.NavMoveDir = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 13819
        g.NavMoveFlags = ImGuiNavMoveFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 13820
        g.NavMoveScrollFlags = ImGuiScrollFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 13821
        if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 13822
        {
1193b4be Poul Sander 2025-02-03 21:28 13823
            const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
1193b4be Poul Sander 2025-02-03 21:28 13824
            if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
1193b4be Poul Sander 2025-02-03 21:28 13825
            if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
1193b4be Poul Sander 2025-02-03 21:28 13826
            if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp,    repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow,    repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
1193b4be Poul Sander 2025-02-03 21:28 13827
            if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
1193b4be Poul Sander 2025-02-03 21:28 13828
        }
1193b4be Poul Sander 2025-02-03 21:28 13829
        g.NavMoveClipDir = g.NavMoveDir;
1193b4be Poul Sander 2025-02-03 21:28 13830
        g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 13831
    }
1193b4be Poul Sander 2025-02-03 21:28 13832
1193b4be Poul Sander 2025-02-03 21:28 13833
    // Update PageUp/PageDown/Home/End scroll
1193b4be Poul Sander 2025-02-03 21:28 13834
    // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
1193b4be Poul Sander 2025-02-03 21:28 13835
    float scoring_rect_offset_y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 13836
    if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
1193b4be Poul Sander 2025-02-03 21:28 13837
        scoring_rect_offset_y = NavUpdatePageUpPageDown();
1193b4be Poul Sander 2025-02-03 21:28 13838
    if (scoring_rect_offset_y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 13839
    {
1193b4be Poul Sander 2025-02-03 21:28 13840
        g.NavScoringNoClipRect = window->InnerRect;
1193b4be Poul Sander 2025-02-03 21:28 13841
        g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
1193b4be Poul Sander 2025-02-03 21:28 13842
    }
1193b4be Poul Sander 2025-02-03 21:28 13843
1193b4be Poul Sander 2025-02-03 21:28 13844
    // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
1193b4be Poul Sander 2025-02-03 21:28 13845
#if IMGUI_DEBUG_NAV_SCORING
1193b4be Poul Sander 2025-02-03 21:28 13846
    //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
1193b4be Poul Sander 2025-02-03 21:28 13847
    //    g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
1193b4be Poul Sander 2025-02-03 21:28 13848
    if (io.KeyCtrl)
1193b4be Poul Sander 2025-02-03 21:28 13849
    {
1193b4be Poul Sander 2025-02-03 21:28 13850
        if (g.NavMoveDir == ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 13851
            g.NavMoveDir = g.NavMoveDirForDebug;
1193b4be Poul Sander 2025-02-03 21:28 13852
        g.NavMoveClipDir = g.NavMoveDir;
1193b4be Poul Sander 2025-02-03 21:28 13853
        g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
1193b4be Poul Sander 2025-02-03 21:28 13854
    }
1193b4be Poul Sander 2025-02-03 21:28 13855
#endif
1193b4be Poul Sander 2025-02-03 21:28 13856
1193b4be Poul Sander 2025-02-03 21:28 13857
    // Submit
1193b4be Poul Sander 2025-02-03 21:28 13858
    g.NavMoveForwardToNextFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 13859
    if (g.NavMoveDir != ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 13860
        NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
1193b4be Poul Sander 2025-02-03 21:28 13861
1193b4be Poul Sander 2025-02-03 21:28 13862
    // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
1193b4be Poul Sander 2025-02-03 21:28 13863
    if (g.NavMoveSubmitted && g.NavId == 0)
1193b4be Poul Sander 2025-02-03 21:28 13864
    {
1193b4be Poul Sander 2025-02-03 21:28 13865
        IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
1193b4be Poul Sander 2025-02-03 21:28 13866
        g.NavInitRequest = g.NavInitRequestFromMove = true;
1193b4be Poul Sander 2025-02-03 21:28 13867
        g.NavInitResult.ID = 0;
1193b4be Poul Sander 2025-02-03 21:28 13868
        if (g.IO.ConfigNavCursorVisibleAuto)
1193b4be Poul Sander 2025-02-03 21:28 13869
            g.NavCursorVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 13870
    }
1193b4be Poul Sander 2025-02-03 21:28 13871
1193b4be Poul Sander 2025-02-03 21:28 13872
    // When using gamepad, we project the reference nav bounding box into window visible area.
1193b4be Poul Sander 2025-02-03 21:28 13873
    // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
1193b4be Poul Sander 2025-02-03 21:28 13874
    // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
1193b4be Poul Sander 2025-02-03 21:28 13875
    if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
1193b4be Poul Sander 2025-02-03 21:28 13876
    {
1193b4be Poul Sander 2025-02-03 21:28 13877
        bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
1193b4be Poul Sander 2025-02-03 21:28 13878
        bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
1193b4be Poul Sander 2025-02-03 21:28 13879
        ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
1193b4be Poul Sander 2025-02-03 21:28 13880
1193b4be Poul Sander 2025-02-03 21:28 13881
        // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
1193b4be Poul Sander 2025-02-03 21:28 13882
        // Otherwise 'inner_rect_rel' would be off on the move result frame.
1193b4be Poul Sander 2025-02-03 21:28 13883
        inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
1193b4be Poul Sander 2025-02-03 21:28 13884
1193b4be Poul Sander 2025-02-03 21:28 13885
        if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
1193b4be Poul Sander 2025-02-03 21:28 13886
        {
1193b4be Poul Sander 2025-02-03 21:28 13887
            IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
1193b4be Poul Sander 2025-02-03 21:28 13888
            float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 13889
            float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
1193b4be Poul Sander 2025-02-03 21:28 13890
            inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 13891
            inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 13892
            inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 13893
            inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 13894
            window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
1193b4be Poul Sander 2025-02-03 21:28 13895
            g.NavId = 0;
1193b4be Poul Sander 2025-02-03 21:28 13896
        }
1193b4be Poul Sander 2025-02-03 21:28 13897
    }
1193b4be Poul Sander 2025-02-03 21:28 13898
1193b4be Poul Sander 2025-02-03 21:28 13899
    // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
1193b4be Poul Sander 2025-02-03 21:28 13900
    ImRect scoring_rect;
1193b4be Poul Sander 2025-02-03 21:28 13901
    if (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 13902
    {
1193b4be Poul Sander 2025-02-03 21:28 13903
        ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
1193b4be Poul Sander 2025-02-03 21:28 13904
        scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
1193b4be Poul Sander 2025-02-03 21:28 13905
        scoring_rect.TranslateY(scoring_rect_offset_y);
1193b4be Poul Sander 2025-02-03 21:28 13906
        if (g.NavMoveSubmitted)
1193b4be Poul Sander 2025-02-03 21:28 13907
            NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
1193b4be Poul Sander 2025-02-03 21:28 13908
        IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
1193b4be Poul Sander 2025-02-03 21:28 13909
        //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 13910
        //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
1193b4be Poul Sander 2025-02-03 21:28 13911
    }
1193b4be Poul Sander 2025-02-03 21:28 13912
    g.NavScoringRect = scoring_rect;
1193b4be Poul Sander 2025-02-03 21:28 13913
    g.NavScoringNoClipRect.Add(scoring_rect);
1193b4be Poul Sander 2025-02-03 21:28 13914
}
1193b4be Poul Sander 2025-02-03 21:28 13915
1193b4be Poul Sander 2025-02-03 21:28 13916
void ImGui::NavUpdateCreateTabbingRequest()
1193b4be Poul Sander 2025-02-03 21:28 13917
{
1193b4be Poul Sander 2025-02-03 21:28 13918
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13919
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 13920
    IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 13921
    if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 13922
        return;
1193b4be Poul Sander 2025-02-03 21:28 13923
1193b4be Poul Sander 2025-02-03 21:28 13924
    const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
1193b4be Poul Sander 2025-02-03 21:28 13925
    if (!tab_pressed)
1193b4be Poul Sander 2025-02-03 21:28 13926
        return;
1193b4be Poul Sander 2025-02-03 21:28 13927
1193b4be Poul Sander 2025-02-03 21:28 13928
    // Initiate tabbing request
1193b4be Poul Sander 2025-02-03 21:28 13929
    // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
1193b4be Poul Sander 2025-02-03 21:28 13930
    // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
1193b4be Poul Sander 2025-02-03 21:28 13931
    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
1193b4be Poul Sander 2025-02-03 21:28 13932
    if (nav_keyboard_active)
1193b4be Poul Sander 2025-02-03 21:28 13933
        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
1193b4be Poul Sander 2025-02-03 21:28 13934
    else
1193b4be Poul Sander 2025-02-03 21:28 13935
        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
1193b4be Poul Sander 2025-02-03 21:28 13936
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
1193b4be Poul Sander 2025-02-03 21:28 13937
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 13938
    ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
1193b4be Poul Sander 2025-02-03 21:28 13939
    NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
1193b4be Poul Sander 2025-02-03 21:28 13940
    g.NavTabbingCounter = -1;
1193b4be Poul Sander 2025-02-03 21:28 13941
}
1193b4be Poul Sander 2025-02-03 21:28 13942
1193b4be Poul Sander 2025-02-03 21:28 13943
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
1193b4be Poul Sander 2025-02-03 21:28 13944
void ImGui::NavMoveRequestApplyResult()
1193b4be Poul Sander 2025-02-03 21:28 13945
{
1193b4be Poul Sander 2025-02-03 21:28 13946
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 13947
#if IMGUI_DEBUG_NAV_SCORING
1193b4be Poul Sander 2025-02-03 21:28 13948
    if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
1193b4be Poul Sander 2025-02-03 21:28 13949
        return;
1193b4be Poul Sander 2025-02-03 21:28 13950
#endif
1193b4be Poul Sander 2025-02-03 21:28 13951
1193b4be Poul Sander 2025-02-03 21:28 13952
    // Select which result to use
1193b4be Poul Sander 2025-02-03 21:28 13953
    ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
1193b4be Poul Sander 2025-02-03 21:28 13954
1193b4be Poul Sander 2025-02-03 21:28 13955
    // Tabbing forward wrap
1193b4be Poul Sander 2025-02-03 21:28 13956
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
1193b4be Poul Sander 2025-02-03 21:28 13957
        if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
1193b4be Poul Sander 2025-02-03 21:28 13958
            result = &g.NavTabbingResultFirst;
1193b4be Poul Sander 2025-02-03 21:28 13959
1193b4be Poul Sander 2025-02-03 21:28 13960
    // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
1193b4be Poul Sander 2025-02-03 21:28 13961
    const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
1193b4be Poul Sander 2025-02-03 21:28 13962
    if (result == NULL)
1193b4be Poul Sander 2025-02-03 21:28 13963
    {
1193b4be Poul Sander 2025-02-03 21:28 13964
        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
1193b4be Poul Sander 2025-02-03 21:28 13965
            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
1193b4be Poul Sander 2025-02-03 21:28 13966
        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
1193b4be Poul Sander 2025-02-03 21:28 13967
            SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 13968
        NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
1193b4be Poul Sander 2025-02-03 21:28 13969
        IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
1193b4be Poul Sander 2025-02-03 21:28 13970
        return;
1193b4be Poul Sander 2025-02-03 21:28 13971
    }
1193b4be Poul Sander 2025-02-03 21:28 13972
1193b4be Poul Sander 2025-02-03 21:28 13973
    // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
1193b4be Poul Sander 2025-02-03 21:28 13974
    if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
1193b4be Poul Sander 2025-02-03 21:28 13975
        if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
1193b4be Poul Sander 2025-02-03 21:28 13976
            result = &g.NavMoveResultLocalVisible;
1193b4be Poul Sander 2025-02-03 21:28 13977
1193b4be Poul Sander 2025-02-03 21:28 13978
    // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
1193b4be Poul Sander 2025-02-03 21:28 13979
    if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 13980
        if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
1193b4be Poul Sander 2025-02-03 21:28 13981
            result = &g.NavMoveResultOther;
1193b4be Poul Sander 2025-02-03 21:28 13982
    IM_ASSERT(g.NavWindow && result->Window);
1193b4be Poul Sander 2025-02-03 21:28 13983
1193b4be Poul Sander 2025-02-03 21:28 13984
    // Scroll to keep newly navigated item fully into view.
1193b4be Poul Sander 2025-02-03 21:28 13985
    if (g.NavLayer == ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 13986
    {
1193b4be Poul Sander 2025-02-03 21:28 13987
        ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
1193b4be Poul Sander 2025-02-03 21:28 13988
        ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
1193b4be Poul Sander 2025-02-03 21:28 13989
1193b4be Poul Sander 2025-02-03 21:28 13990
        if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
1193b4be Poul Sander 2025-02-03 21:28 13991
        {
1193b4be Poul Sander 2025-02-03 21:28 13992
            // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
1193b4be Poul Sander 2025-02-03 21:28 13993
            float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 13994
            SetScrollY(result->Window, scroll_target);
1193b4be Poul Sander 2025-02-03 21:28 13995
        }
1193b4be Poul Sander 2025-02-03 21:28 13996
    }
1193b4be Poul Sander 2025-02-03 21:28 13997
1193b4be Poul Sander 2025-02-03 21:28 13998
    if (g.NavWindow != result->Window)
1193b4be Poul Sander 2025-02-03 21:28 13999
    {
1193b4be Poul Sander 2025-02-03 21:28 14000
        IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
1193b4be Poul Sander 2025-02-03 21:28 14001
        g.NavWindow = result->Window;
1193b4be Poul Sander 2025-02-03 21:28 14002
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
1193b4be Poul Sander 2025-02-03 21:28 14003
    }
1193b4be Poul Sander 2025-02-03 21:28 14004
1193b4be Poul Sander 2025-02-03 21:28 14005
    // Clear active id unless requested not to
1193b4be Poul Sander 2025-02-03 21:28 14006
    // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
1193b4be Poul Sander 2025-02-03 21:28 14007
    // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
1193b4be Poul Sander 2025-02-03 21:28 14008
    if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14009
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 14010
1193b4be Poul Sander 2025-02-03 21:28 14011
    // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
1193b4be Poul Sander 2025-02-03 21:28 14012
    // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
1193b4be Poul Sander 2025-02-03 21:28 14013
    if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14014
    {
1193b4be Poul Sander 2025-02-03 21:28 14015
        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 14016
        g.NavJustMovedToId = result->ID;
1193b4be Poul Sander 2025-02-03 21:28 14017
        g.NavJustMovedToFocusScopeId = result->FocusScopeId;
1193b4be Poul Sander 2025-02-03 21:28 14018
        g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
1193b4be Poul Sander 2025-02-03 21:28 14019
        g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14020
        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14021
        //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
1193b4be Poul Sander 2025-02-03 21:28 14022
    }
1193b4be Poul Sander 2025-02-03 21:28 14023
1193b4be Poul Sander 2025-02-03 21:28 14024
    // Apply new NavID/Focus
1193b4be Poul Sander 2025-02-03 21:28 14025
    IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 14026
    ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
1193b4be Poul Sander 2025-02-03 21:28 14027
    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
1193b4be Poul Sander 2025-02-03 21:28 14028
    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
1193b4be Poul Sander 2025-02-03 21:28 14029
        g.NavLastValidSelectionUserData = result->SelectionUserData;
1193b4be Poul Sander 2025-02-03 21:28 14030
1193b4be Poul Sander 2025-02-03 21:28 14031
    // Restore last preferred position for current axis
1193b4be Poul Sander 2025-02-03 21:28 14032
    // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
1193b4be Poul Sander 2025-02-03 21:28 14033
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14034
    {
1193b4be Poul Sander 2025-02-03 21:28 14035
        preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
1193b4be Poul Sander 2025-02-03 21:28 14036
        g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
1193b4be Poul Sander 2025-02-03 21:28 14037
    }
1193b4be Poul Sander 2025-02-03 21:28 14038
1193b4be Poul Sander 2025-02-03 21:28 14039
    // Tabbing: Activates Inputable, otherwise only Focus
1193b4be Poul Sander 2025-02-03 21:28 14040
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14041
        g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
1193b4be Poul Sander 2025-02-03 21:28 14042
1193b4be Poul Sander 2025-02-03 21:28 14043
    // Activate
1193b4be Poul Sander 2025-02-03 21:28 14044
    if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
1193b4be Poul Sander 2025-02-03 21:28 14045
    {
1193b4be Poul Sander 2025-02-03 21:28 14046
        g.NavNextActivateId = result->ID;
1193b4be Poul Sander 2025-02-03 21:28 14047
        g.NavNextActivateFlags = ImGuiActivateFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 14048
        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
1193b4be Poul Sander 2025-02-03 21:28 14049
            g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
1193b4be Poul Sander 2025-02-03 21:28 14050
    }
1193b4be Poul Sander 2025-02-03 21:28 14051
1193b4be Poul Sander 2025-02-03 21:28 14052
    // Make nav cursor visible
1193b4be Poul Sander 2025-02-03 21:28 14053
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14054
        SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 14055
}
1193b4be Poul Sander 2025-02-03 21:28 14056
1193b4be Poul Sander 2025-02-03 21:28 14057
// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
1193b4be Poul Sander 2025-02-03 21:28 14058
// FIXME: In order to support e.g. Escape to clear a selection we'll need:
1193b4be Poul Sander 2025-02-03 21:28 14059
// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
1193b4be Poul Sander 2025-02-03 21:28 14060
// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
1193b4be Poul Sander 2025-02-03 21:28 14061
static void ImGui::NavUpdateCancelRequest()
1193b4be Poul Sander 2025-02-03 21:28 14062
{
1193b4be Poul Sander 2025-02-03 21:28 14063
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14064
    const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14065
    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14066
    if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
1193b4be Poul Sander 2025-02-03 21:28 14067
        return;
1193b4be Poul Sander 2025-02-03 21:28 14068
1193b4be Poul Sander 2025-02-03 21:28 14069
    IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
1193b4be Poul Sander 2025-02-03 21:28 14070
    if (g.ActiveId != 0)
1193b4be Poul Sander 2025-02-03 21:28 14071
    {
1193b4be Poul Sander 2025-02-03 21:28 14072
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 14073
    }
1193b4be Poul Sander 2025-02-03 21:28 14074
    else if (g.NavLayer != ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 14075
    {
1193b4be Poul Sander 2025-02-03 21:28 14076
        // Leave the "menu" layer
1193b4be Poul Sander 2025-02-03 21:28 14077
        NavRestoreLayer(ImGuiNavLayer_Main);
1193b4be Poul Sander 2025-02-03 21:28 14078
        SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 14079
    }
1193b4be Poul Sander 2025-02-03 21:28 14080
    else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 14081
    {
1193b4be Poul Sander 2025-02-03 21:28 14082
        // Exit child window
1193b4be Poul Sander 2025-02-03 21:28 14083
        ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
1193b4be Poul Sander 2025-02-03 21:28 14084
        ImGuiWindow* parent_window = child_window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14085
        IM_ASSERT(child_window->ChildId != 0);
1193b4be Poul Sander 2025-02-03 21:28 14086
        FocusWindow(parent_window);
1193b4be Poul Sander 2025-02-03 21:28 14087
        SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
1193b4be Poul Sander 2025-02-03 21:28 14088
        SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 14089
    }
1193b4be Poul Sander 2025-02-03 21:28 14090
    else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
1193b4be Poul Sander 2025-02-03 21:28 14091
    {
1193b4be Poul Sander 2025-02-03 21:28 14092
        // Close open popup/menu
1193b4be Poul Sander 2025-02-03 21:28 14093
        ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
1193b4be Poul Sander 2025-02-03 21:28 14094
    }
1193b4be Poul Sander 2025-02-03 21:28 14095
    else
1193b4be Poul Sander 2025-02-03 21:28 14096
    {
1193b4be Poul Sander 2025-02-03 21:28 14097
        // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
1193b4be Poul Sander 2025-02-03 21:28 14098
        // FIXME-NAV: This should happen on window appearing.
1193b4be Poul Sander 2025-02-03 21:28 14099
        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
1193b4be Poul Sander 2025-02-03 21:28 14100
            if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
1193b4be Poul Sander 2025-02-03 21:28 14101
                g.NavWindow->NavLastIds[0] = 0;
1193b4be Poul Sander 2025-02-03 21:28 14102
1193b4be Poul Sander 2025-02-03 21:28 14103
        // Clear nav focus
1193b4be Poul Sander 2025-02-03 21:28 14104
        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
1193b4be Poul Sander 2025-02-03 21:28 14105
            g.NavId = 0;
1193b4be Poul Sander 2025-02-03 21:28 14106
        if (g.IO.ConfigNavEscapeClearFocusWindow)
1193b4be Poul Sander 2025-02-03 21:28 14107
            FocusWindow(NULL);
1193b4be Poul Sander 2025-02-03 21:28 14108
    }
1193b4be Poul Sander 2025-02-03 21:28 14109
}
1193b4be Poul Sander 2025-02-03 21:28 14110
1193b4be Poul Sander 2025-02-03 21:28 14111
// Handle PageUp/PageDown/Home/End keys
1193b4be Poul Sander 2025-02-03 21:28 14112
// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
1193b4be Poul Sander 2025-02-03 21:28 14113
// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
1193b4be Poul Sander 2025-02-03 21:28 14114
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
1193b4be Poul Sander 2025-02-03 21:28 14115
static float ImGui::NavUpdatePageUpPageDown()
1193b4be Poul Sander 2025-02-03 21:28 14116
{
1193b4be Poul Sander 2025-02-03 21:28 14117
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14118
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 14119
    if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
1193b4be Poul Sander 2025-02-03 21:28 14120
        return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14121
1193b4be Poul Sander 2025-02-03 21:28 14122
    const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
1193b4be Poul Sander 2025-02-03 21:28 14123
    const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
1193b4be Poul Sander 2025-02-03 21:28 14124
    const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
1193b4be Poul Sander 2025-02-03 21:28 14125
    const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
1193b4be Poul Sander 2025-02-03 21:28 14126
    if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
1193b4be Poul Sander 2025-02-03 21:28 14127
        return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14128
1193b4be Poul Sander 2025-02-03 21:28 14129
    if (g.NavLayer != ImGuiNavLayer_Main)
1193b4be Poul Sander 2025-02-03 21:28 14130
        NavRestoreLayer(ImGuiNavLayer_Main);
1193b4be Poul Sander 2025-02-03 21:28 14131
1193b4be Poul Sander 2025-02-03 21:28 14132
    if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
1193b4be Poul Sander 2025-02-03 21:28 14133
    {
1193b4be Poul Sander 2025-02-03 21:28 14134
        // Fallback manual-scroll when window has no navigable item
1193b4be Poul Sander 2025-02-03 21:28 14135
        if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
1193b4be Poul Sander 2025-02-03 21:28 14136
            SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
1193b4be Poul Sander 2025-02-03 21:28 14137
        else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
1193b4be Poul Sander 2025-02-03 21:28 14138
            SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
1193b4be Poul Sander 2025-02-03 21:28 14139
        else if (home_pressed)
1193b4be Poul Sander 2025-02-03 21:28 14140
            SetScrollY(window, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 14141
        else if (end_pressed)
1193b4be Poul Sander 2025-02-03 21:28 14142
            SetScrollY(window, window->ScrollMax.y);
1193b4be Poul Sander 2025-02-03 21:28 14143
    }
1193b4be Poul Sander 2025-02-03 21:28 14144
    else
1193b4be Poul Sander 2025-02-03 21:28 14145
    {
1193b4be Poul Sander 2025-02-03 21:28 14146
        ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
1193b4be Poul Sander 2025-02-03 21:28 14147
        const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
1193b4be Poul Sander 2025-02-03 21:28 14148
        float nav_scoring_rect_offset_y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14149
        if (IsKeyPressed(ImGuiKey_PageUp, true))
1193b4be Poul Sander 2025-02-03 21:28 14150
        {
1193b4be Poul Sander 2025-02-03 21:28 14151
            nav_scoring_rect_offset_y = -page_offset_y;
1193b4be Poul Sander 2025-02-03 21:28 14152
            g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
1193b4be Poul Sander 2025-02-03 21:28 14153
            g.NavMoveClipDir = ImGuiDir_Up;
1193b4be Poul Sander 2025-02-03 21:28 14154
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
1193b4be Poul Sander 2025-02-03 21:28 14155
        }
1193b4be Poul Sander 2025-02-03 21:28 14156
        else if (IsKeyPressed(ImGuiKey_PageDown, true))
1193b4be Poul Sander 2025-02-03 21:28 14157
        {
1193b4be Poul Sander 2025-02-03 21:28 14158
            nav_scoring_rect_offset_y = +page_offset_y;
1193b4be Poul Sander 2025-02-03 21:28 14159
            g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
1193b4be Poul Sander 2025-02-03 21:28 14160
            g.NavMoveClipDir = ImGuiDir_Down;
1193b4be Poul Sander 2025-02-03 21:28 14161
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
1193b4be Poul Sander 2025-02-03 21:28 14162
        }
1193b4be Poul Sander 2025-02-03 21:28 14163
        else if (home_pressed)
1193b4be Poul Sander 2025-02-03 21:28 14164
        {
1193b4be Poul Sander 2025-02-03 21:28 14165
            // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
1193b4be Poul Sander 2025-02-03 21:28 14166
            // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
1193b4be Poul Sander 2025-02-03 21:28 14167
            // Preserve current horizontal position if we have any.
1193b4be Poul Sander 2025-02-03 21:28 14168
            nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14169
            if (nav_rect_rel.IsInverted())
1193b4be Poul Sander 2025-02-03 21:28 14170
                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14171
            g.NavMoveDir = ImGuiDir_Down;
1193b4be Poul Sander 2025-02-03 21:28 14172
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 14173
            // FIXME-NAV: MoveClipDir left to _None, intentional?
1193b4be Poul Sander 2025-02-03 21:28 14174
        }
1193b4be Poul Sander 2025-02-03 21:28 14175
        else if (end_pressed)
1193b4be Poul Sander 2025-02-03 21:28 14176
        {
1193b4be Poul Sander 2025-02-03 21:28 14177
            nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
1193b4be Poul Sander 2025-02-03 21:28 14178
            if (nav_rect_rel.IsInverted())
1193b4be Poul Sander 2025-02-03 21:28 14179
                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14180
            g.NavMoveDir = ImGuiDir_Up;
1193b4be Poul Sander 2025-02-03 21:28 14181
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
1193b4be Poul Sander 2025-02-03 21:28 14182
            // FIXME-NAV: MoveClipDir left to _None, intentional?
1193b4be Poul Sander 2025-02-03 21:28 14183
        }
1193b4be Poul Sander 2025-02-03 21:28 14184
        return nav_scoring_rect_offset_y;
1193b4be Poul Sander 2025-02-03 21:28 14185
    }
1193b4be Poul Sander 2025-02-03 21:28 14186
    return 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14187
}
1193b4be Poul Sander 2025-02-03 21:28 14188
1193b4be Poul Sander 2025-02-03 21:28 14189
static void ImGui::NavEndFrame()
1193b4be Poul Sander 2025-02-03 21:28 14190
{
1193b4be Poul Sander 2025-02-03 21:28 14191
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14192
1193b4be Poul Sander 2025-02-03 21:28 14193
    // Show CTRL+TAB list window
1193b4be Poul Sander 2025-02-03 21:28 14194
    if (g.NavWindowingTarget != NULL)
1193b4be Poul Sander 2025-02-03 21:28 14195
        NavUpdateWindowingOverlay();
1193b4be Poul Sander 2025-02-03 21:28 14196
1193b4be Poul Sander 2025-02-03 21:28 14197
    // Perform wrap-around in menus
1193b4be Poul Sander 2025-02-03 21:28 14198
    // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.
1193b4be Poul Sander 2025-02-03 21:28 14199
    // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
1193b4be Poul Sander 2025-02-03 21:28 14200
    if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14201
        NavUpdateCreateWrappingRequest();
1193b4be Poul Sander 2025-02-03 21:28 14202
}
1193b4be Poul Sander 2025-02-03 21:28 14203
1193b4be Poul Sander 2025-02-03 21:28 14204
static void ImGui::NavUpdateCreateWrappingRequest()
1193b4be Poul Sander 2025-02-03 21:28 14205
{
1193b4be Poul Sander 2025-02-03 21:28 14206
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14207
    ImGuiWindow* window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 14208
1193b4be Poul Sander 2025-02-03 21:28 14209
    bool do_forward = false;
1193b4be Poul Sander 2025-02-03 21:28 14210
    ImRect bb_rel = window->NavRectRel[g.NavLayer];
1193b4be Poul Sander 2025-02-03 21:28 14211
    ImGuiDir clip_dir = g.NavMoveDir;
1193b4be Poul Sander 2025-02-03 21:28 14212
1193b4be Poul Sander 2025-02-03 21:28 14213
    const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
1193b4be Poul Sander 2025-02-03 21:28 14214
    //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
1193b4be Poul Sander 2025-02-03 21:28 14215
    if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
1193b4be Poul Sander 2025-02-03 21:28 14216
    {
1193b4be Poul Sander 2025-02-03 21:28 14217
        bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
1193b4be Poul Sander 2025-02-03 21:28 14218
        if (move_flags & ImGuiNavMoveFlags_WrapX)
1193b4be Poul Sander 2025-02-03 21:28 14219
        {
1193b4be Poul Sander 2025-02-03 21:28 14220
            bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
1193b4be Poul Sander 2025-02-03 21:28 14221
            clip_dir = ImGuiDir_Up;
1193b4be Poul Sander 2025-02-03 21:28 14222
        }
1193b4be Poul Sander 2025-02-03 21:28 14223
        do_forward = true;
1193b4be Poul Sander 2025-02-03 21:28 14224
    }
1193b4be Poul Sander 2025-02-03 21:28 14225
    if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
1193b4be Poul Sander 2025-02-03 21:28 14226
    {
1193b4be Poul Sander 2025-02-03 21:28 14227
        bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
1193b4be Poul Sander 2025-02-03 21:28 14228
        if (move_flags & ImGuiNavMoveFlags_WrapX)
1193b4be Poul Sander 2025-02-03 21:28 14229
        {
1193b4be Poul Sander 2025-02-03 21:28 14230
            bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
1193b4be Poul Sander 2025-02-03 21:28 14231
            clip_dir = ImGuiDir_Down;
1193b4be Poul Sander 2025-02-03 21:28 14232
        }
1193b4be Poul Sander 2025-02-03 21:28 14233
        do_forward = true;
1193b4be Poul Sander 2025-02-03 21:28 14234
    }
1193b4be Poul Sander 2025-02-03 21:28 14235
    if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
1193b4be Poul Sander 2025-02-03 21:28 14236
    {
1193b4be Poul Sander 2025-02-03 21:28 14237
        bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
1193b4be Poul Sander 2025-02-03 21:28 14238
        if (move_flags & ImGuiNavMoveFlags_WrapY)
1193b4be Poul Sander 2025-02-03 21:28 14239
        {
1193b4be Poul Sander 2025-02-03 21:28 14240
            bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
1193b4be Poul Sander 2025-02-03 21:28 14241
            clip_dir = ImGuiDir_Left;
1193b4be Poul Sander 2025-02-03 21:28 14242
        }
1193b4be Poul Sander 2025-02-03 21:28 14243
        do_forward = true;
1193b4be Poul Sander 2025-02-03 21:28 14244
    }
1193b4be Poul Sander 2025-02-03 21:28 14245
    if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
1193b4be Poul Sander 2025-02-03 21:28 14246
    {
1193b4be Poul Sander 2025-02-03 21:28 14247
        bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
1193b4be Poul Sander 2025-02-03 21:28 14248
        if (move_flags & ImGuiNavMoveFlags_WrapY)
1193b4be Poul Sander 2025-02-03 21:28 14249
        {
1193b4be Poul Sander 2025-02-03 21:28 14250
            bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
1193b4be Poul Sander 2025-02-03 21:28 14251
            clip_dir = ImGuiDir_Right;
1193b4be Poul Sander 2025-02-03 21:28 14252
        }
1193b4be Poul Sander 2025-02-03 21:28 14253
        do_forward = true;
1193b4be Poul Sander 2025-02-03 21:28 14254
    }
1193b4be Poul Sander 2025-02-03 21:28 14255
    if (!do_forward)
1193b4be Poul Sander 2025-02-03 21:28 14256
        return;
1193b4be Poul Sander 2025-02-03 21:28 14257
    window->NavRectRel[g.NavLayer] = bb_rel;
1193b4be Poul Sander 2025-02-03 21:28 14258
    NavClearPreferredPosForAxis(ImGuiAxis_X);
1193b4be Poul Sander 2025-02-03 21:28 14259
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
1193b4be Poul Sander 2025-02-03 21:28 14260
    NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
1193b4be Poul Sander 2025-02-03 21:28 14261
}
1193b4be Poul Sander 2025-02-03 21:28 14262
1193b4be Poul Sander 2025-02-03 21:28 14263
static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 14264
{
1193b4be Poul Sander 2025-02-03 21:28 14265
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14266
    IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 14267
    int order = window->FocusOrder;
1193b4be Poul Sander 2025-02-03 21:28 14268
    IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
1193b4be Poul Sander 2025-02-03 21:28 14269
    IM_ASSERT(g.WindowsFocusOrder[order] == window);
1193b4be Poul Sander 2025-02-03 21:28 14270
    return order;
1193b4be Poul Sander 2025-02-03 21:28 14271
}
1193b4be Poul Sander 2025-02-03 21:28 14272
1193b4be Poul Sander 2025-02-03 21:28 14273
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
1193b4be Poul Sander 2025-02-03 21:28 14274
{
1193b4be Poul Sander 2025-02-03 21:28 14275
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14276
    for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
1193b4be Poul Sander 2025-02-03 21:28 14277
        if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
1193b4be Poul Sander 2025-02-03 21:28 14278
            return g.WindowsFocusOrder[i];
1193b4be Poul Sander 2025-02-03 21:28 14279
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 14280
}
1193b4be Poul Sander 2025-02-03 21:28 14281
1193b4be Poul Sander 2025-02-03 21:28 14282
static void NavUpdateWindowingTarget(int focus_change_dir)
1193b4be Poul Sander 2025-02-03 21:28 14283
{
1193b4be Poul Sander 2025-02-03 21:28 14284
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14285
    IM_ASSERT(g.NavWindowingTarget);
1193b4be Poul Sander 2025-02-03 21:28 14286
    if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
1193b4be Poul Sander 2025-02-03 21:28 14287
        return;
1193b4be Poul Sander 2025-02-03 21:28 14288
1193b4be Poul Sander 2025-02-03 21:28 14289
    const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
1193b4be Poul Sander 2025-02-03 21:28 14290
    ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
1193b4be Poul Sander 2025-02-03 21:28 14291
    if (!window_target)
1193b4be Poul Sander 2025-02-03 21:28 14292
        window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
1193b4be Poul Sander 2025-02-03 21:28 14293
    if (window_target) // Don't reset windowing target if there's a single window in the list
1193b4be Poul Sander 2025-02-03 21:28 14294
    {
1193b4be Poul Sander 2025-02-03 21:28 14295
        g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
1193b4be Poul Sander 2025-02-03 21:28 14296
        g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 14297
    }
1193b4be Poul Sander 2025-02-03 21:28 14298
    g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 14299
}
1193b4be Poul Sander 2025-02-03 21:28 14300
1193b4be Poul Sander 2025-02-03 21:28 14301
// Windowing management mode
1193b4be Poul Sander 2025-02-03 21:28 14302
// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
1193b4be Poul Sander 2025-02-03 21:28 14303
// Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
1193b4be Poul Sander 2025-02-03 21:28 14304
static void ImGui::NavUpdateWindowing()
1193b4be Poul Sander 2025-02-03 21:28 14305
{
1193b4be Poul Sander 2025-02-03 21:28 14306
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14307
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 14308
1193b4be Poul Sander 2025-02-03 21:28 14309
    ImGuiWindow* apply_focus_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14310
    bool apply_toggle_layer = false;
1193b4be Poul Sander 2025-02-03 21:28 14311
1193b4be Poul Sander 2025-02-03 21:28 14312
    ImGuiWindow* modal_window = GetTopMostPopupModal();
1193b4be Poul Sander 2025-02-03 21:28 14313
    bool allow_windowing = (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal.
1193b4be Poul Sander 2025-02-03 21:28 14314
    if (!allow_windowing)
1193b4be Poul Sander 2025-02-03 21:28 14315
        g.NavWindowingTarget = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14316
1193b4be Poul Sander 2025-02-03 21:28 14317
    // Fade out
1193b4be Poul Sander 2025-02-03 21:28 14318
    if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
1193b4be Poul Sander 2025-02-03 21:28 14319
    {
1193b4be Poul Sander 2025-02-03 21:28 14320
        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 14321
        if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 14322
            g.NavWindowingTargetAnim = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14323
    }
1193b4be Poul Sander 2025-02-03 21:28 14324
1193b4be Poul Sander 2025-02-03 21:28 14325
    // Start CTRL+Tab or Square+L/R window selection
1193b4be Poul Sander 2025-02-03 21:28 14326
    // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
1193b4be Poul Sander 2025-02-03 21:28 14327
    const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing");
1193b4be Poul Sander 2025-02-03 21:28 14328
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14329
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
1193b4be Poul Sander 2025-02-03 21:28 14330
    const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 14331
    const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 14332
    const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 14333
    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
1193b4be Poul Sander 2025-02-03 21:28 14334
    bool just_started_windowing_from_null_focus = false;
1193b4be Poul Sander 2025-02-03 21:28 14335
    if (start_windowing_with_gamepad || start_windowing_with_keyboard)
1193b4be Poul Sander 2025-02-03 21:28 14336
        if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
1193b4be Poul Sander 2025-02-03 21:28 14337
        {
1193b4be Poul Sander 2025-02-03 21:28 14338
            g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
1193b4be Poul Sander 2025-02-03 21:28 14339
            g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 14340
            g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 14341
            g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
1193b4be Poul Sander 2025-02-03 21:28 14342
            g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
1193b4be Poul Sander 2025-02-03 21:28 14343
            if (g.NavWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 14344
                just_started_windowing_from_null_focus = true;
1193b4be Poul Sander 2025-02-03 21:28 14345
1193b4be Poul Sander 2025-02-03 21:28 14346
            // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
1193b4be Poul Sander 2025-02-03 21:28 14347
            if (keyboard_next_window || keyboard_prev_window)
1193b4be Poul Sander 2025-02-03 21:28 14348
                SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
1193b4be Poul Sander 2025-02-03 21:28 14349
        }
1193b4be Poul Sander 2025-02-03 21:28 14350
1193b4be Poul Sander 2025-02-03 21:28 14351
    // Gamepad update
1193b4be Poul Sander 2025-02-03 21:28 14352
    g.NavWindowingTimer += io.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 14353
    if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
1193b4be Poul Sander 2025-02-03 21:28 14354
    {
1193b4be Poul Sander 2025-02-03 21:28 14355
        // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
1193b4be Poul Sander 2025-02-03 21:28 14356
        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
1193b4be Poul Sander 2025-02-03 21:28 14357
1193b4be Poul Sander 2025-02-03 21:28 14358
        // Select window to focus
1193b4be Poul Sander 2025-02-03 21:28 14359
        const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
1193b4be Poul Sander 2025-02-03 21:28 14360
        if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
1193b4be Poul Sander 2025-02-03 21:28 14361
        {
1193b4be Poul Sander 2025-02-03 21:28 14362
            NavUpdateWindowingTarget(focus_change_dir);
1193b4be Poul Sander 2025-02-03 21:28 14363
            g.NavWindowingHighlightAlpha = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 14364
        }
1193b4be Poul Sander 2025-02-03 21:28 14365
1193b4be Poul Sander 2025-02-03 21:28 14366
        // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
1193b4be Poul Sander 2025-02-03 21:28 14367
        if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
1193b4be Poul Sander 2025-02-03 21:28 14368
        {
1193b4be Poul Sander 2025-02-03 21:28 14369
            g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
1193b4be Poul Sander 2025-02-03 21:28 14370
            if (g.NavWindowingToggleLayer && g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 14371
                apply_toggle_layer = true;
1193b4be Poul Sander 2025-02-03 21:28 14372
            else if (!g.NavWindowingToggleLayer)
1193b4be Poul Sander 2025-02-03 21:28 14373
                apply_focus_window = g.NavWindowingTarget;
1193b4be Poul Sander 2025-02-03 21:28 14374
            g.NavWindowingTarget = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14375
        }
1193b4be Poul Sander 2025-02-03 21:28 14376
    }
1193b4be Poul Sander 2025-02-03 21:28 14377
1193b4be Poul Sander 2025-02-03 21:28 14378
    // Keyboard: Focus
1193b4be Poul Sander 2025-02-03 21:28 14379
    if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
1193b4be Poul Sander 2025-02-03 21:28 14380
    {
1193b4be Poul Sander 2025-02-03 21:28 14381
        // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
1193b4be Poul Sander 2025-02-03 21:28 14382
        ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
1193b4be Poul Sander 2025-02-03 21:28 14383
        IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
1193b4be Poul Sander 2025-02-03 21:28 14384
        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
1193b4be Poul Sander 2025-02-03 21:28 14385
        if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
1193b4be Poul Sander 2025-02-03 21:28 14386
            NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
1193b4be Poul Sander 2025-02-03 21:28 14387
        else if ((io.KeyMods & shared_mods) != shared_mods)
1193b4be Poul Sander 2025-02-03 21:28 14388
            apply_focus_window = g.NavWindowingTarget;
1193b4be Poul Sander 2025-02-03 21:28 14389
    }
1193b4be Poul Sander 2025-02-03 21:28 14390
1193b4be Poul Sander 2025-02-03 21:28 14391
    // Keyboard: Press and Release ALT to toggle menu layer
1193b4be Poul Sander 2025-02-03 21:28 14392
    const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
1193b4be Poul Sander 2025-02-03 21:28 14393
    bool windowing_toggle_layer_start = false;
1193b4be Poul Sander 2025-02-03 21:28 14394
    for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
1193b4be Poul Sander 2025-02-03 21:28 14395
        if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
1193b4be Poul Sander 2025-02-03 21:28 14396
        {
1193b4be Poul Sander 2025-02-03 21:28 14397
            windowing_toggle_layer_start = true;
1193b4be Poul Sander 2025-02-03 21:28 14398
            g.NavWindowingToggleLayer = true;
1193b4be Poul Sander 2025-02-03 21:28 14399
            g.NavWindowingToggleKey = windowing_toggle_key;
1193b4be Poul Sander 2025-02-03 21:28 14400
            g.NavInputSource = ImGuiInputSource_Keyboard;
1193b4be Poul Sander 2025-02-03 21:28 14401
            break;
1193b4be Poul Sander 2025-02-03 21:28 14402
        }
1193b4be Poul Sander 2025-02-03 21:28 14403
    if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
1193b4be Poul Sander 2025-02-03 21:28 14404
    {
1193b4be Poul Sander 2025-02-03 21:28 14405
        // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
1193b4be Poul Sander 2025-02-03 21:28 14406
        // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
1193b4be Poul Sander 2025-02-03 21:28 14407
        // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
1193b4be Poul Sander 2025-02-03 21:28 14408
        // We cancel toggling nav layer if an owner has claimed the key.
1193b4be Poul Sander 2025-02-03 21:28 14409
        if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
1193b4be Poul Sander 2025-02-03 21:28 14410
            g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 14411
        else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
1193b4be Poul Sander 2025-02-03 21:28 14412
            g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 14413
        else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
1193b4be Poul Sander 2025-02-03 21:28 14414
            g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 14415
1193b4be Poul Sander 2025-02-03 21:28 14416
        // Apply layer toggle on Alt release
1193b4be Poul Sander 2025-02-03 21:28 14417
        // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
1193b4be Poul Sander 2025-02-03 21:28 14418
        if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
1193b4be Poul Sander 2025-02-03 21:28 14419
            if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
1193b4be Poul Sander 2025-02-03 21:28 14420
                if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
1193b4be Poul Sander 2025-02-03 21:28 14421
                    apply_toggle_layer = true;
1193b4be Poul Sander 2025-02-03 21:28 14422
        if (!IsKeyDown(g.NavWindowingToggleKey))
1193b4be Poul Sander 2025-02-03 21:28 14423
            g.NavWindowingToggleLayer = false;
1193b4be Poul Sander 2025-02-03 21:28 14424
    }
1193b4be Poul Sander 2025-02-03 21:28 14425
1193b4be Poul Sander 2025-02-03 21:28 14426
    // Move window
1193b4be Poul Sander 2025-02-03 21:28 14427
    if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
1193b4be Poul Sander 2025-02-03 21:28 14428
    {
1193b4be Poul Sander 2025-02-03 21:28 14429
        ImVec2 nav_move_dir;
1193b4be Poul Sander 2025-02-03 21:28 14430
        if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
1193b4be Poul Sander 2025-02-03 21:28 14431
            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
1193b4be Poul Sander 2025-02-03 21:28 14432
        if (g.NavInputSource == ImGuiInputSource_Gamepad)
1193b4be Poul Sander 2025-02-03 21:28 14433
            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
1193b4be Poul Sander 2025-02-03 21:28 14434
        if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 14435
        {
1193b4be Poul Sander 2025-02-03 21:28 14436
            const float NAV_MOVE_SPEED = 800.0f;
1193b4be Poul Sander 2025-02-03 21:28 14437
            const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
1193b4be Poul Sander 2025-02-03 21:28 14438
            g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
1193b4be Poul Sander 2025-02-03 21:28 14439
            g.NavHighlightItemUnderNav = true;
1193b4be Poul Sander 2025-02-03 21:28 14440
            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
1193b4be Poul Sander 2025-02-03 21:28 14441
            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 14442
            {
1193b4be Poul Sander 2025-02-03 21:28 14443
                ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;
1193b4be Poul Sander 2025-02-03 21:28 14444
                SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
1193b4be Poul Sander 2025-02-03 21:28 14445
                g.NavWindowingAccumDeltaPos -= accum_floored;
1193b4be Poul Sander 2025-02-03 21:28 14446
            }
1193b4be Poul Sander 2025-02-03 21:28 14447
        }
1193b4be Poul Sander 2025-02-03 21:28 14448
    }
1193b4be Poul Sander 2025-02-03 21:28 14449
1193b4be Poul Sander 2025-02-03 21:28 14450
    // Apply final focus
1193b4be Poul Sander 2025-02-03 21:28 14451
    if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
1193b4be Poul Sander 2025-02-03 21:28 14452
    {
1193b4be Poul Sander 2025-02-03 21:28 14453
        // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow()
1193b4be Poul Sander 2025-02-03 21:28 14454
        // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()
1193b4be Poul Sander 2025-02-03 21:28 14455
        ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
1193b4be Poul Sander 2025-02-03 21:28 14456
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 14457
        SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 14458
        ClosePopupsOverWindow(apply_focus_window, false);
1193b4be Poul Sander 2025-02-03 21:28 14459
        FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
1193b4be Poul Sander 2025-02-03 21:28 14460
        apply_focus_window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 14461
        if (apply_focus_window->NavLastIds[0] == 0)
1193b4be Poul Sander 2025-02-03 21:28 14462
            NavInitWindow(apply_focus_window, false);
1193b4be Poul Sander 2025-02-03 21:28 14463
1193b4be Poul Sander 2025-02-03 21:28 14464
        // If the window has ONLY a menu layer (no main layer), select it directly
1193b4be Poul Sander 2025-02-03 21:28 14465
        // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
1193b4be Poul Sander 2025-02-03 21:28 14466
        // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
1193b4be Poul Sander 2025-02-03 21:28 14467
        // the target window as already been previewed once.
1193b4be Poul Sander 2025-02-03 21:28 14468
        // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
1193b4be Poul Sander 2025-02-03 21:28 14469
        // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
1193b4be Poul Sander 2025-02-03 21:28 14470
        // won't be valid.
1193b4be Poul Sander 2025-02-03 21:28 14471
        if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
1193b4be Poul Sander 2025-02-03 21:28 14472
            g.NavLayer = ImGuiNavLayer_Menu;
1193b4be Poul Sander 2025-02-03 21:28 14473
1193b4be Poul Sander 2025-02-03 21:28 14474
        // Request OS level focus
1193b4be Poul Sander 2025-02-03 21:28 14475
        if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
1193b4be Poul Sander 2025-02-03 21:28 14476
            g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 14477
    }
1193b4be Poul Sander 2025-02-03 21:28 14478
    if (apply_focus_window)
1193b4be Poul Sander 2025-02-03 21:28 14479
        g.NavWindowingTarget = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14480
1193b4be Poul Sander 2025-02-03 21:28 14481
    // Apply menu/layer toggle
1193b4be Poul Sander 2025-02-03 21:28 14482
    if (apply_toggle_layer && g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 14483
    {
1193b4be Poul Sander 2025-02-03 21:28 14484
        ClearActiveID();
1193b4be Poul Sander 2025-02-03 21:28 14485
1193b4be Poul Sander 2025-02-03 21:28 14486
        // Move to parent menu if necessary
1193b4be Poul Sander 2025-02-03 21:28 14487
        ImGuiWindow* new_nav_window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 14488
        while (new_nav_window->ParentWindow
1193b4be Poul Sander 2025-02-03 21:28 14489
            && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
1193b4be Poul Sander 2025-02-03 21:28 14490
            && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
1193b4be Poul Sander 2025-02-03 21:28 14491
            && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14492
            new_nav_window = new_nav_window->ParentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14493
        if (new_nav_window != g.NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 14494
        {
1193b4be Poul Sander 2025-02-03 21:28 14495
            ImGuiWindow* old_nav_window = g.NavWindow;
1193b4be Poul Sander 2025-02-03 21:28 14496
            FocusWindow(new_nav_window);
1193b4be Poul Sander 2025-02-03 21:28 14497
            new_nav_window->NavLastChildNavWindow = old_nav_window;
1193b4be Poul Sander 2025-02-03 21:28 14498
        }
1193b4be Poul Sander 2025-02-03 21:28 14499
1193b4be Poul Sander 2025-02-03 21:28 14500
        // Toggle layer
1193b4be Poul Sander 2025-02-03 21:28 14501
        const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 14502
        if (new_nav_layer != g.NavLayer)
1193b4be Poul Sander 2025-02-03 21:28 14503
        {
1193b4be Poul Sander 2025-02-03 21:28 14504
            // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
1193b4be Poul Sander 2025-02-03 21:28 14505
            const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
1193b4be Poul Sander 2025-02-03 21:28 14506
            if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
1193b4be Poul Sander 2025-02-03 21:28 14507
                g.NavWindow->NavLastIds[new_nav_layer] = 0;
1193b4be Poul Sander 2025-02-03 21:28 14508
            NavRestoreLayer(new_nav_layer);
1193b4be Poul Sander 2025-02-03 21:28 14509
            SetNavCursorVisibleAfterMove();
1193b4be Poul Sander 2025-02-03 21:28 14510
        }
1193b4be Poul Sander 2025-02-03 21:28 14511
    }
1193b4be Poul Sander 2025-02-03 21:28 14512
}
1193b4be Poul Sander 2025-02-03 21:28 14513
1193b4be Poul Sander 2025-02-03 21:28 14514
// Window has already passed the IsWindowNavFocusable()
1193b4be Poul Sander 2025-02-03 21:28 14515
static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 14516
{
1193b4be Poul Sander 2025-02-03 21:28 14517
    if (window->Flags & ImGuiWindowFlags_Popup)
1193b4be Poul Sander 2025-02-03 21:28 14518
        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
1193b4be Poul Sander 2025-02-03 21:28 14519
    if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
1193b4be Poul Sander 2025-02-03 21:28 14520
        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
1193b4be Poul Sander 2025-02-03 21:28 14521
    if (window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 14522
        return "(Dock node)"; // Not normally shown to user.
1193b4be Poul Sander 2025-02-03 21:28 14523
    return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
1193b4be Poul Sander 2025-02-03 21:28 14524
}
1193b4be Poul Sander 2025-02-03 21:28 14525
1193b4be Poul Sander 2025-02-03 21:28 14526
// Overlay displayed when using CTRL+TAB. Called by EndFrame().
1193b4be Poul Sander 2025-02-03 21:28 14527
void ImGui::NavUpdateWindowingOverlay()
1193b4be Poul Sander 2025-02-03 21:28 14528
{
1193b4be Poul Sander 2025-02-03 21:28 14529
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14530
    IM_ASSERT(g.NavWindowingTarget != NULL);
1193b4be Poul Sander 2025-02-03 21:28 14531
1193b4be Poul Sander 2025-02-03 21:28 14532
    if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
1193b4be Poul Sander 2025-02-03 21:28 14533
        return;
1193b4be Poul Sander 2025-02-03 21:28 14534
1193b4be Poul Sander 2025-02-03 21:28 14535
    if (g.NavWindowingListWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 14536
        g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
1193b4be Poul Sander 2025-02-03 21:28 14537
    const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 14538
    SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
1193b4be Poul Sander 2025-02-03 21:28 14539
    SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
1193b4be Poul Sander 2025-02-03 21:28 14540
    PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 14541
    Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
1193b4be Poul Sander 2025-02-03 21:28 14542
    if (g.ContextName[0] != 0)
1193b4be Poul Sander 2025-02-03 21:28 14543
        SeparatorText(g.ContextName);
1193b4be Poul Sander 2025-02-03 21:28 14544
    for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 14545
    {
1193b4be Poul Sander 2025-02-03 21:28 14546
        ImGuiWindow* window = g.WindowsFocusOrder[n];
1193b4be Poul Sander 2025-02-03 21:28 14547
        IM_ASSERT(window != NULL); // Fix static analyzers
1193b4be Poul Sander 2025-02-03 21:28 14548
        if (!IsWindowNavFocusable(window))
1193b4be Poul Sander 2025-02-03 21:28 14549
            continue;
1193b4be Poul Sander 2025-02-03 21:28 14550
        const char* label = window->Name;
1193b4be Poul Sander 2025-02-03 21:28 14551
        if (label == FindRenderedTextEnd(label))
1193b4be Poul Sander 2025-02-03 21:28 14552
            label = GetFallbackWindowNameForWindowingList(window);
1193b4be Poul Sander 2025-02-03 21:28 14553
        Selectable(label, g.NavWindowingTarget == window);
1193b4be Poul Sander 2025-02-03 21:28 14554
    }
1193b4be Poul Sander 2025-02-03 21:28 14555
    End();
1193b4be Poul Sander 2025-02-03 21:28 14556
    PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 14557
}
1193b4be Poul Sander 2025-02-03 21:28 14558
1193b4be Poul Sander 2025-02-03 21:28 14559
1193b4be Poul Sander 2025-02-03 21:28 14560
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 14561
// [SECTION] DRAG AND DROP
1193b4be Poul Sander 2025-02-03 21:28 14562
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 14563
1193b4be Poul Sander 2025-02-03 21:28 14564
bool ImGui::IsDragDropActive()
1193b4be Poul Sander 2025-02-03 21:28 14565
{
1193b4be Poul Sander 2025-02-03 21:28 14566
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14567
    return g.DragDropActive;
1193b4be Poul Sander 2025-02-03 21:28 14568
}
1193b4be Poul Sander 2025-02-03 21:28 14569
1193b4be Poul Sander 2025-02-03 21:28 14570
void ImGui::ClearDragDrop()
1193b4be Poul Sander 2025-02-03 21:28 14571
{
1193b4be Poul Sander 2025-02-03 21:28 14572
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14573
    if (g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 14574
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
1193b4be Poul Sander 2025-02-03 21:28 14575
    g.DragDropActive = false;
1193b4be Poul Sander 2025-02-03 21:28 14576
    g.DragDropPayload.Clear();
1193b4be Poul Sander 2025-02-03 21:28 14577
    g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 14578
    g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
1193b4be Poul Sander 2025-02-03 21:28 14579
    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 14580
    g.DragDropAcceptFrameCount = -1;
1193b4be Poul Sander 2025-02-03 21:28 14581
1193b4be Poul Sander 2025-02-03 21:28 14582
    g.DragDropPayloadBufHeap.clear();
1193b4be Poul Sander 2025-02-03 21:28 14583
    memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
1193b4be Poul Sander 2025-02-03 21:28 14584
}
1193b4be Poul Sander 2025-02-03 21:28 14585
1193b4be Poul Sander 2025-02-03 21:28 14586
bool ImGui::BeginTooltipHidden()
1193b4be Poul Sander 2025-02-03 21:28 14587
{
1193b4be Poul Sander 2025-02-03 21:28 14588
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14589
    bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
1193b4be Poul Sander 2025-02-03 21:28 14590
    SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
1193b4be Poul Sander 2025-02-03 21:28 14591
    return ret;
1193b4be Poul Sander 2025-02-03 21:28 14592
}
1193b4be Poul Sander 2025-02-03 21:28 14593
1193b4be Poul Sander 2025-02-03 21:28 14594
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
1193b4be Poul Sander 2025-02-03 21:28 14595
// If the item has an identifier:
1193b4be Poul Sander 2025-02-03 21:28 14596
// - This assume/require the item to be activated (typically via ButtonBehavior).
1193b4be Poul Sander 2025-02-03 21:28 14597
// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
1193b4be Poul Sander 2025-02-03 21:28 14598
// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
1193b4be Poul Sander 2025-02-03 21:28 14599
// If the item has no identifier:
1193b4be Poul Sander 2025-02-03 21:28 14600
// - Currently always assume left mouse button.
1193b4be Poul Sander 2025-02-03 21:28 14601
bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 14602
{
1193b4be Poul Sander 2025-02-03 21:28 14603
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14604
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14605
1193b4be Poul Sander 2025-02-03 21:28 14606
    // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
1193b4be Poul Sander 2025-02-03 21:28 14607
    // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
1193b4be Poul Sander 2025-02-03 21:28 14608
    ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
1193b4be Poul Sander 2025-02-03 21:28 14609
1193b4be Poul Sander 2025-02-03 21:28 14610
    bool source_drag_active = false;
1193b4be Poul Sander 2025-02-03 21:28 14611
    ImGuiID source_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 14612
    ImGuiID source_parent_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 14613
    if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
1193b4be Poul Sander 2025-02-03 21:28 14614
    {
1193b4be Poul Sander 2025-02-03 21:28 14615
        source_id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 14616
        if (source_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 14617
        {
1193b4be Poul Sander 2025-02-03 21:28 14618
            // Common path: items with ID
1193b4be Poul Sander 2025-02-03 21:28 14619
            if (g.ActiveId != source_id)
1193b4be Poul Sander 2025-02-03 21:28 14620
                return false;
1193b4be Poul Sander 2025-02-03 21:28 14621
            if (g.ActiveIdMouseButton != -1)
1193b4be Poul Sander 2025-02-03 21:28 14622
                mouse_button = g.ActiveIdMouseButton;
1193b4be Poul Sander 2025-02-03 21:28 14623
            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 14624
                return false;
1193b4be Poul Sander 2025-02-03 21:28 14625
            g.ActiveIdAllowOverlap = false;
1193b4be Poul Sander 2025-02-03 21:28 14626
        }
1193b4be Poul Sander 2025-02-03 21:28 14627
        else
1193b4be Poul Sander 2025-02-03 21:28 14628
        {
1193b4be Poul Sander 2025-02-03 21:28 14629
            // Uncommon path: items without ID
1193b4be Poul Sander 2025-02-03 21:28 14630
            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 14631
                return false;
1193b4be Poul Sander 2025-02-03 21:28 14632
            if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
1193b4be Poul Sander 2025-02-03 21:28 14633
                return false;
1193b4be Poul Sander 2025-02-03 21:28 14634
1193b4be Poul Sander 2025-02-03 21:28 14635
            // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
1193b4be Poul Sander 2025-02-03 21:28 14636
            // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
1193b4be Poul Sander 2025-02-03 21:28 14637
            if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
1193b4be Poul Sander 2025-02-03 21:28 14638
            {
1193b4be Poul Sander 2025-02-03 21:28 14639
                IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 14640
                return false;
1193b4be Poul Sander 2025-02-03 21:28 14641
            }
1193b4be Poul Sander 2025-02-03 21:28 14642
1193b4be Poul Sander 2025-02-03 21:28 14643
            // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
1193b4be Poul Sander 2025-02-03 21:28 14644
            // We build a throwaway ID based on current ID stack + relative AABB of items in window.
1193b4be Poul Sander 2025-02-03 21:28 14645
            // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
1193b4be Poul Sander 2025-02-03 21:28 14646
            // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
1193b4be Poul Sander 2025-02-03 21:28 14647
            // Rely on keeping other window->LastItemXXX fields intact.
1193b4be Poul Sander 2025-02-03 21:28 14648
            source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
1193b4be Poul Sander 2025-02-03 21:28 14649
            KeepAliveID(source_id);
1193b4be Poul Sander 2025-02-03 21:28 14650
            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
1193b4be Poul Sander 2025-02-03 21:28 14651
            if (is_hovered && g.IO.MouseClicked[mouse_button])
1193b4be Poul Sander 2025-02-03 21:28 14652
            {
1193b4be Poul Sander 2025-02-03 21:28 14653
                SetActiveID(source_id, window);
1193b4be Poul Sander 2025-02-03 21:28 14654
                FocusWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 14655
            }
1193b4be Poul Sander 2025-02-03 21:28 14656
            if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
1193b4be Poul Sander 2025-02-03 21:28 14657
                g.ActiveIdAllowOverlap = is_hovered;
1193b4be Poul Sander 2025-02-03 21:28 14658
        }
1193b4be Poul Sander 2025-02-03 21:28 14659
        if (g.ActiveId != source_id)
1193b4be Poul Sander 2025-02-03 21:28 14660
            return false;
1193b4be Poul Sander 2025-02-03 21:28 14661
        source_parent_id = window->IDStack.back();
1193b4be Poul Sander 2025-02-03 21:28 14662
        source_drag_active = IsMouseDragging(mouse_button);
1193b4be Poul Sander 2025-02-03 21:28 14663
1193b4be Poul Sander 2025-02-03 21:28 14664
        // Disable navigation and key inputs while dragging + cancel existing request if any
1193b4be Poul Sander 2025-02-03 21:28 14665
        SetActiveIdUsingAllKeyboardKeys();
1193b4be Poul Sander 2025-02-03 21:28 14666
    }
1193b4be Poul Sander 2025-02-03 21:28 14667
    else
1193b4be Poul Sander 2025-02-03 21:28 14668
    {
1193b4be Poul Sander 2025-02-03 21:28 14669
        // When ImGuiDragDropFlags_SourceExtern is set:
1193b4be Poul Sander 2025-02-03 21:28 14670
        window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14671
        source_id = ImHashStr("#SourceExtern");
1193b4be Poul Sander 2025-02-03 21:28 14672
        source_drag_active = true;
1193b4be Poul Sander 2025-02-03 21:28 14673
        mouse_button = g.IO.MouseDown[0] ? 0 : -1;
1193b4be Poul Sander 2025-02-03 21:28 14674
        KeepAliveID(source_id);
1193b4be Poul Sander 2025-02-03 21:28 14675
        SetActiveID(source_id, NULL);
1193b4be Poul Sander 2025-02-03 21:28 14676
    }
1193b4be Poul Sander 2025-02-03 21:28 14677
1193b4be Poul Sander 2025-02-03 21:28 14678
    IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 14679
    if (!source_drag_active)
1193b4be Poul Sander 2025-02-03 21:28 14680
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14681
1193b4be Poul Sander 2025-02-03 21:28 14682
    // Activate drag and drop
1193b4be Poul Sander 2025-02-03 21:28 14683
    if (!g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 14684
    {
1193b4be Poul Sander 2025-02-03 21:28 14685
        IM_ASSERT(source_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 14686
        ClearDragDrop();
1193b4be Poul Sander 2025-02-03 21:28 14687
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
1193b4be Poul Sander 2025-02-03 21:28 14688
            source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
1193b4be Poul Sander 2025-02-03 21:28 14689
        ImGuiPayload& payload = g.DragDropPayload;
1193b4be Poul Sander 2025-02-03 21:28 14690
        payload.SourceId = source_id;
1193b4be Poul Sander 2025-02-03 21:28 14691
        payload.SourceParentId = source_parent_id;
1193b4be Poul Sander 2025-02-03 21:28 14692
        g.DragDropActive = true;
1193b4be Poul Sander 2025-02-03 21:28 14693
        g.DragDropSourceFlags = flags;
1193b4be Poul Sander 2025-02-03 21:28 14694
        g.DragDropMouseButton = mouse_button;
1193b4be Poul Sander 2025-02-03 21:28 14695
        if (payload.SourceId == g.ActiveId)
1193b4be Poul Sander 2025-02-03 21:28 14696
            g.ActiveIdNoClearOnFocusLoss = true;
1193b4be Poul Sander 2025-02-03 21:28 14697
    }
1193b4be Poul Sander 2025-02-03 21:28 14698
    g.DragDropSourceFrameCount = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 14699
    g.DragDropWithinSource = true;
1193b4be Poul Sander 2025-02-03 21:28 14700
1193b4be Poul Sander 2025-02-03 21:28 14701
    if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
1193b4be Poul Sander 2025-02-03 21:28 14702
    {
1193b4be Poul Sander 2025-02-03 21:28 14703
        // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
1193b4be Poul Sander 2025-02-03 21:28 14704
        // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
1193b4be Poul Sander 2025-02-03 21:28 14705
        bool ret;
1193b4be Poul Sander 2025-02-03 21:28 14706
        if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
1193b4be Poul Sander 2025-02-03 21:28 14707
            ret = BeginTooltipHidden();
1193b4be Poul Sander 2025-02-03 21:28 14708
        else
1193b4be Poul Sander 2025-02-03 21:28 14709
            ret = BeginTooltip();
1193b4be Poul Sander 2025-02-03 21:28 14710
        IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
1193b4be Poul Sander 2025-02-03 21:28 14711
        IM_UNUSED(ret);
1193b4be Poul Sander 2025-02-03 21:28 14712
    }
1193b4be Poul Sander 2025-02-03 21:28 14713
1193b4be Poul Sander 2025-02-03 21:28 14714
    if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
1193b4be Poul Sander 2025-02-03 21:28 14715
        g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
1193b4be Poul Sander 2025-02-03 21:28 14716
1193b4be Poul Sander 2025-02-03 21:28 14717
    return true;
1193b4be Poul Sander 2025-02-03 21:28 14718
}
1193b4be Poul Sander 2025-02-03 21:28 14719
1193b4be Poul Sander 2025-02-03 21:28 14720
void ImGui::EndDragDropSource()
1193b4be Poul Sander 2025-02-03 21:28 14721
{
1193b4be Poul Sander 2025-02-03 21:28 14722
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14723
    IM_ASSERT(g.DragDropActive);
1193b4be Poul Sander 2025-02-03 21:28 14724
    IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
1193b4be Poul Sander 2025-02-03 21:28 14725
1193b4be Poul Sander 2025-02-03 21:28 14726
    if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
1193b4be Poul Sander 2025-02-03 21:28 14727
        EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 14728
1193b4be Poul Sander 2025-02-03 21:28 14729
    // Discard the drag if have not called SetDragDropPayload()
1193b4be Poul Sander 2025-02-03 21:28 14730
    if (g.DragDropPayload.DataFrameCount == -1)
1193b4be Poul Sander 2025-02-03 21:28 14731
        ClearDragDrop();
1193b4be Poul Sander 2025-02-03 21:28 14732
    g.DragDropWithinSource = false;
1193b4be Poul Sander 2025-02-03 21:28 14733
}
1193b4be Poul Sander 2025-02-03 21:28 14734
1193b4be Poul Sander 2025-02-03 21:28 14735
// Use 'cond' to choose to submit payload on drag start or every frame
1193b4be Poul Sander 2025-02-03 21:28 14736
bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 14737
{
1193b4be Poul Sander 2025-02-03 21:28 14738
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14739
    ImGuiPayload& payload = g.DragDropPayload;
1193b4be Poul Sander 2025-02-03 21:28 14740
    if (cond == 0)
1193b4be Poul Sander 2025-02-03 21:28 14741
        cond = ImGuiCond_Always;
1193b4be Poul Sander 2025-02-03 21:28 14742
1193b4be Poul Sander 2025-02-03 21:28 14743
    IM_ASSERT(type != NULL);
1193b4be Poul Sander 2025-02-03 21:28 14744
    IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
1193b4be Poul Sander 2025-02-03 21:28 14745
    IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
1193b4be Poul Sander 2025-02-03 21:28 14746
    IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
1193b4be Poul Sander 2025-02-03 21:28 14747
    IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
1193b4be Poul Sander 2025-02-03 21:28 14748
1193b4be Poul Sander 2025-02-03 21:28 14749
    if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
1193b4be Poul Sander 2025-02-03 21:28 14750
    {
1193b4be Poul Sander 2025-02-03 21:28 14751
        // Copy payload
1193b4be Poul Sander 2025-02-03 21:28 14752
        ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
1193b4be Poul Sander 2025-02-03 21:28 14753
        g.DragDropPayloadBufHeap.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 14754
        if (data_size > sizeof(g.DragDropPayloadBufLocal))
1193b4be Poul Sander 2025-02-03 21:28 14755
        {
1193b4be Poul Sander 2025-02-03 21:28 14756
            // Store in heap
1193b4be Poul Sander 2025-02-03 21:28 14757
            g.DragDropPayloadBufHeap.resize((int)data_size);
1193b4be Poul Sander 2025-02-03 21:28 14758
            payload.Data = g.DragDropPayloadBufHeap.Data;
1193b4be Poul Sander 2025-02-03 21:28 14759
            memcpy(payload.Data, data, data_size);
1193b4be Poul Sander 2025-02-03 21:28 14760
        }
1193b4be Poul Sander 2025-02-03 21:28 14761
        else if (data_size > 0)
1193b4be Poul Sander 2025-02-03 21:28 14762
        {
1193b4be Poul Sander 2025-02-03 21:28 14763
            // Store locally
1193b4be Poul Sander 2025-02-03 21:28 14764
            memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
1193b4be Poul Sander 2025-02-03 21:28 14765
            payload.Data = g.DragDropPayloadBufLocal;
1193b4be Poul Sander 2025-02-03 21:28 14766
            memcpy(payload.Data, data, data_size);
1193b4be Poul Sander 2025-02-03 21:28 14767
        }
1193b4be Poul Sander 2025-02-03 21:28 14768
        else
1193b4be Poul Sander 2025-02-03 21:28 14769
        {
1193b4be Poul Sander 2025-02-03 21:28 14770
            payload.Data = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14771
        }
1193b4be Poul Sander 2025-02-03 21:28 14772
        payload.DataSize = (int)data_size;
1193b4be Poul Sander 2025-02-03 21:28 14773
    }
1193b4be Poul Sander 2025-02-03 21:28 14774
    payload.DataFrameCount = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 14775
1193b4be Poul Sander 2025-02-03 21:28 14776
    // Return whether the payload has been accepted
1193b4be Poul Sander 2025-02-03 21:28 14777
    return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
1193b4be Poul Sander 2025-02-03 21:28 14778
}
1193b4be Poul Sander 2025-02-03 21:28 14779
1193b4be Poul Sander 2025-02-03 21:28 14780
bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 14781
{
1193b4be Poul Sander 2025-02-03 21:28 14782
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14783
    if (!g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 14784
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14785
1193b4be Poul Sander 2025-02-03 21:28 14786
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14787
    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
1193b4be Poul Sander 2025-02-03 21:28 14788
    if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
1193b4be Poul Sander 2025-02-03 21:28 14789
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14790
    IM_ASSERT(id != 0);
1193b4be Poul Sander 2025-02-03 21:28 14791
    if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
1193b4be Poul Sander 2025-02-03 21:28 14792
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14793
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 14794
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14795
1193b4be Poul Sander 2025-02-03 21:28 14796
    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 14797
    g.DragDropTargetRect = bb;
1193b4be Poul Sander 2025-02-03 21:28 14798
    g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
1193b4be Poul Sander 2025-02-03 21:28 14799
    g.DragDropTargetId = id;
1193b4be Poul Sander 2025-02-03 21:28 14800
    g.DragDropWithinTarget = true;
1193b4be Poul Sander 2025-02-03 21:28 14801
    return true;
1193b4be Poul Sander 2025-02-03 21:28 14802
}
1193b4be Poul Sander 2025-02-03 21:28 14803
1193b4be Poul Sander 2025-02-03 21:28 14804
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
1193b4be Poul Sander 2025-02-03 21:28 14805
// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
1193b4be Poul Sander 2025-02-03 21:28 14806
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
1193b4be Poul Sander 2025-02-03 21:28 14807
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
1193b4be Poul Sander 2025-02-03 21:28 14808
bool ImGui::BeginDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 14809
{
1193b4be Poul Sander 2025-02-03 21:28 14810
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14811
    if (!g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 14812
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14813
1193b4be Poul Sander 2025-02-03 21:28 14814
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14815
    if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
1193b4be Poul Sander 2025-02-03 21:28 14816
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14817
    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
1193b4be Poul Sander 2025-02-03 21:28 14818
    if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 14819
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14820
1193b4be Poul Sander 2025-02-03 21:28 14821
    const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
1193b4be Poul Sander 2025-02-03 21:28 14822
    ImGuiID id = g.LastItemData.ID;
1193b4be Poul Sander 2025-02-03 21:28 14823
    if (id == 0)
1193b4be Poul Sander 2025-02-03 21:28 14824
    {
1193b4be Poul Sander 2025-02-03 21:28 14825
        id = window->GetIDFromRectangle(display_rect);
1193b4be Poul Sander 2025-02-03 21:28 14826
        KeepAliveID(id);
1193b4be Poul Sander 2025-02-03 21:28 14827
    }
1193b4be Poul Sander 2025-02-03 21:28 14828
    if (g.DragDropPayload.SourceId == id)
1193b4be Poul Sander 2025-02-03 21:28 14829
        return false;
1193b4be Poul Sander 2025-02-03 21:28 14830
1193b4be Poul Sander 2025-02-03 21:28 14831
    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 14832
    g.DragDropTargetRect = display_rect;
1193b4be Poul Sander 2025-02-03 21:28 14833
    g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
1193b4be Poul Sander 2025-02-03 21:28 14834
    g.DragDropTargetId = id;
1193b4be Poul Sander 2025-02-03 21:28 14835
    g.DragDropWithinTarget = true;
1193b4be Poul Sander 2025-02-03 21:28 14836
    return true;
1193b4be Poul Sander 2025-02-03 21:28 14837
}
1193b4be Poul Sander 2025-02-03 21:28 14838
1193b4be Poul Sander 2025-02-03 21:28 14839
bool ImGui::IsDragDropPayloadBeingAccepted()
1193b4be Poul Sander 2025-02-03 21:28 14840
{
1193b4be Poul Sander 2025-02-03 21:28 14841
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14842
    return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
1193b4be Poul Sander 2025-02-03 21:28 14843
}
1193b4be Poul Sander 2025-02-03 21:28 14844
1193b4be Poul Sander 2025-02-03 21:28 14845
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 14846
{
1193b4be Poul Sander 2025-02-03 21:28 14847
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14848
    ImGuiPayload& payload = g.DragDropPayload;
1193b4be Poul Sander 2025-02-03 21:28 14849
    IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
1193b4be Poul Sander 2025-02-03 21:28 14850
    IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
1193b4be Poul Sander 2025-02-03 21:28 14851
    if (type != NULL && !payload.IsDataType(type))
1193b4be Poul Sander 2025-02-03 21:28 14852
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 14853
1193b4be Poul Sander 2025-02-03 21:28 14854
    // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
1193b4be Poul Sander 2025-02-03 21:28 14855
    // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
1193b4be Poul Sander 2025-02-03 21:28 14856
    const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
1193b4be Poul Sander 2025-02-03 21:28 14857
    ImRect r = g.DragDropTargetRect;
1193b4be Poul Sander 2025-02-03 21:28 14858
    float r_surface = r.GetWidth() * r.GetHeight();
1193b4be Poul Sander 2025-02-03 21:28 14859
    if (r_surface > g.DragDropAcceptIdCurrRectSurface)
1193b4be Poul Sander 2025-02-03 21:28 14860
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 14861
1193b4be Poul Sander 2025-02-03 21:28 14862
    g.DragDropAcceptFlags = flags;
1193b4be Poul Sander 2025-02-03 21:28 14863
    g.DragDropAcceptIdCurr = g.DragDropTargetId;
1193b4be Poul Sander 2025-02-03 21:28 14864
    g.DragDropAcceptIdCurrRectSurface = r_surface;
1193b4be Poul Sander 2025-02-03 21:28 14865
    //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
1193b4be Poul Sander 2025-02-03 21:28 14866
1193b4be Poul Sander 2025-02-03 21:28 14867
    // Render default drop visuals
1193b4be Poul Sander 2025-02-03 21:28 14868
    payload.Preview = was_accepted_previously;
1193b4be Poul Sander 2025-02-03 21:28 14869
    flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
1193b4be Poul Sander 2025-02-03 21:28 14870
    if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
1193b4be Poul Sander 2025-02-03 21:28 14871
        RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
1193b4be Poul Sander 2025-02-03 21:28 14872
1193b4be Poul Sander 2025-02-03 21:28 14873
    g.DragDropAcceptFrameCount = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 14874
    if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
1193b4be Poul Sander 2025-02-03 21:28 14875
        payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 14876
    else
1193b4be Poul Sander 2025-02-03 21:28 14877
        payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
1193b4be Poul Sander 2025-02-03 21:28 14878
    if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
1193b4be Poul Sander 2025-02-03 21:28 14879
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 14880
1193b4be Poul Sander 2025-02-03 21:28 14881
    if (payload.Delivery)
1193b4be Poul Sander 2025-02-03 21:28 14882
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
1193b4be Poul Sander 2025-02-03 21:28 14883
    return &payload;
1193b4be Poul Sander 2025-02-03 21:28 14884
}
1193b4be Poul Sander 2025-02-03 21:28 14885
1193b4be Poul Sander 2025-02-03 21:28 14886
// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
1193b4be Poul Sander 2025-02-03 21:28 14887
void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 14888
{
1193b4be Poul Sander 2025-02-03 21:28 14889
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14890
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14891
    ImRect bb_display = bb;
1193b4be Poul Sander 2025-02-03 21:28 14892
    bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
1193b4be Poul Sander 2025-02-03 21:28 14893
    bb_display.Expand(3.5f);
1193b4be Poul Sander 2025-02-03 21:28 14894
    bool push_clip_rect = !window->ClipRect.Contains(bb_display);
1193b4be Poul Sander 2025-02-03 21:28 14895
    if (push_clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 14896
        window->DrawList->PushClipRectFullScreen();
1193b4be Poul Sander 2025-02-03 21:28 14897
    window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 14898
    if (push_clip_rect)
1193b4be Poul Sander 2025-02-03 21:28 14899
        window->DrawList->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 14900
}
1193b4be Poul Sander 2025-02-03 21:28 14901
1193b4be Poul Sander 2025-02-03 21:28 14902
const ImGuiPayload* ImGui::GetDragDropPayload()
1193b4be Poul Sander 2025-02-03 21:28 14903
{
1193b4be Poul Sander 2025-02-03 21:28 14904
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14905
    return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
1193b4be Poul Sander 2025-02-03 21:28 14906
}
1193b4be Poul Sander 2025-02-03 21:28 14907
1193b4be Poul Sander 2025-02-03 21:28 14908
void ImGui::EndDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 14909
{
1193b4be Poul Sander 2025-02-03 21:28 14910
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14911
    IM_ASSERT(g.DragDropActive);
1193b4be Poul Sander 2025-02-03 21:28 14912
    IM_ASSERT(g.DragDropWithinTarget);
1193b4be Poul Sander 2025-02-03 21:28 14913
    g.DragDropWithinTarget = false;
1193b4be Poul Sander 2025-02-03 21:28 14914
1193b4be Poul Sander 2025-02-03 21:28 14915
    // Clear drag and drop state payload right after delivery
1193b4be Poul Sander 2025-02-03 21:28 14916
    if (g.DragDropPayload.Delivery)
1193b4be Poul Sander 2025-02-03 21:28 14917
        ClearDragDrop();
1193b4be Poul Sander 2025-02-03 21:28 14918
}
1193b4be Poul Sander 2025-02-03 21:28 14919
1193b4be Poul Sander 2025-02-03 21:28 14920
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 14921
// [SECTION] LOGGING/CAPTURING
1193b4be Poul Sander 2025-02-03 21:28 14922
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 14923
// All text output from the interface can be captured into tty/file/clipboard.
1193b4be Poul Sander 2025-02-03 21:28 14924
// By default, tree nodes are automatically opened during logging.
1193b4be Poul Sander 2025-02-03 21:28 14925
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 14926
1193b4be Poul Sander 2025-02-03 21:28 14927
// Pass text data straight to log (without being displayed)
1193b4be Poul Sander 2025-02-03 21:28 14928
static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 14929
{
1193b4be Poul Sander 2025-02-03 21:28 14930
    if (g.LogFile)
1193b4be Poul Sander 2025-02-03 21:28 14931
    {
1193b4be Poul Sander 2025-02-03 21:28 14932
        g.LogBuffer.Buf.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 14933
        g.LogBuffer.appendfv(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 14934
        ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
1193b4be Poul Sander 2025-02-03 21:28 14935
    }
1193b4be Poul Sander 2025-02-03 21:28 14936
    else
1193b4be Poul Sander 2025-02-03 21:28 14937
    {
1193b4be Poul Sander 2025-02-03 21:28 14938
        g.LogBuffer.appendfv(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 14939
    }
1193b4be Poul Sander 2025-02-03 21:28 14940
}
1193b4be Poul Sander 2025-02-03 21:28 14941
1193b4be Poul Sander 2025-02-03 21:28 14942
void ImGui::LogText(const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 14943
{
1193b4be Poul Sander 2025-02-03 21:28 14944
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14945
    if (!g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 14946
        return;
1193b4be Poul Sander 2025-02-03 21:28 14947
1193b4be Poul Sander 2025-02-03 21:28 14948
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 14949
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 14950
    LogTextV(g, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 14951
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 14952
}
1193b4be Poul Sander 2025-02-03 21:28 14953
1193b4be Poul Sander 2025-02-03 21:28 14954
void ImGui::LogTextV(const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 14955
{
1193b4be Poul Sander 2025-02-03 21:28 14956
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14957
    if (!g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 14958
        return;
1193b4be Poul Sander 2025-02-03 21:28 14959
1193b4be Poul Sander 2025-02-03 21:28 14960
    LogTextV(g, fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 14961
}
1193b4be Poul Sander 2025-02-03 21:28 14962
1193b4be Poul Sander 2025-02-03 21:28 14963
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
1193b4be Poul Sander 2025-02-03 21:28 14964
// We split text into individual lines to add current tree level padding
1193b4be Poul Sander 2025-02-03 21:28 14965
// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
1193b4be Poul Sander 2025-02-03 21:28 14966
void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
1193b4be Poul Sander 2025-02-03 21:28 14967
{
1193b4be Poul Sander 2025-02-03 21:28 14968
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 14969
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 14970
1193b4be Poul Sander 2025-02-03 21:28 14971
    const char* prefix = g.LogNextPrefix;
1193b4be Poul Sander 2025-02-03 21:28 14972
    const char* suffix = g.LogNextSuffix;
1193b4be Poul Sander 2025-02-03 21:28 14973
    g.LogNextPrefix = g.LogNextSuffix = NULL;
1193b4be Poul Sander 2025-02-03 21:28 14974
1193b4be Poul Sander 2025-02-03 21:28 14975
    if (!text_end)
1193b4be Poul Sander 2025-02-03 21:28 14976
        text_end = FindRenderedTextEnd(text, text_end);
1193b4be Poul Sander 2025-02-03 21:28 14977
1193b4be Poul Sander 2025-02-03 21:28 14978
    const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
1193b4be Poul Sander 2025-02-03 21:28 14979
    if (ref_pos)
1193b4be Poul Sander 2025-02-03 21:28 14980
        g.LogLinePosY = ref_pos->y;
1193b4be Poul Sander 2025-02-03 21:28 14981
    if (log_new_line)
1193b4be Poul Sander 2025-02-03 21:28 14982
    {
1193b4be Poul Sander 2025-02-03 21:28 14983
        LogText(IM_NEWLINE);
1193b4be Poul Sander 2025-02-03 21:28 14984
        g.LogLineFirstItem = true;
1193b4be Poul Sander 2025-02-03 21:28 14985
    }
1193b4be Poul Sander 2025-02-03 21:28 14986
1193b4be Poul Sander 2025-02-03 21:28 14987
    if (prefix)
1193b4be Poul Sander 2025-02-03 21:28 14988
        LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
1193b4be Poul Sander 2025-02-03 21:28 14989
1193b4be Poul Sander 2025-02-03 21:28 14990
    // Re-adjust padding if we have popped out of our starting depth
1193b4be Poul Sander 2025-02-03 21:28 14991
    if (g.LogDepthRef > window->DC.TreeDepth)
1193b4be Poul Sander 2025-02-03 21:28 14992
        g.LogDepthRef = window->DC.TreeDepth;
1193b4be Poul Sander 2025-02-03 21:28 14993
    const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
1193b4be Poul Sander 2025-02-03 21:28 14994
1193b4be Poul Sander 2025-02-03 21:28 14995
    const char* text_remaining = text;
1193b4be Poul Sander 2025-02-03 21:28 14996
    for (;;)
1193b4be Poul Sander 2025-02-03 21:28 14997
    {
1193b4be Poul Sander 2025-02-03 21:28 14998
        // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
1193b4be Poul Sander 2025-02-03 21:28 14999
        // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
1193b4be Poul Sander 2025-02-03 21:28 15000
        const char* line_start = text_remaining;
1193b4be Poul Sander 2025-02-03 21:28 15001
        const char* line_end = ImStreolRange(line_start, text_end);
1193b4be Poul Sander 2025-02-03 21:28 15002
        const bool is_last_line = (line_end == text_end);
1193b4be Poul Sander 2025-02-03 21:28 15003
        if (line_start != line_end || !is_last_line)
1193b4be Poul Sander 2025-02-03 21:28 15004
        {
1193b4be Poul Sander 2025-02-03 21:28 15005
            const int line_length = (int)(line_end - line_start);
1193b4be Poul Sander 2025-02-03 21:28 15006
            const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
1193b4be Poul Sander 2025-02-03 21:28 15007
            LogText("%*s%.*s", indentation, "", line_length, line_start);
1193b4be Poul Sander 2025-02-03 21:28 15008
            g.LogLineFirstItem = false;
1193b4be Poul Sander 2025-02-03 21:28 15009
            if (*line_end == '\n')
1193b4be Poul Sander 2025-02-03 21:28 15010
            {
1193b4be Poul Sander 2025-02-03 21:28 15011
                LogText(IM_NEWLINE);
1193b4be Poul Sander 2025-02-03 21:28 15012
                g.LogLineFirstItem = true;
1193b4be Poul Sander 2025-02-03 21:28 15013
            }
1193b4be Poul Sander 2025-02-03 21:28 15014
        }
1193b4be Poul Sander 2025-02-03 21:28 15015
        if (is_last_line)
1193b4be Poul Sander 2025-02-03 21:28 15016
            break;
1193b4be Poul Sander 2025-02-03 21:28 15017
        text_remaining = line_end + 1;
1193b4be Poul Sander 2025-02-03 21:28 15018
    }
1193b4be Poul Sander 2025-02-03 21:28 15019
1193b4be Poul Sander 2025-02-03 21:28 15020
    if (suffix)
1193b4be Poul Sander 2025-02-03 21:28 15021
        LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
1193b4be Poul Sander 2025-02-03 21:28 15022
}
1193b4be Poul Sander 2025-02-03 21:28 15023
1193b4be Poul Sander 2025-02-03 21:28 15024
// Start logging/capturing text output
1193b4be Poul Sander 2025-02-03 21:28 15025
void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
1193b4be Poul Sander 2025-02-03 21:28 15026
{
1193b4be Poul Sander 2025-02-03 21:28 15027
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15028
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 15029
    IM_ASSERT(g.LogEnabled == false);
1193b4be Poul Sander 2025-02-03 21:28 15030
    IM_ASSERT(g.LogFile == NULL);
1193b4be Poul Sander 2025-02-03 21:28 15031
    IM_ASSERT(g.LogBuffer.empty());
1193b4be Poul Sander 2025-02-03 21:28 15032
    g.LogEnabled = g.ItemUnclipByLog = true;
1193b4be Poul Sander 2025-02-03 21:28 15033
    g.LogType = type;
1193b4be Poul Sander 2025-02-03 21:28 15034
    g.LogNextPrefix = g.LogNextSuffix = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15035
    g.LogDepthRef = window->DC.TreeDepth;
1193b4be Poul Sander 2025-02-03 21:28 15036
    g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
1193b4be Poul Sander 2025-02-03 21:28 15037
    g.LogLinePosY = FLT_MAX;
1193b4be Poul Sander 2025-02-03 21:28 15038
    g.LogLineFirstItem = true;
1193b4be Poul Sander 2025-02-03 21:28 15039
}
1193b4be Poul Sander 2025-02-03 21:28 15040
1193b4be Poul Sander 2025-02-03 21:28 15041
// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
1193b4be Poul Sander 2025-02-03 21:28 15042
void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
1193b4be Poul Sander 2025-02-03 21:28 15043
{
1193b4be Poul Sander 2025-02-03 21:28 15044
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15045
    g.LogNextPrefix = prefix;
1193b4be Poul Sander 2025-02-03 21:28 15046
    g.LogNextSuffix = suffix;
1193b4be Poul Sander 2025-02-03 21:28 15047
}
1193b4be Poul Sander 2025-02-03 21:28 15048
1193b4be Poul Sander 2025-02-03 21:28 15049
void ImGui::LogToTTY(int auto_open_depth)
1193b4be Poul Sander 2025-02-03 21:28 15050
{
1193b4be Poul Sander 2025-02-03 21:28 15051
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15052
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 15053
        return;
1193b4be Poul Sander 2025-02-03 21:28 15054
    IM_UNUSED(auto_open_depth);
1193b4be Poul Sander 2025-02-03 21:28 15055
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 15056
    LogBegin(ImGuiLogType_TTY, auto_open_depth);
1193b4be Poul Sander 2025-02-03 21:28 15057
    g.LogFile = stdout;
1193b4be Poul Sander 2025-02-03 21:28 15058
#endif
1193b4be Poul Sander 2025-02-03 21:28 15059
}
1193b4be Poul Sander 2025-02-03 21:28 15060
1193b4be Poul Sander 2025-02-03 21:28 15061
// Start logging/capturing text output to given file
1193b4be Poul Sander 2025-02-03 21:28 15062
void ImGui::LogToFile(int auto_open_depth, const char* filename)
1193b4be Poul Sander 2025-02-03 21:28 15063
{
1193b4be Poul Sander 2025-02-03 21:28 15064
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15065
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 15066
        return;
1193b4be Poul Sander 2025-02-03 21:28 15067
1193b4be Poul Sander 2025-02-03 21:28 15068
    // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
1193b4be Poul Sander 2025-02-03 21:28 15069
    // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
1193b4be Poul Sander 2025-02-03 21:28 15070
    // By opening the file in binary mode "ab" we have consistent output everywhere.
1193b4be Poul Sander 2025-02-03 21:28 15071
    if (!filename)
1193b4be Poul Sander 2025-02-03 21:28 15072
        filename = g.IO.LogFilename;
1193b4be Poul Sander 2025-02-03 21:28 15073
    if (!filename || !filename[0])
1193b4be Poul Sander 2025-02-03 21:28 15074
        return;
1193b4be Poul Sander 2025-02-03 21:28 15075
    ImFileHandle f = ImFileOpen(filename, "ab");
1193b4be Poul Sander 2025-02-03 21:28 15076
    if (!f)
1193b4be Poul Sander 2025-02-03 21:28 15077
    {
1193b4be Poul Sander 2025-02-03 21:28 15078
        IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 15079
        return;
1193b4be Poul Sander 2025-02-03 21:28 15080
    }
1193b4be Poul Sander 2025-02-03 21:28 15081
1193b4be Poul Sander 2025-02-03 21:28 15082
    LogBegin(ImGuiLogType_File, auto_open_depth);
1193b4be Poul Sander 2025-02-03 21:28 15083
    g.LogFile = f;
1193b4be Poul Sander 2025-02-03 21:28 15084
}
1193b4be Poul Sander 2025-02-03 21:28 15085
1193b4be Poul Sander 2025-02-03 21:28 15086
// Start logging/capturing text output to clipboard
1193b4be Poul Sander 2025-02-03 21:28 15087
void ImGui::LogToClipboard(int auto_open_depth)
1193b4be Poul Sander 2025-02-03 21:28 15088
{
1193b4be Poul Sander 2025-02-03 21:28 15089
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15090
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 15091
        return;
1193b4be Poul Sander 2025-02-03 21:28 15092
    LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
1193b4be Poul Sander 2025-02-03 21:28 15093
}
1193b4be Poul Sander 2025-02-03 21:28 15094
1193b4be Poul Sander 2025-02-03 21:28 15095
void ImGui::LogToBuffer(int auto_open_depth)
1193b4be Poul Sander 2025-02-03 21:28 15096
{
1193b4be Poul Sander 2025-02-03 21:28 15097
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15098
    if (g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 15099
        return;
1193b4be Poul Sander 2025-02-03 21:28 15100
    LogBegin(ImGuiLogType_Buffer, auto_open_depth);
1193b4be Poul Sander 2025-02-03 21:28 15101
}
1193b4be Poul Sander 2025-02-03 21:28 15102
1193b4be Poul Sander 2025-02-03 21:28 15103
void ImGui::LogFinish()
1193b4be Poul Sander 2025-02-03 21:28 15104
{
1193b4be Poul Sander 2025-02-03 21:28 15105
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15106
    if (!g.LogEnabled)
1193b4be Poul Sander 2025-02-03 21:28 15107
        return;
1193b4be Poul Sander 2025-02-03 21:28 15108
1193b4be Poul Sander 2025-02-03 21:28 15109
    LogText(IM_NEWLINE);
1193b4be Poul Sander 2025-02-03 21:28 15110
    switch (g.LogType)
1193b4be Poul Sander 2025-02-03 21:28 15111
    {
1193b4be Poul Sander 2025-02-03 21:28 15112
    case ImGuiLogType_TTY:
1193b4be Poul Sander 2025-02-03 21:28 15113
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 15114
        fflush(g.LogFile);
1193b4be Poul Sander 2025-02-03 21:28 15115
#endif
1193b4be Poul Sander 2025-02-03 21:28 15116
        break;
1193b4be Poul Sander 2025-02-03 21:28 15117
    case ImGuiLogType_File:
1193b4be Poul Sander 2025-02-03 21:28 15118
        ImFileClose(g.LogFile);
1193b4be Poul Sander 2025-02-03 21:28 15119
        break;
1193b4be Poul Sander 2025-02-03 21:28 15120
    case ImGuiLogType_Buffer:
1193b4be Poul Sander 2025-02-03 21:28 15121
        break;
1193b4be Poul Sander 2025-02-03 21:28 15122
    case ImGuiLogType_Clipboard:
1193b4be Poul Sander 2025-02-03 21:28 15123
        if (!g.LogBuffer.empty())
1193b4be Poul Sander 2025-02-03 21:28 15124
            SetClipboardText(g.LogBuffer.begin());
1193b4be Poul Sander 2025-02-03 21:28 15125
        break;
1193b4be Poul Sander 2025-02-03 21:28 15126
    case ImGuiLogType_None:
1193b4be Poul Sander 2025-02-03 21:28 15127
        IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 15128
        break;
1193b4be Poul Sander 2025-02-03 21:28 15129
    }
1193b4be Poul Sander 2025-02-03 21:28 15130
1193b4be Poul Sander 2025-02-03 21:28 15131
    g.LogEnabled = g.ItemUnclipByLog = false;
1193b4be Poul Sander 2025-02-03 21:28 15132
    g.LogType = ImGuiLogType_None;
1193b4be Poul Sander 2025-02-03 21:28 15133
    g.LogFile = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15134
    g.LogBuffer.clear();
1193b4be Poul Sander 2025-02-03 21:28 15135
}
1193b4be Poul Sander 2025-02-03 21:28 15136
1193b4be Poul Sander 2025-02-03 21:28 15137
// Helper to display logging buttons
1193b4be Poul Sander 2025-02-03 21:28 15138
// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
1193b4be Poul Sander 2025-02-03 21:28 15139
void ImGui::LogButtons()
1193b4be Poul Sander 2025-02-03 21:28 15140
{
1193b4be Poul Sander 2025-02-03 21:28 15141
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15142
1193b4be Poul Sander 2025-02-03 21:28 15143
    PushID("LogButtons");
1193b4be Poul Sander 2025-02-03 21:28 15144
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 15145
    const bool log_to_tty = Button("Log To TTY"); SameLine();
1193b4be Poul Sander 2025-02-03 21:28 15146
#else
1193b4be Poul Sander 2025-02-03 21:28 15147
    const bool log_to_tty = false;
1193b4be Poul Sander 2025-02-03 21:28 15148
#endif
1193b4be Poul Sander 2025-02-03 21:28 15149
    const bool log_to_file = Button("Log To File"); SameLine();
1193b4be Poul Sander 2025-02-03 21:28 15150
    const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
1193b4be Poul Sander 2025-02-03 21:28 15151
    PushItemFlag(ImGuiItemFlags_NoTabStop, true);
1193b4be Poul Sander 2025-02-03 21:28 15152
    SetNextItemWidth(80.0f);
1193b4be Poul Sander 2025-02-03 21:28 15153
    SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
1193b4be Poul Sander 2025-02-03 21:28 15154
    PopItemFlag();
1193b4be Poul Sander 2025-02-03 21:28 15155
    PopID();
1193b4be Poul Sander 2025-02-03 21:28 15156
1193b4be Poul Sander 2025-02-03 21:28 15157
    // Start logging at the end of the function so that the buttons don't appear in the log
1193b4be Poul Sander 2025-02-03 21:28 15158
    if (log_to_tty)
1193b4be Poul Sander 2025-02-03 21:28 15159
        LogToTTY();
1193b4be Poul Sander 2025-02-03 21:28 15160
    if (log_to_file)
1193b4be Poul Sander 2025-02-03 21:28 15161
        LogToFile();
1193b4be Poul Sander 2025-02-03 21:28 15162
    if (log_to_clipboard)
1193b4be Poul Sander 2025-02-03 21:28 15163
        LogToClipboard();
1193b4be Poul Sander 2025-02-03 21:28 15164
}
1193b4be Poul Sander 2025-02-03 21:28 15165
1193b4be Poul Sander 2025-02-03 21:28 15166
1193b4be Poul Sander 2025-02-03 21:28 15167
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15168
// [SECTION] SETTINGS
1193b4be Poul Sander 2025-02-03 21:28 15169
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15170
// - UpdateSettings() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15171
// - MarkIniSettingsDirty() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15172
// - FindSettingsHandler() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15173
// - ClearIniSettings() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15174
// - LoadIniSettingsFromDisk()
1193b4be Poul Sander 2025-02-03 21:28 15175
// - LoadIniSettingsFromMemory()
1193b4be Poul Sander 2025-02-03 21:28 15176
// - SaveIniSettingsToDisk()
1193b4be Poul Sander 2025-02-03 21:28 15177
// - SaveIniSettingsToMemory()
1193b4be Poul Sander 2025-02-03 21:28 15178
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15179
// - CreateNewWindowSettings() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15180
// - FindWindowSettingsByID() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15181
// - FindWindowSettingsByWindow() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15182
// - ClearWindowSettings() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15183
// - WindowSettingsHandler_***() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15184
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15185
1193b4be Poul Sander 2025-02-03 21:28 15186
// Called by NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 15187
void ImGui::UpdateSettings()
1193b4be Poul Sander 2025-02-03 21:28 15188
{
1193b4be Poul Sander 2025-02-03 21:28 15189
    // Load settings on first frame (if not explicitly loaded manually before)
1193b4be Poul Sander 2025-02-03 21:28 15190
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15191
    if (!g.SettingsLoaded)
1193b4be Poul Sander 2025-02-03 21:28 15192
    {
1193b4be Poul Sander 2025-02-03 21:28 15193
        IM_ASSERT(g.SettingsWindows.empty());
1193b4be Poul Sander 2025-02-03 21:28 15194
        if (g.IO.IniFilename)
1193b4be Poul Sander 2025-02-03 21:28 15195
            LoadIniSettingsFromDisk(g.IO.IniFilename);
1193b4be Poul Sander 2025-02-03 21:28 15196
        g.SettingsLoaded = true;
1193b4be Poul Sander 2025-02-03 21:28 15197
    }
1193b4be Poul Sander 2025-02-03 21:28 15198
1193b4be Poul Sander 2025-02-03 21:28 15199
    // Save settings (with a delay after the last modification, so we don't spam disk too much)
1193b4be Poul Sander 2025-02-03 21:28 15200
    if (g.SettingsDirtyTimer > 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 15201
    {
1193b4be Poul Sander 2025-02-03 21:28 15202
        g.SettingsDirtyTimer -= g.IO.DeltaTime;
1193b4be Poul Sander 2025-02-03 21:28 15203
        if (g.SettingsDirtyTimer <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 15204
        {
1193b4be Poul Sander 2025-02-03 21:28 15205
            if (g.IO.IniFilename != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15206
                SaveIniSettingsToDisk(g.IO.IniFilename);
1193b4be Poul Sander 2025-02-03 21:28 15207
            else
1193b4be Poul Sander 2025-02-03 21:28 15208
                g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
1193b4be Poul Sander 2025-02-03 21:28 15209
            g.SettingsDirtyTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 15210
        }
1193b4be Poul Sander 2025-02-03 21:28 15211
    }
1193b4be Poul Sander 2025-02-03 21:28 15212
}
1193b4be Poul Sander 2025-02-03 21:28 15213
1193b4be Poul Sander 2025-02-03 21:28 15214
void ImGui::MarkIniSettingsDirty()
1193b4be Poul Sander 2025-02-03 21:28 15215
{
1193b4be Poul Sander 2025-02-03 21:28 15216
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15217
    if (g.SettingsDirtyTimer <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 15218
        g.SettingsDirtyTimer = g.IO.IniSavingRate;
1193b4be Poul Sander 2025-02-03 21:28 15219
}
1193b4be Poul Sander 2025-02-03 21:28 15220
1193b4be Poul Sander 2025-02-03 21:28 15221
void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 15222
{
1193b4be Poul Sander 2025-02-03 21:28 15223
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15224
    if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
1193b4be Poul Sander 2025-02-03 21:28 15225
        if (g.SettingsDirtyTimer <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 15226
            g.SettingsDirtyTimer = g.IO.IniSavingRate;
1193b4be Poul Sander 2025-02-03 21:28 15227
}
1193b4be Poul Sander 2025-02-03 21:28 15228
1193b4be Poul Sander 2025-02-03 21:28 15229
void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
1193b4be Poul Sander 2025-02-03 21:28 15230
{
1193b4be Poul Sander 2025-02-03 21:28 15231
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15232
    IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
1193b4be Poul Sander 2025-02-03 21:28 15233
    g.SettingsHandlers.push_back(*handler);
1193b4be Poul Sander 2025-02-03 21:28 15234
}
1193b4be Poul Sander 2025-02-03 21:28 15235
1193b4be Poul Sander 2025-02-03 21:28 15236
void ImGui::RemoveSettingsHandler(const char* type_name)
1193b4be Poul Sander 2025-02-03 21:28 15237
{
1193b4be Poul Sander 2025-02-03 21:28 15238
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15239
    if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
1193b4be Poul Sander 2025-02-03 21:28 15240
        g.SettingsHandlers.erase(handler);
1193b4be Poul Sander 2025-02-03 21:28 15241
}
1193b4be Poul Sander 2025-02-03 21:28 15242
1193b4be Poul Sander 2025-02-03 21:28 15243
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
1193b4be Poul Sander 2025-02-03 21:28 15244
{
1193b4be Poul Sander 2025-02-03 21:28 15245
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15246
    const ImGuiID type_hash = ImHashStr(type_name);
1193b4be Poul Sander 2025-02-03 21:28 15247
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 15248
        if (handler.TypeHash == type_hash)
1193b4be Poul Sander 2025-02-03 21:28 15249
            return &handler;
1193b4be Poul Sander 2025-02-03 21:28 15250
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 15251
}
1193b4be Poul Sander 2025-02-03 21:28 15252
1193b4be Poul Sander 2025-02-03 21:28 15253
// Clear all settings (windows, tables, docking etc.)
1193b4be Poul Sander 2025-02-03 21:28 15254
void ImGui::ClearIniSettings()
1193b4be Poul Sander 2025-02-03 21:28 15255
{
1193b4be Poul Sander 2025-02-03 21:28 15256
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15257
    g.SettingsIniData.clear();
1193b4be Poul Sander 2025-02-03 21:28 15258
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 15259
        if (handler.ClearAllFn != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15260
            handler.ClearAllFn(&g, &handler);
1193b4be Poul Sander 2025-02-03 21:28 15261
}
1193b4be Poul Sander 2025-02-03 21:28 15262
1193b4be Poul Sander 2025-02-03 21:28 15263
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
1193b4be Poul Sander 2025-02-03 21:28 15264
{
1193b4be Poul Sander 2025-02-03 21:28 15265
    size_t file_data_size = 0;
1193b4be Poul Sander 2025-02-03 21:28 15266
    char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
1193b4be Poul Sander 2025-02-03 21:28 15267
    if (!file_data)
1193b4be Poul Sander 2025-02-03 21:28 15268
        return;
1193b4be Poul Sander 2025-02-03 21:28 15269
    if (file_data_size > 0)
1193b4be Poul Sander 2025-02-03 21:28 15270
        LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
1193b4be Poul Sander 2025-02-03 21:28 15271
    IM_FREE(file_data);
1193b4be Poul Sander 2025-02-03 21:28 15272
}
1193b4be Poul Sander 2025-02-03 21:28 15273
1193b4be Poul Sander 2025-02-03 21:28 15274
// Zero-tolerance, no error reporting, cheap .ini parsing
1193b4be Poul Sander 2025-02-03 21:28 15275
// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!
1193b4be Poul Sander 2025-02-03 21:28 15276
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
1193b4be Poul Sander 2025-02-03 21:28 15277
{
1193b4be Poul Sander 2025-02-03 21:28 15278
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15279
    IM_ASSERT(g.Initialized);
1193b4be Poul Sander 2025-02-03 21:28 15280
    //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
1193b4be Poul Sander 2025-02-03 21:28 15281
    //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
1193b4be Poul Sander 2025-02-03 21:28 15282
1193b4be Poul Sander 2025-02-03 21:28 15283
    // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
1193b4be Poul Sander 2025-02-03 21:28 15284
    // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
1193b4be Poul Sander 2025-02-03 21:28 15285
    if (ini_size == 0)
1193b4be Poul Sander 2025-02-03 21:28 15286
        ini_size = strlen(ini_data);
1193b4be Poul Sander 2025-02-03 21:28 15287
    g.SettingsIniData.Buf.resize((int)ini_size + 1);
1193b4be Poul Sander 2025-02-03 21:28 15288
    char* const buf = g.SettingsIniData.Buf.Data;
1193b4be Poul Sander 2025-02-03 21:28 15289
    char* const buf_end = buf + ini_size;
1193b4be Poul Sander 2025-02-03 21:28 15290
    memcpy(buf, ini_data, ini_size);
1193b4be Poul Sander 2025-02-03 21:28 15291
    buf_end[0] = 0;
1193b4be Poul Sander 2025-02-03 21:28 15292
1193b4be Poul Sander 2025-02-03 21:28 15293
    // Call pre-read handlers
1193b4be Poul Sander 2025-02-03 21:28 15294
    // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
1193b4be Poul Sander 2025-02-03 21:28 15295
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 15296
        if (handler.ReadInitFn != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15297
            handler.ReadInitFn(&g, &handler);
1193b4be Poul Sander 2025-02-03 21:28 15298
1193b4be Poul Sander 2025-02-03 21:28 15299
    void* entry_data = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15300
    ImGuiSettingsHandler* entry_handler = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15301
1193b4be Poul Sander 2025-02-03 21:28 15302
    char* line_end = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15303
    for (char* line = buf; line < buf_end; line = line_end + 1)
1193b4be Poul Sander 2025-02-03 21:28 15304
    {
1193b4be Poul Sander 2025-02-03 21:28 15305
        // Skip new lines markers, then find end of the line
1193b4be Poul Sander 2025-02-03 21:28 15306
        while (*line == '\n' || *line == '\r')
1193b4be Poul Sander 2025-02-03 21:28 15307
            line++;
1193b4be Poul Sander 2025-02-03 21:28 15308
        line_end = line;
1193b4be Poul Sander 2025-02-03 21:28 15309
        while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
1193b4be Poul Sander 2025-02-03 21:28 15310
            line_end++;
1193b4be Poul Sander 2025-02-03 21:28 15311
        line_end[0] = 0;
1193b4be Poul Sander 2025-02-03 21:28 15312
        if (line[0] == ';')
1193b4be Poul Sander 2025-02-03 21:28 15313
            continue;
1193b4be Poul Sander 2025-02-03 21:28 15314
        if (line[0] == '[' && line_end > line && line_end[-1] == ']')
1193b4be Poul Sander 2025-02-03 21:28 15315
        {
1193b4be Poul Sander 2025-02-03 21:28 15316
            // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
1193b4be Poul Sander 2025-02-03 21:28 15317
            line_end[-1] = 0;
1193b4be Poul Sander 2025-02-03 21:28 15318
            const char* name_end = line_end - 1;
1193b4be Poul Sander 2025-02-03 21:28 15319
            const char* type_start = line + 1;
1193b4be Poul Sander 2025-02-03 21:28 15320
            char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
1193b4be Poul Sander 2025-02-03 21:28 15321
            const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
1193b4be Poul Sander 2025-02-03 21:28 15322
            if (!type_end || !name_start)
1193b4be Poul Sander 2025-02-03 21:28 15323
                continue;
1193b4be Poul Sander 2025-02-03 21:28 15324
            *type_end = 0; // Overwrite first ']'
1193b4be Poul Sander 2025-02-03 21:28 15325
            name_start++;  // Skip second '['
1193b4be Poul Sander 2025-02-03 21:28 15326
            entry_handler = FindSettingsHandler(type_start);
1193b4be Poul Sander 2025-02-03 21:28 15327
            entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 15328
        }
1193b4be Poul Sander 2025-02-03 21:28 15329
        else if (entry_handler != NULL && entry_data != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15330
        {
1193b4be Poul Sander 2025-02-03 21:28 15331
            // Let type handler parse the line
1193b4be Poul Sander 2025-02-03 21:28 15332
            entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
1193b4be Poul Sander 2025-02-03 21:28 15333
        }
1193b4be Poul Sander 2025-02-03 21:28 15334
    }
1193b4be Poul Sander 2025-02-03 21:28 15335
    g.SettingsLoaded = true;
1193b4be Poul Sander 2025-02-03 21:28 15336
1193b4be Poul Sander 2025-02-03 21:28 15337
    // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
1193b4be Poul Sander 2025-02-03 21:28 15338
    memcpy(buf, ini_data, ini_size);
1193b4be Poul Sander 2025-02-03 21:28 15339
1193b4be Poul Sander 2025-02-03 21:28 15340
    // Call post-read handlers
1193b4be Poul Sander 2025-02-03 21:28 15341
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 15342
        if (handler.ApplyAllFn != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15343
            handler.ApplyAllFn(&g, &handler);
1193b4be Poul Sander 2025-02-03 21:28 15344
}
1193b4be Poul Sander 2025-02-03 21:28 15345
1193b4be Poul Sander 2025-02-03 21:28 15346
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
1193b4be Poul Sander 2025-02-03 21:28 15347
{
1193b4be Poul Sander 2025-02-03 21:28 15348
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15349
    g.SettingsDirtyTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 15350
    if (!ini_filename)
1193b4be Poul Sander 2025-02-03 21:28 15351
        return;
1193b4be Poul Sander 2025-02-03 21:28 15352
1193b4be Poul Sander 2025-02-03 21:28 15353
    size_t ini_data_size = 0;
1193b4be Poul Sander 2025-02-03 21:28 15354
    const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
1193b4be Poul Sander 2025-02-03 21:28 15355
    ImFileHandle f = ImFileOpen(ini_filename, "wt");
1193b4be Poul Sander 2025-02-03 21:28 15356
    if (!f)
1193b4be Poul Sander 2025-02-03 21:28 15357
        return;
1193b4be Poul Sander 2025-02-03 21:28 15358
    ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
1193b4be Poul Sander 2025-02-03 21:28 15359
    ImFileClose(f);
1193b4be Poul Sander 2025-02-03 21:28 15360
}
1193b4be Poul Sander 2025-02-03 21:28 15361
1193b4be Poul Sander 2025-02-03 21:28 15362
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
1193b4be Poul Sander 2025-02-03 21:28 15363
const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
1193b4be Poul Sander 2025-02-03 21:28 15364
{
1193b4be Poul Sander 2025-02-03 21:28 15365
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15366
    g.SettingsDirtyTimer = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 15367
    g.SettingsIniData.Buf.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 15368
    g.SettingsIniData.Buf.push_back(0);
1193b4be Poul Sander 2025-02-03 21:28 15369
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 15370
        handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
1193b4be Poul Sander 2025-02-03 21:28 15371
    if (out_size)
1193b4be Poul Sander 2025-02-03 21:28 15372
        *out_size = (size_t)g.SettingsIniData.size();
1193b4be Poul Sander 2025-02-03 21:28 15373
    return g.SettingsIniData.c_str();
1193b4be Poul Sander 2025-02-03 21:28 15374
}
1193b4be Poul Sander 2025-02-03 21:28 15375
1193b4be Poul Sander 2025-02-03 21:28 15376
ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
1193b4be Poul Sander 2025-02-03 21:28 15377
{
1193b4be Poul Sander 2025-02-03 21:28 15378
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15379
1193b4be Poul Sander 2025-02-03 21:28 15380
    if (g.IO.ConfigDebugIniSettings == false)
1193b4be Poul Sander 2025-02-03 21:28 15381
    {
1193b4be Poul Sander 2025-02-03 21:28 15382
        // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
1193b4be Poul Sander 2025-02-03 21:28 15383
        // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
1193b4be Poul Sander 2025-02-03 21:28 15384
        if (const char* p = strstr(name, "###"))
1193b4be Poul Sander 2025-02-03 21:28 15385
            name = p;
1193b4be Poul Sander 2025-02-03 21:28 15386
    }
1193b4be Poul Sander 2025-02-03 21:28 15387
    const size_t name_len = strlen(name);
1193b4be Poul Sander 2025-02-03 21:28 15388
1193b4be Poul Sander 2025-02-03 21:28 15389
    // Allocate chunk
1193b4be Poul Sander 2025-02-03 21:28 15390
    const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
1193b4be Poul Sander 2025-02-03 21:28 15391
    ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
1193b4be Poul Sander 2025-02-03 21:28 15392
    IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
1193b4be Poul Sander 2025-02-03 21:28 15393
    settings->ID = ImHashStr(name, name_len);
1193b4be Poul Sander 2025-02-03 21:28 15394
    memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
1193b4be Poul Sander 2025-02-03 21:28 15395
1193b4be Poul Sander 2025-02-03 21:28 15396
    return settings;
1193b4be Poul Sander 2025-02-03 21:28 15397
}
1193b4be Poul Sander 2025-02-03 21:28 15398
1193b4be Poul Sander 2025-02-03 21:28 15399
// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
1193b4be Poul Sander 2025-02-03 21:28 15400
// This is called once per window .ini entry + once per newly instantiated window.
1193b4be Poul Sander 2025-02-03 21:28 15401
ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 15402
{
1193b4be Poul Sander 2025-02-03 21:28 15403
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15404
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 15405
        if (settings->ID == id && !settings->WantDelete)
1193b4be Poul Sander 2025-02-03 21:28 15406
            return settings;
1193b4be Poul Sander 2025-02-03 21:28 15407
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 15408
}
1193b4be Poul Sander 2025-02-03 21:28 15409
1193b4be Poul Sander 2025-02-03 21:28 15410
// This is faster if you are holding on a Window already as we don't need to perform a search.
1193b4be Poul Sander 2025-02-03 21:28 15411
ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 15412
{
1193b4be Poul Sander 2025-02-03 21:28 15413
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15414
    if (window->SettingsOffset != -1)
1193b4be Poul Sander 2025-02-03 21:28 15415
        return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
1193b4be Poul Sander 2025-02-03 21:28 15416
    return FindWindowSettingsByID(window->ID);
1193b4be Poul Sander 2025-02-03 21:28 15417
}
1193b4be Poul Sander 2025-02-03 21:28 15418
1193b4be Poul Sander 2025-02-03 21:28 15419
// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
1193b4be Poul Sander 2025-02-03 21:28 15420
void ImGui::ClearWindowSettings(const char* name)
1193b4be Poul Sander 2025-02-03 21:28 15421
{
1193b4be Poul Sander 2025-02-03 21:28 15422
    //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
1193b4be Poul Sander 2025-02-03 21:28 15423
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15424
    ImGuiWindow* window = FindWindowByName(name);
1193b4be Poul Sander 2025-02-03 21:28 15425
    if (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15426
    {
1193b4be Poul Sander 2025-02-03 21:28 15427
        window->Flags |= ImGuiWindowFlags_NoSavedSettings;
1193b4be Poul Sander 2025-02-03 21:28 15428
        InitOrLoadWindowSettings(window, NULL);
1193b4be Poul Sander 2025-02-03 21:28 15429
        if (window->DockId != 0)
1193b4be Poul Sander 2025-02-03 21:28 15430
            DockContextProcessUndockWindow(&g, window, true);
1193b4be Poul Sander 2025-02-03 21:28 15431
    }
1193b4be Poul Sander 2025-02-03 21:28 15432
    if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
1193b4be Poul Sander 2025-02-03 21:28 15433
        settings->WantDelete = true;
1193b4be Poul Sander 2025-02-03 21:28 15434
}
1193b4be Poul Sander 2025-02-03 21:28 15435
1193b4be Poul Sander 2025-02-03 21:28 15436
static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
1193b4be Poul Sander 2025-02-03 21:28 15437
{
1193b4be Poul Sander 2025-02-03 21:28 15438
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 15439
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 15440
        window->SettingsOffset = -1;
1193b4be Poul Sander 2025-02-03 21:28 15441
    g.SettingsWindows.clear();
1193b4be Poul Sander 2025-02-03 21:28 15442
}
1193b4be Poul Sander 2025-02-03 21:28 15443
1193b4be Poul Sander 2025-02-03 21:28 15444
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
1193b4be Poul Sander 2025-02-03 21:28 15445
{
1193b4be Poul Sander 2025-02-03 21:28 15446
    ImGuiID id = ImHashStr(name);
1193b4be Poul Sander 2025-02-03 21:28 15447
    ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
1193b4be Poul Sander 2025-02-03 21:28 15448
    if (settings)
1193b4be Poul Sander 2025-02-03 21:28 15449
        *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
1193b4be Poul Sander 2025-02-03 21:28 15450
    else
1193b4be Poul Sander 2025-02-03 21:28 15451
        settings = ImGui::CreateNewWindowSettings(name);
1193b4be Poul Sander 2025-02-03 21:28 15452
    settings->ID = id;
1193b4be Poul Sander 2025-02-03 21:28 15453
    settings->WantApply = true;
1193b4be Poul Sander 2025-02-03 21:28 15454
    return (void*)settings;
1193b4be Poul Sander 2025-02-03 21:28 15455
}
1193b4be Poul Sander 2025-02-03 21:28 15456
1193b4be Poul Sander 2025-02-03 21:28 15457
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
1193b4be Poul Sander 2025-02-03 21:28 15458
{
1193b4be Poul Sander 2025-02-03 21:28 15459
    ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
1193b4be Poul Sander 2025-02-03 21:28 15460
    int x, y;
1193b4be Poul Sander 2025-02-03 21:28 15461
    int i;
1193b4be Poul Sander 2025-02-03 21:28 15462
    ImU32 u1;
1193b4be Poul Sander 2025-02-03 21:28 15463
    if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)             { settings->Pos = ImVec2ih((short)x, (short)y); }
1193b4be Poul Sander 2025-02-03 21:28 15464
    else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)       { settings->Size = ImVec2ih((short)x, (short)y); }
1193b4be Poul Sander 2025-02-03 21:28 15465
    else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1)   { settings->ViewportId = u1; }
1193b4be Poul Sander 2025-02-03 21:28 15466
    else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
1193b4be Poul Sander 2025-02-03 21:28 15467
    else if (sscanf(line, "Collapsed=%d", &i) == 1)         { settings->Collapsed = (i != 0); }
1193b4be Poul Sander 2025-02-03 21:28 15468
    else if (sscanf(line, "IsChild=%d", &i) == 1)           { settings->IsChild = (i != 0); }
1193b4be Poul Sander 2025-02-03 21:28 15469
    else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2)  { settings->DockId = u1; settings->DockOrder = (short)i; }
1193b4be Poul Sander 2025-02-03 21:28 15470
    else if (sscanf(line, "DockId=0x%X", &u1) == 1)         { settings->DockId = u1; settings->DockOrder = -1; }
1193b4be Poul Sander 2025-02-03 21:28 15471
    else if (sscanf(line, "ClassId=0x%X", &u1) == 1)        { settings->ClassId = u1; }
1193b4be Poul Sander 2025-02-03 21:28 15472
}
1193b4be Poul Sander 2025-02-03 21:28 15473
1193b4be Poul Sander 2025-02-03 21:28 15474
// Apply to existing windows (if any)
1193b4be Poul Sander 2025-02-03 21:28 15475
static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
1193b4be Poul Sander 2025-02-03 21:28 15476
{
1193b4be Poul Sander 2025-02-03 21:28 15477
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 15478
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 15479
        if (settings->WantApply)
1193b4be Poul Sander 2025-02-03 21:28 15480
        {
1193b4be Poul Sander 2025-02-03 21:28 15481
            if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
1193b4be Poul Sander 2025-02-03 21:28 15482
                ApplyWindowSettings(window, settings);
1193b4be Poul Sander 2025-02-03 21:28 15483
            settings->WantApply = false;
1193b4be Poul Sander 2025-02-03 21:28 15484
        }
1193b4be Poul Sander 2025-02-03 21:28 15485
}
1193b4be Poul Sander 2025-02-03 21:28 15486
1193b4be Poul Sander 2025-02-03 21:28 15487
static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
1193b4be Poul Sander 2025-02-03 21:28 15488
{
1193b4be Poul Sander 2025-02-03 21:28 15489
    // Gather data from windows that were active during this session
1193b4be Poul Sander 2025-02-03 21:28 15490
    // (if a window wasn't opened in this session we preserve its settings)
1193b4be Poul Sander 2025-02-03 21:28 15491
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 15492
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 15493
    {
1193b4be Poul Sander 2025-02-03 21:28 15494
        if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
1193b4be Poul Sander 2025-02-03 21:28 15495
            continue;
1193b4be Poul Sander 2025-02-03 21:28 15496
1193b4be Poul Sander 2025-02-03 21:28 15497
        ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 15498
        if (!settings)
1193b4be Poul Sander 2025-02-03 21:28 15499
        {
1193b4be Poul Sander 2025-02-03 21:28 15500
            settings = ImGui::CreateNewWindowSettings(window->Name);
1193b4be Poul Sander 2025-02-03 21:28 15501
            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
1193b4be Poul Sander 2025-02-03 21:28 15502
        }
1193b4be Poul Sander 2025-02-03 21:28 15503
        IM_ASSERT(settings->ID == window->ID);
1193b4be Poul Sander 2025-02-03 21:28 15504
        settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
1193b4be Poul Sander 2025-02-03 21:28 15505
        settings->Size = ImVec2ih(window->SizeFull);
1193b4be Poul Sander 2025-02-03 21:28 15506
        settings->ViewportId = window->ViewportId;
1193b4be Poul Sander 2025-02-03 21:28 15507
        settings->ViewportPos = ImVec2ih(window->ViewportPos);
1193b4be Poul Sander 2025-02-03 21:28 15508
        IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
1193b4be Poul Sander 2025-02-03 21:28 15509
        settings->DockId = window->DockId;
1193b4be Poul Sander 2025-02-03 21:28 15510
        settings->ClassId = window->WindowClass.ClassId;
1193b4be Poul Sander 2025-02-03 21:28 15511
        settings->DockOrder = window->DockOrder;
1193b4be Poul Sander 2025-02-03 21:28 15512
        settings->Collapsed = window->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 15513
        settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set.
1193b4be Poul Sander 2025-02-03 21:28 15514
        settings->WantDelete = false;
1193b4be Poul Sander 2025-02-03 21:28 15515
    }
1193b4be Poul Sander 2025-02-03 21:28 15516
1193b4be Poul Sander 2025-02-03 21:28 15517
    // Write to text buffer
1193b4be Poul Sander 2025-02-03 21:28 15518
    buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
1193b4be Poul Sander 2025-02-03 21:28 15519
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 15520
    {
1193b4be Poul Sander 2025-02-03 21:28 15521
        if (settings->WantDelete)
1193b4be Poul Sander 2025-02-03 21:28 15522
            continue;
1193b4be Poul Sander 2025-02-03 21:28 15523
        const char* settings_name = settings->GetName();
1193b4be Poul Sander 2025-02-03 21:28 15524
        buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
1193b4be Poul Sander 2025-02-03 21:28 15525
        if (settings->IsChild)
1193b4be Poul Sander 2025-02-03 21:28 15526
        {
1193b4be Poul Sander 2025-02-03 21:28 15527
            buf->appendf("IsChild=1\n");
1193b4be Poul Sander 2025-02-03 21:28 15528
            buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 15529
        }
1193b4be Poul Sander 2025-02-03 21:28 15530
        else
1193b4be Poul Sander 2025-02-03 21:28 15531
        {
1193b4be Poul Sander 2025-02-03 21:28 15532
            if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 15533
            {
1193b4be Poul Sander 2025-02-03 21:28 15534
                buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
1193b4be Poul Sander 2025-02-03 21:28 15535
                buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
1193b4be Poul Sander 2025-02-03 21:28 15536
            }
1193b4be Poul Sander 2025-02-03 21:28 15537
            if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 15538
                buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
1193b4be Poul Sander 2025-02-03 21:28 15539
            if (settings->Size.x != 0 || settings->Size.y != 0)
1193b4be Poul Sander 2025-02-03 21:28 15540
                buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 15541
            buf->appendf("Collapsed=%d\n", settings->Collapsed);
1193b4be Poul Sander 2025-02-03 21:28 15542
            if (settings->DockId != 0)
1193b4be Poul Sander 2025-02-03 21:28 15543
            {
1193b4be Poul Sander 2025-02-03 21:28 15544
                //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier.
1193b4be Poul Sander 2025-02-03 21:28 15545
                if (settings->DockOrder == -1)
1193b4be Poul Sander 2025-02-03 21:28 15546
                    buf->appendf("DockId=0x%08X\n", settings->DockId);
1193b4be Poul Sander 2025-02-03 21:28 15547
                else
1193b4be Poul Sander 2025-02-03 21:28 15548
                    buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
1193b4be Poul Sander 2025-02-03 21:28 15549
                if (settings->ClassId != 0)
1193b4be Poul Sander 2025-02-03 21:28 15550
                    buf->appendf("ClassId=0x%08X\n", settings->ClassId);
1193b4be Poul Sander 2025-02-03 21:28 15551
            }
1193b4be Poul Sander 2025-02-03 21:28 15552
        }
1193b4be Poul Sander 2025-02-03 21:28 15553
        buf->append("\n");
1193b4be Poul Sander 2025-02-03 21:28 15554
    }
1193b4be Poul Sander 2025-02-03 21:28 15555
}
1193b4be Poul Sander 2025-02-03 21:28 15556
1193b4be Poul Sander 2025-02-03 21:28 15557
1193b4be Poul Sander 2025-02-03 21:28 15558
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15559
// [SECTION] LOCALIZATION
1193b4be Poul Sander 2025-02-03 21:28 15560
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15561
1193b4be Poul Sander 2025-02-03 21:28 15562
void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
1193b4be Poul Sander 2025-02-03 21:28 15563
{
1193b4be Poul Sander 2025-02-03 21:28 15564
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15565
    for (int n = 0; n < count; n++)
1193b4be Poul Sander 2025-02-03 21:28 15566
        g.LocalizationTable[entries[n].Key] = entries[n].Text;
1193b4be Poul Sander 2025-02-03 21:28 15567
}
1193b4be Poul Sander 2025-02-03 21:28 15568
1193b4be Poul Sander 2025-02-03 21:28 15569
1193b4be Poul Sander 2025-02-03 21:28 15570
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15571
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
1193b4be Poul Sander 2025-02-03 21:28 15572
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15573
// - GetMainViewport()
1193b4be Poul Sander 2025-02-03 21:28 15574
// - FindViewportByID()
1193b4be Poul Sander 2025-02-03 21:28 15575
// - FindViewportByPlatformHandle()
1193b4be Poul Sander 2025-02-03 21:28 15576
// - SetCurrentViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15577
// - SetWindowViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15578
// - GetWindowAlwaysWantOwnViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15579
// - UpdateTryMergeWindowIntoHostViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15580
// - UpdateTryMergeWindowIntoHostViewports() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15581
// - TranslateWindowsInViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15582
// - ScaleWindowsInViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15583
// - FindHoveredViewportFromPlatformWindowStack() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15584
// - UpdateViewportsNewFrame() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15585
// - UpdateViewportsEndFrame() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15586
// - AddUpdateViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15587
// - WindowSelectViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15588
// - WindowSyncOwnedViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15589
// - UpdatePlatformWindows()
1193b4be Poul Sander 2025-02-03 21:28 15590
// - RenderPlatformWindowsDefault()
1193b4be Poul Sander 2025-02-03 21:28 15591
// - FindPlatformMonitorForPos() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15592
// - FindPlatformMonitorForRect() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15593
// - UpdateViewportPlatformMonitor() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15594
// - DestroyPlatformWindow() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 15595
// - DestroyPlatformWindows()
1193b4be Poul Sander 2025-02-03 21:28 15596
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 15597
1193b4be Poul Sander 2025-02-03 21:28 15598
ImGuiViewport* ImGui::GetMainViewport()
1193b4be Poul Sander 2025-02-03 21:28 15599
{
1193b4be Poul Sander 2025-02-03 21:28 15600
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15601
    return g.Viewports[0];
1193b4be Poul Sander 2025-02-03 21:28 15602
}
1193b4be Poul Sander 2025-02-03 21:28 15603
1193b4be Poul Sander 2025-02-03 21:28 15604
// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
1193b4be Poul Sander 2025-02-03 21:28 15605
ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 15606
{
1193b4be Poul Sander 2025-02-03 21:28 15607
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15608
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 15609
        if (viewport->ID == id)
1193b4be Poul Sander 2025-02-03 21:28 15610
            return viewport;
1193b4be Poul Sander 2025-02-03 21:28 15611
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 15612
}
1193b4be Poul Sander 2025-02-03 21:28 15613
1193b4be Poul Sander 2025-02-03 21:28 15614
ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
1193b4be Poul Sander 2025-02-03 21:28 15615
{
1193b4be Poul Sander 2025-02-03 21:28 15616
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15617
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 15618
        if (viewport->PlatformHandle == platform_handle)
1193b4be Poul Sander 2025-02-03 21:28 15619
            return viewport;
1193b4be Poul Sander 2025-02-03 21:28 15620
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 15621
}
1193b4be Poul Sander 2025-02-03 21:28 15622
1193b4be Poul Sander 2025-02-03 21:28 15623
void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 15624
{
1193b4be Poul Sander 2025-02-03 21:28 15625
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15626
    (void)current_window;
1193b4be Poul Sander 2025-02-03 21:28 15627
1193b4be Poul Sander 2025-02-03 21:28 15628
    if (viewport)
1193b4be Poul Sander 2025-02-03 21:28 15629
        viewport->LastFrameActive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 15630
    if (g.CurrentViewport == viewport)
1193b4be Poul Sander 2025-02-03 21:28 15631
        return;
1193b4be Poul Sander 2025-02-03 21:28 15632
    g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 15633
    g.CurrentViewport = viewport;
1193b4be Poul Sander 2025-02-03 21:28 15634
    //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
1193b4be Poul Sander 2025-02-03 21:28 15635
1193b4be Poul Sander 2025-02-03 21:28 15636
    // Notify platform layer of viewport changes
1193b4be Poul Sander 2025-02-03 21:28 15637
    // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
1193b4be Poul Sander 2025-02-03 21:28 15638
    if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
1193b4be Poul Sander 2025-02-03 21:28 15639
        g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
1193b4be Poul Sander 2025-02-03 21:28 15640
}
1193b4be Poul Sander 2025-02-03 21:28 15641
1193b4be Poul Sander 2025-02-03 21:28 15642
void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 15643
{
1193b4be Poul Sander 2025-02-03 21:28 15644
    // Abandon viewport
1193b4be Poul Sander 2025-02-03 21:28 15645
    if (window->ViewportOwned && window->Viewport->Window == window)
1193b4be Poul Sander 2025-02-03 21:28 15646
        window->Viewport->Size = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 15647
1193b4be Poul Sander 2025-02-03 21:28 15648
    window->Viewport = viewport;
1193b4be Poul Sander 2025-02-03 21:28 15649
    window->ViewportId = viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 15650
    window->ViewportOwned = (viewport->Window == window);
1193b4be Poul Sander 2025-02-03 21:28 15651
}
1193b4be Poul Sander 2025-02-03 21:28 15652
1193b4be Poul Sander 2025-02-03 21:28 15653
static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 15654
{
1193b4be Poul Sander 2025-02-03 21:28 15655
    // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own.
1193b4be Poul Sander 2025-02-03 21:28 15656
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15657
    if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
1193b4be Poul Sander 2025-02-03 21:28 15658
        if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
1193b4be Poul Sander 2025-02-03 21:28 15659
            if (!window->DockIsActive)
1193b4be Poul Sander 2025-02-03 21:28 15660
                if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 15661
                    if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
1193b4be Poul Sander 2025-02-03 21:28 15662
                        return true;
1193b4be Poul Sander 2025-02-03 21:28 15663
    return false;
1193b4be Poul Sander 2025-02-03 21:28 15664
}
1193b4be Poul Sander 2025-02-03 21:28 15665
1193b4be Poul Sander 2025-02-03 21:28 15666
static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 15667
{
1193b4be Poul Sander 2025-02-03 21:28 15668
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15669
    if (window->Viewport == viewport)
1193b4be Poul Sander 2025-02-03 21:28 15670
        return false;
1193b4be Poul Sander 2025-02-03 21:28 15671
    if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
1193b4be Poul Sander 2025-02-03 21:28 15672
        return false;
1193b4be Poul Sander 2025-02-03 21:28 15673
    if ((viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0)
1193b4be Poul Sander 2025-02-03 21:28 15674
        return false;
1193b4be Poul Sander 2025-02-03 21:28 15675
    if (!viewport->GetMainRect().Contains(window->Rect()))
1193b4be Poul Sander 2025-02-03 21:28 15676
        return false;
1193b4be Poul Sander 2025-02-03 21:28 15677
    if (GetWindowAlwaysWantOwnViewport(window))
1193b4be Poul Sander 2025-02-03 21:28 15678
        return false;
1193b4be Poul Sander 2025-02-03 21:28 15679
1193b4be Poul Sander 2025-02-03 21:28 15680
    // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could..
1193b4be Poul Sander 2025-02-03 21:28 15681
    for (ImGuiWindow* window_behind : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 15682
    {
1193b4be Poul Sander 2025-02-03 21:28 15683
        if (window_behind == window)
1193b4be Poul Sander 2025-02-03 21:28 15684
            break;
1193b4be Poul Sander 2025-02-03 21:28 15685
        if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 15686
            if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
1193b4be Poul Sander 2025-02-03 21:28 15687
                return false;
1193b4be Poul Sander 2025-02-03 21:28 15688
    }
1193b4be Poul Sander 2025-02-03 21:28 15689
1193b4be Poul Sander 2025-02-03 21:28 15690
    // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
1193b4be Poul Sander 2025-02-03 21:28 15691
    ImGuiViewportP* old_viewport = window->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 15692
    if (window->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 15693
        for (int n = 0; n < g.Windows.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 15694
            if (g.Windows[n]->Viewport == old_viewport)
1193b4be Poul Sander 2025-02-03 21:28 15695
                SetWindowViewport(g.Windows[n], viewport);
1193b4be Poul Sander 2025-02-03 21:28 15696
    SetWindowViewport(window, viewport);
1193b4be Poul Sander 2025-02-03 21:28 15697
    BringWindowToDisplayFront(window);
1193b4be Poul Sander 2025-02-03 21:28 15698
1193b4be Poul Sander 2025-02-03 21:28 15699
    return true;
1193b4be Poul Sander 2025-02-03 21:28 15700
}
1193b4be Poul Sander 2025-02-03 21:28 15701
1193b4be Poul Sander 2025-02-03 21:28 15702
// FIXME: handle 0 to N host viewports
1193b4be Poul Sander 2025-02-03 21:28 15703
static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 15704
{
1193b4be Poul Sander 2025-02-03 21:28 15705
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15706
    return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
1193b4be Poul Sander 2025-02-03 21:28 15707
}
1193b4be Poul Sander 2025-02-03 21:28 15708
1193b4be Poul Sander 2025-02-03 21:28 15709
// Translate Dear ImGui windows when a Host Viewport has been moved
1193b4be Poul Sander 2025-02-03 21:28 15710
// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
1193b4be Poul Sander 2025-02-03 21:28 15711
void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size)
1193b4be Poul Sander 2025-02-03 21:28 15712
{
1193b4be Poul Sander 2025-02-03 21:28 15713
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15714
    IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
1193b4be Poul Sander 2025-02-03 21:28 15715
1193b4be Poul Sander 2025-02-03 21:28 15716
    // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
1193b4be Poul Sander 2025-02-03 21:28 15717
    // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
1193b4be Poul Sander 2025-02-03 21:28 15718
    // 2) If it's not going to fit into the new size, keep it at same absolute position.
1193b4be Poul Sander 2025-02-03 21:28 15719
    // One problem with this is that most Win32 applications doesn't update their render while dragging,
1193b4be Poul Sander 2025-02-03 21:28 15720
    // and so the window will appear to teleport when releasing the mouse.
1193b4be Poul Sander 2025-02-03 21:28 15721
    const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
1193b4be Poul Sander 2025-02-03 21:28 15722
    ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
1193b4be Poul Sander 2025-02-03 21:28 15723
    ImVec2 delta_pos = new_pos - old_pos;
1193b4be Poul Sander 2025-02-03 21:28 15724
    for (ImGuiWindow* window : g.Windows) // FIXME-OPT
1193b4be Poul Sander 2025-02-03 21:28 15725
        if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect()))))
1193b4be Poul Sander 2025-02-03 21:28 15726
            TranslateWindow(window, delta_pos);
1193b4be Poul Sander 2025-02-03 21:28 15727
}
1193b4be Poul Sander 2025-02-03 21:28 15728
1193b4be Poul Sander 2025-02-03 21:28 15729
// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
1193b4be Poul Sander 2025-02-03 21:28 15730
void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
1193b4be Poul Sander 2025-02-03 21:28 15731
{
1193b4be Poul Sander 2025-02-03 21:28 15732
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15733
    if (viewport->Window)
1193b4be Poul Sander 2025-02-03 21:28 15734
    {
1193b4be Poul Sander 2025-02-03 21:28 15735
        ScaleWindow(viewport->Window, scale);
1193b4be Poul Sander 2025-02-03 21:28 15736
    }
1193b4be Poul Sander 2025-02-03 21:28 15737
    else
1193b4be Poul Sander 2025-02-03 21:28 15738
    {
1193b4be Poul Sander 2025-02-03 21:28 15739
        for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 15740
            if (window->Viewport == viewport)
1193b4be Poul Sander 2025-02-03 21:28 15741
                ScaleWindow(window, scale);
1193b4be Poul Sander 2025-02-03 21:28 15742
    }
1193b4be Poul Sander 2025-02-03 21:28 15743
}
1193b4be Poul Sander 2025-02-03 21:28 15744
1193b4be Poul Sander 2025-02-03 21:28 15745
// If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
1193b4be Poul Sander 2025-02-03 21:28 15746
// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
1193b4be Poul Sander 2025-02-03 21:28 15747
// B) It requires Platform_GetWindowFocus to be implemented by backend.
1193b4be Poul Sander 2025-02-03 21:28 15748
ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos)
1193b4be Poul Sander 2025-02-03 21:28 15749
{
1193b4be Poul Sander 2025-02-03 21:28 15750
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15751
    ImGuiViewportP* best_candidate = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15752
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 15753
        if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
1193b4be Poul Sander 2025-02-03 21:28 15754
            if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount)
1193b4be Poul Sander 2025-02-03 21:28 15755
                best_candidate = viewport;
1193b4be Poul Sander 2025-02-03 21:28 15756
    return best_candidate;
1193b4be Poul Sander 2025-02-03 21:28 15757
}
1193b4be Poul Sander 2025-02-03 21:28 15758
1193b4be Poul Sander 2025-02-03 21:28 15759
// Update viewports and monitor infos
1193b4be Poul Sander 2025-02-03 21:28 15760
// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.
1193b4be Poul Sander 2025-02-03 21:28 15761
static void ImGui::UpdateViewportsNewFrame()
1193b4be Poul Sander 2025-02-03 21:28 15762
{
1193b4be Poul Sander 2025-02-03 21:28 15763
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 15764
    IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
1193b4be Poul Sander 2025-02-03 21:28 15765
1193b4be Poul Sander 2025-02-03 21:28 15766
    // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
1193b4be Poul Sander 2025-02-03 21:28 15767
    // Update Focused status
1193b4be Poul Sander 2025-02-03 21:28 15768
    const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
1193b4be Poul Sander 2025-02-03 21:28 15769
    if (viewports_enabled)
1193b4be Poul Sander 2025-02-03 21:28 15770
    {
1193b4be Poul Sander 2025-02-03 21:28 15771
        ImGuiViewportP* focused_viewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15772
        for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 15773
        {
1193b4be Poul Sander 2025-02-03 21:28 15774
            const bool platform_funcs_available = viewport->PlatformWindowCreated;
1193b4be Poul Sander 2025-02-03 21:28 15775
            if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
1193b4be Poul Sander 2025-02-03 21:28 15776
            {
1193b4be Poul Sander 2025-02-03 21:28 15777
                bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15778
                if (is_minimized)
1193b4be Poul Sander 2025-02-03 21:28 15779
                    viewport->Flags |= ImGuiViewportFlags_IsMinimized;
1193b4be Poul Sander 2025-02-03 21:28 15780
                else
1193b4be Poul Sander 2025-02-03 21:28 15781
                    viewport->Flags &= ~ImGuiViewportFlags_IsMinimized;
1193b4be Poul Sander 2025-02-03 21:28 15782
            }
1193b4be Poul Sander 2025-02-03 21:28 15783
1193b4be Poul Sander 2025-02-03 21:28 15784
            // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
1193b4be Poul Sander 2025-02-03 21:28 15785
            // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
1193b4be Poul Sander 2025-02-03 21:28 15786
            if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available)
1193b4be Poul Sander 2025-02-03 21:28 15787
            {
1193b4be Poul Sander 2025-02-03 21:28 15788
                bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15789
                if (is_focused)
1193b4be Poul Sander 2025-02-03 21:28 15790
                    viewport->Flags |= ImGuiViewportFlags_IsFocused;
1193b4be Poul Sander 2025-02-03 21:28 15791
                else
1193b4be Poul Sander 2025-02-03 21:28 15792
                    viewport->Flags &= ~ImGuiViewportFlags_IsFocused;
1193b4be Poul Sander 2025-02-03 21:28 15793
                if (is_focused)
1193b4be Poul Sander 2025-02-03 21:28 15794
                    focused_viewport = viewport;
1193b4be Poul Sander 2025-02-03 21:28 15795
            }
1193b4be Poul Sander 2025-02-03 21:28 15796
        }
1193b4be Poul Sander 2025-02-03 21:28 15797
1193b4be Poul Sander 2025-02-03 21:28 15798
        // Focused viewport has changed?
1193b4be Poul Sander 2025-02-03 21:28 15799
        if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID)
1193b4be Poul Sander 2025-02-03 21:28 15800
        {
1193b4be Poul Sander 2025-02-03 21:28 15801
            IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X, attempting to apply our focus.\n", g.PlatformLastFocusedViewportId, focused_viewport->ID);
1193b4be Poul Sander 2025-02-03 21:28 15802
            const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId);
1193b4be Poul Sander 2025-02-03 21:28 15803
            const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false);
1193b4be Poul Sander 2025-02-03 21:28 15804
1193b4be Poul Sander 2025-02-03 21:28 15805
            // Store a tag so we can infer z-order easily from all our windows
1193b4be Poul Sander 2025-02-03 21:28 15806
            // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag
1193b4be Poul Sander 2025-02-03 21:28 15807
            // will keep the front most stamp instead of losing it back to their parent viewport.
1193b4be Poul Sander 2025-02-03 21:28 15808
            if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
1193b4be Poul Sander 2025-02-03 21:28 15809
                focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
1193b4be Poul Sander 2025-02-03 21:28 15810
            g.PlatformLastFocusedViewportId = focused_viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 15811
1193b4be Poul Sander 2025-02-03 21:28 15812
            // Focus associated dear imgui window
1193b4be Poul Sander 2025-02-03 21:28 15813
            // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299)
1193b4be Poul Sander 2025-02-03 21:28 15814
            // - if focus didn't happen because we destroyed another window (#6462)
1193b4be Poul Sander 2025-02-03 21:28 15815
            // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well.
1193b4be Poul Sander 2025-02-03 21:28 15816
            const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed;
1193b4be Poul Sander 2025-02-03 21:28 15817
            if (apply_imgui_focus_on_focused_viewport)
1193b4be Poul Sander 2025-02-03 21:28 15818
            {
1193b4be Poul Sander 2025-02-03 21:28 15819
                focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus.
1193b4be Poul Sander 2025-02-03 21:28 15820
                ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild;
1193b4be Poul Sander 2025-02-03 21:28 15821
                if (focused_viewport->Window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15822
                    FocusWindow(focused_viewport->Window, focus_request_flags);
1193b4be Poul Sander 2025-02-03 21:28 15823
                else if (focused_viewport->LastFocusedHadNavWindow)
1193b4be Poul Sander 2025-02-03 21:28 15824
                    FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport
1193b4be Poul Sander 2025-02-03 21:28 15825
                else
1193b4be Poul Sander 2025-02-03 21:28 15826
                    FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused
1193b4be Poul Sander 2025-02-03 21:28 15827
            }
1193b4be Poul Sander 2025-02-03 21:28 15828
        }
1193b4be Poul Sander 2025-02-03 21:28 15829
        if (focused_viewport)
1193b4be Poul Sander 2025-02-03 21:28 15830
            focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport);
1193b4be Poul Sander 2025-02-03 21:28 15831
    }
1193b4be Poul Sander 2025-02-03 21:28 15832
1193b4be Poul Sander 2025-02-03 21:28 15833
    // Create/update main viewport with current platform position.
1193b4be Poul Sander 2025-02-03 21:28 15834
    // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
1193b4be Poul Sander 2025-02-03 21:28 15835
    ImGuiViewportP* main_viewport = g.Viewports[0];
1193b4be Poul Sander 2025-02-03 21:28 15836
    IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
1193b4be Poul Sander 2025-02-03 21:28 15837
    IM_ASSERT(main_viewport->Window == NULL);
1193b4be Poul Sander 2025-02-03 21:28 15838
    ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 15839
    ImVec2 main_viewport_size = g.IO.DisplaySize;
1193b4be Poul Sander 2025-02-03 21:28 15840
    if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized))
1193b4be Poul Sander 2025-02-03 21:28 15841
    {
1193b4be Poul Sander 2025-02-03 21:28 15842
        main_viewport_pos = main_viewport->Pos;    // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path)
1193b4be Poul Sander 2025-02-03 21:28 15843
        main_viewport_size = main_viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 15844
    }
1193b4be Poul Sander 2025-02-03 21:28 15845
    AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);
1193b4be Poul Sander 2025-02-03 21:28 15846
1193b4be Poul Sander 2025-02-03 21:28 15847
    g.CurrentDpiScale = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 15848
    g.CurrentViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15849
    g.MouseViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15850
    for (int n = 0; n < g.Viewports.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 15851
    {
1193b4be Poul Sander 2025-02-03 21:28 15852
        ImGuiViewportP* viewport = g.Viewports[n];
1193b4be Poul Sander 2025-02-03 21:28 15853
        viewport->Idx = n;
1193b4be Poul Sander 2025-02-03 21:28 15854
1193b4be Poul Sander 2025-02-03 21:28 15855
        // Erase unused viewports
1193b4be Poul Sander 2025-02-03 21:28 15856
        if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
1193b4be Poul Sander 2025-02-03 21:28 15857
        {
1193b4be Poul Sander 2025-02-03 21:28 15858
            DestroyViewport(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15859
            n--;
1193b4be Poul Sander 2025-02-03 21:28 15860
            continue;
1193b4be Poul Sander 2025-02-03 21:28 15861
        }
1193b4be Poul Sander 2025-02-03 21:28 15862
1193b4be Poul Sander 2025-02-03 21:28 15863
        const bool platform_funcs_available = viewport->PlatformWindowCreated;
1193b4be Poul Sander 2025-02-03 21:28 15864
        if (viewports_enabled)
1193b4be Poul Sander 2025-02-03 21:28 15865
        {
1193b4be Poul Sander 2025-02-03 21:28 15866
            // Update Position and Size (from Platform Window to ImGui) if requested.
1193b4be Poul Sander 2025-02-03 21:28 15867
            // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
1193b4be Poul Sander 2025-02-03 21:28 15868
            if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available)
1193b4be Poul Sander 2025-02-03 21:28 15869
            {
1193b4be Poul Sander 2025-02-03 21:28 15870
                // Viewport->WorkPos and WorkSize will be updated below
1193b4be Poul Sander 2025-02-03 21:28 15871
                if (viewport->PlatformRequestMove)
1193b4be Poul Sander 2025-02-03 21:28 15872
                    viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15873
                if (viewport->PlatformRequestResize)
1193b4be Poul Sander 2025-02-03 21:28 15874
                    viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15875
            }
1193b4be Poul Sander 2025-02-03 21:28 15876
        }
1193b4be Poul Sander 2025-02-03 21:28 15877
1193b4be Poul Sander 2025-02-03 21:28 15878
        // Update/copy monitor info
1193b4be Poul Sander 2025-02-03 21:28 15879
        UpdateViewportPlatformMonitor(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15880
1193b4be Poul Sander 2025-02-03 21:28 15881
        // Lock down space taken by menu bars and status bars + query initial insets from backend
1193b4be Poul Sander 2025-02-03 21:28 15882
        // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
1193b4be Poul Sander 2025-02-03 21:28 15883
        viewport->WorkInsetMin = viewport->BuildWorkInsetMin;
1193b4be Poul Sander 2025-02-03 21:28 15884
        viewport->WorkInsetMax = viewport->BuildWorkInsetMax;
1193b4be Poul Sander 2025-02-03 21:28 15885
        viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 15886
        if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available)
1193b4be Poul Sander 2025-02-03 21:28 15887
        {
1193b4be Poul Sander 2025-02-03 21:28 15888
            ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15889
            IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 15890
            viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y);
1193b4be Poul Sander 2025-02-03 21:28 15891
            viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w);
1193b4be Poul Sander 2025-02-03 21:28 15892
        }
1193b4be Poul Sander 2025-02-03 21:28 15893
        viewport->UpdateWorkRect();
1193b4be Poul Sander 2025-02-03 21:28 15894
1193b4be Poul Sander 2025-02-03 21:28 15895
        // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
1193b4be Poul Sander 2025-02-03 21:28 15896
        viewport->Alpha = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 15897
1193b4be Poul Sander 2025-02-03 21:28 15898
        // Translate Dear ImGui windows when a Host Viewport has been moved
1193b4be Poul Sander 2025-02-03 21:28 15899
        // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
1193b4be Poul Sander 2025-02-03 21:28 15900
        const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
1193b4be Poul Sander 2025-02-03 21:28 15901
        if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
1193b4be Poul Sander 2025-02-03 21:28 15902
            TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size);
1193b4be Poul Sander 2025-02-03 21:28 15903
1193b4be Poul Sander 2025-02-03 21:28 15904
        // Update DPI scale
1193b4be Poul Sander 2025-02-03 21:28 15905
        float new_dpi_scale;
1193b4be Poul Sander 2025-02-03 21:28 15906
        if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
1193b4be Poul Sander 2025-02-03 21:28 15907
            new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
1193b4be Poul Sander 2025-02-03 21:28 15908
        else if (viewport->PlatformMonitor != -1)
1193b4be Poul Sander 2025-02-03 21:28 15909
            new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
1193b4be Poul Sander 2025-02-03 21:28 15910
        else
1193b4be Poul Sander 2025-02-03 21:28 15911
            new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 15912
        if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
1193b4be Poul Sander 2025-02-03 21:28 15913
        {
1193b4be Poul Sander 2025-02-03 21:28 15914
            float scale_factor = new_dpi_scale / viewport->DpiScale;
1193b4be Poul Sander 2025-02-03 21:28 15915
            if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
1193b4be Poul Sander 2025-02-03 21:28 15916
                ScaleWindowsInViewport(viewport, scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 15917
            //if (viewport == GetMainViewport())
1193b4be Poul Sander 2025-02-03 21:28 15918
            //    g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 15919
1193b4be Poul Sander 2025-02-03 21:28 15920
            // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
1193b4be Poul Sander 2025-02-03 21:28 15921
            // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
1193b4be Poul Sander 2025-02-03 21:28 15922
            // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.)
1193b4be Poul Sander 2025-02-03 21:28 15923
            //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
1193b4be Poul Sander 2025-02-03 21:28 15924
            //    g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor);
1193b4be Poul Sander 2025-02-03 21:28 15925
        }
1193b4be Poul Sander 2025-02-03 21:28 15926
        viewport->DpiScale = new_dpi_scale;
1193b4be Poul Sander 2025-02-03 21:28 15927
    }
1193b4be Poul Sander 2025-02-03 21:28 15928
1193b4be Poul Sander 2025-02-03 21:28 15929
    // Update fallback monitor
1193b4be Poul Sander 2025-02-03 21:28 15930
    g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 15931
    if (g.PlatformIO.Monitors.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 15932
    {
1193b4be Poul Sander 2025-02-03 21:28 15933
        ImGuiPlatformMonitor* monitor = &g.FallbackMonitor;
1193b4be Poul Sander 2025-02-03 21:28 15934
        monitor->MainPos = main_viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 15935
        monitor->MainSize = main_viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 15936
        monitor->WorkPos = main_viewport->WorkPos;
1193b4be Poul Sander 2025-02-03 21:28 15937
        monitor->WorkSize = main_viewport->WorkSize;
1193b4be Poul Sander 2025-02-03 21:28 15938
        monitor->DpiScale = main_viewport->DpiScale;
1193b4be Poul Sander 2025-02-03 21:28 15939
        g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos);
1193b4be Poul Sander 2025-02-03 21:28 15940
        g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize);
1193b4be Poul Sander 2025-02-03 21:28 15941
    }
1193b4be Poul Sander 2025-02-03 21:28 15942
    else
1193b4be Poul Sander 2025-02-03 21:28 15943
    {
1193b4be Poul Sander 2025-02-03 21:28 15944
        g.FallbackMonitor = g.PlatformIO.Monitors[0];
1193b4be Poul Sander 2025-02-03 21:28 15945
    }
1193b4be Poul Sander 2025-02-03 21:28 15946
    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
1193b4be Poul Sander 2025-02-03 21:28 15947
    {
1193b4be Poul Sander 2025-02-03 21:28 15948
        g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos);
1193b4be Poul Sander 2025-02-03 21:28 15949
        g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize);
1193b4be Poul Sander 2025-02-03 21:28 15950
    }
1193b4be Poul Sander 2025-02-03 21:28 15951
1193b4be Poul Sander 2025-02-03 21:28 15952
    if (!viewports_enabled)
1193b4be Poul Sander 2025-02-03 21:28 15953
    {
1193b4be Poul Sander 2025-02-03 21:28 15954
        g.MouseViewport = main_viewport;
1193b4be Poul Sander 2025-02-03 21:28 15955
        return;
1193b4be Poul Sander 2025-02-03 21:28 15956
    }
1193b4be Poul Sander 2025-02-03 21:28 15957
1193b4be Poul Sander 2025-02-03 21:28 15958
    // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
1193b4be Poul Sander 2025-02-03 21:28 15959
    // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
1193b4be Poul Sander 2025-02-03 21:28 15960
    ImGuiViewportP* viewport_hovered = NULL;
1193b4be Poul Sander 2025-02-03 21:28 15961
    if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
1193b4be Poul Sander 2025-02-03 21:28 15962
    {
1193b4be Poul Sander 2025-02-03 21:28 15963
        viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 15964
        if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
1193b4be Poul Sander 2025-02-03 21:28 15965
            viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback.
1193b4be Poul Sander 2025-02-03 21:28 15966
    }
1193b4be Poul Sander 2025-02-03 21:28 15967
    else
1193b4be Poul Sander 2025-02-03 21:28 15968
    {
1193b4be Poul Sander 2025-02-03 21:28 15969
        // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
1193b4be Poul Sander 2025-02-03 21:28 15970
        // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
1193b4be Poul Sander 2025-02-03 21:28 15971
        // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.
1193b4be Poul Sander 2025-02-03 21:28 15972
        // C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
1193b4be Poul Sander 2025-02-03 21:28 15973
        viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
1193b4be Poul Sander 2025-02-03 21:28 15974
    }
1193b4be Poul Sander 2025-02-03 21:28 15975
    if (viewport_hovered != NULL)
1193b4be Poul Sander 2025-02-03 21:28 15976
        g.MouseLastHoveredViewport = viewport_hovered;
1193b4be Poul Sander 2025-02-03 21:28 15977
    else if (g.MouseLastHoveredViewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 15978
        g.MouseLastHoveredViewport = g.Viewports[0];
1193b4be Poul Sander 2025-02-03 21:28 15979
1193b4be Poul Sander 2025-02-03 21:28 15980
    // Update mouse reference viewport
1193b4be Poul Sander 2025-02-03 21:28 15981
    // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
1193b4be Poul Sander 2025-02-03 21:28 15982
    // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)
1193b4be Poul Sander 2025-02-03 21:28 15983
    if (g.MovingWindow && g.MovingWindow->Viewport)
1193b4be Poul Sander 2025-02-03 21:28 15984
        g.MouseViewport = g.MovingWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 15985
    else
1193b4be Poul Sander 2025-02-03 21:28 15986
        g.MouseViewport = g.MouseLastHoveredViewport;
1193b4be Poul Sander 2025-02-03 21:28 15987
1193b4be Poul Sander 2025-02-03 21:28 15988
    // When dragging something, always refer to the last hovered viewport.
1193b4be Poul Sander 2025-02-03 21:28 15989
    // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
1193b4be Poul Sander 2025-02-03 21:28 15990
    // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info)
1193b4be Poul Sander 2025-02-03 21:28 15991
    // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release.
1193b4be Poul Sander 2025-02-03 21:28 15992
    // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that.
1193b4be Poul Sander 2025-02-03 21:28 15993
    const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
1193b4be Poul Sander 2025-02-03 21:28 15994
    if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
1193b4be Poul Sander 2025-02-03 21:28 15995
        viewport_hovered = g.MouseLastHoveredViewport;
1193b4be Poul Sander 2025-02-03 21:28 15996
    if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
1193b4be Poul Sander 2025-02-03 21:28 15997
        if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
1193b4be Poul Sander 2025-02-03 21:28 15998
            g.MouseViewport = viewport_hovered;
1193b4be Poul Sander 2025-02-03 21:28 15999
1193b4be Poul Sander 2025-02-03 21:28 16000
    IM_ASSERT(g.MouseViewport != NULL);
1193b4be Poul Sander 2025-02-03 21:28 16001
}
1193b4be Poul Sander 2025-02-03 21:28 16002
1193b4be Poul Sander 2025-02-03 21:28 16003
// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
1193b4be Poul Sander 2025-02-03 21:28 16004
static void ImGui::UpdateViewportsEndFrame()
1193b4be Poul Sander 2025-02-03 21:28 16005
{
1193b4be Poul Sander 2025-02-03 21:28 16006
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16007
    g.PlatformIO.Viewports.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 16008
    for (int i = 0; i < g.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16009
    {
1193b4be Poul Sander 2025-02-03 21:28 16010
        ImGuiViewportP* viewport = g.Viewports[i];
1193b4be Poul Sander 2025-02-03 21:28 16011
        viewport->LastPos = viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 16012
        viewport->LastSize = viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 16013
        if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 16014
            if (i > 0) // Always include main viewport in the list
1193b4be Poul Sander 2025-02-03 21:28 16015
                continue;
1193b4be Poul Sander 2025-02-03 21:28 16016
        if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
1193b4be Poul Sander 2025-02-03 21:28 16017
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16018
        if (i > 0)
1193b4be Poul Sander 2025-02-03 21:28 16019
            IM_ASSERT(viewport->Window != NULL);
1193b4be Poul Sander 2025-02-03 21:28 16020
        g.PlatformIO.Viewports.push_back(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16021
    }
1193b4be Poul Sander 2025-02-03 21:28 16022
    g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
1193b4be Poul Sander 2025-02-03 21:28 16023
}
1193b4be Poul Sander 2025-02-03 21:28 16024
1193b4be Poul Sander 2025-02-03 21:28 16025
// FIXME: We should ideally refactor the system to call this every frame (we currently don't)
1193b4be Poul Sander 2025-02-03 21:28 16026
ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 16027
{
1193b4be Poul Sander 2025-02-03 21:28 16028
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16029
    IM_ASSERT(id != 0);
1193b4be Poul Sander 2025-02-03 21:28 16030
1193b4be Poul Sander 2025-02-03 21:28 16031
    flags |= ImGuiViewportFlags_IsPlatformWindow;
1193b4be Poul Sander 2025-02-03 21:28 16032
    if (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 16033
    {
1193b4be Poul Sander 2025-02-03 21:28 16034
        if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window)
1193b4be Poul Sander 2025-02-03 21:28 16035
            flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
1193b4be Poul Sander 2025-02-03 21:28 16036
        if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
1193b4be Poul Sander 2025-02-03 21:28 16037
            flags |= ImGuiViewportFlags_NoInputs;
1193b4be Poul Sander 2025-02-03 21:28 16038
        if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
1193b4be Poul Sander 2025-02-03 21:28 16039
            flags |= ImGuiViewportFlags_NoFocusOnAppearing;
1193b4be Poul Sander 2025-02-03 21:28 16040
    }
1193b4be Poul Sander 2025-02-03 21:28 16041
1193b4be Poul Sander 2025-02-03 21:28 16042
    ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
1193b4be Poul Sander 2025-02-03 21:28 16043
    if (viewport)
1193b4be Poul Sander 2025-02-03 21:28 16044
    {
1193b4be Poul Sander 2025-02-03 21:28 16045
        // Always update for main viewport as we are already pulling correct platform pos/size (see #4900)
1193b4be Poul Sander 2025-02-03 21:28 16046
        if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 16047
            viewport->Pos = pos;
1193b4be Poul Sander 2025-02-03 21:28 16048
        if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 16049
            viewport->Size = size;
1193b4be Poul Sander 2025-02-03 21:28 16050
        viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags
1193b4be Poul Sander 2025-02-03 21:28 16051
    }
1193b4be Poul Sander 2025-02-03 21:28 16052
    else
1193b4be Poul Sander 2025-02-03 21:28 16053
    {
1193b4be Poul Sander 2025-02-03 21:28 16054
        // New viewport
1193b4be Poul Sander 2025-02-03 21:28 16055
        viewport = IM_NEW(ImGuiViewportP)();
1193b4be Poul Sander 2025-02-03 21:28 16056
        viewport->ID = id;
1193b4be Poul Sander 2025-02-03 21:28 16057
        viewport->Idx = g.Viewports.Size;
1193b4be Poul Sander 2025-02-03 21:28 16058
        viewport->Pos = viewport->LastPos = pos;
1193b4be Poul Sander 2025-02-03 21:28 16059
        viewport->Size = viewport->LastSize = size;
1193b4be Poul Sander 2025-02-03 21:28 16060
        viewport->Flags = flags;
1193b4be Poul Sander 2025-02-03 21:28 16061
        UpdateViewportPlatformMonitor(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16062
        g.Viewports.push_back(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16063
        g.ViewportCreatedCount++;
1193b4be Poul Sander 2025-02-03 21:28 16064
        IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : "<NULL>");
1193b4be Poul Sander 2025-02-03 21:28 16065
1193b4be Poul Sander 2025-02-03 21:28 16066
        // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
1193b4be Poul Sander 2025-02-03 21:28 16067
        // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
1193b4be Poul Sander 2025-02-03 21:28 16068
        g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
1193b4be Poul Sander 2025-02-03 21:28 16069
        g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
1193b4be Poul Sander 2025-02-03 21:28 16070
        g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
1193b4be Poul Sander 2025-02-03 21:28 16071
        g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 16072
1193b4be Poul Sander 2025-02-03 21:28 16073
        // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
1193b4be Poul Sander 2025-02-03 21:28 16074
        // This is so we can select an appropriate font size on the first frame of our window lifetime
1193b4be Poul Sander 2025-02-03 21:28 16075
        if (viewport->PlatformMonitor != -1)
1193b4be Poul Sander 2025-02-03 21:28 16076
            viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
1193b4be Poul Sander 2025-02-03 21:28 16077
    }
1193b4be Poul Sander 2025-02-03 21:28 16078
1193b4be Poul Sander 2025-02-03 21:28 16079
    viewport->Window = window;
1193b4be Poul Sander 2025-02-03 21:28 16080
    viewport->LastFrameActive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 16081
    viewport->UpdateWorkRect();
1193b4be Poul Sander 2025-02-03 21:28 16082
    IM_ASSERT(window == NULL || viewport->ID == window->ID);
1193b4be Poul Sander 2025-02-03 21:28 16083
1193b4be Poul Sander 2025-02-03 21:28 16084
    if (window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 16085
        window->ViewportOwned = true;
1193b4be Poul Sander 2025-02-03 21:28 16086
1193b4be Poul Sander 2025-02-03 21:28 16087
    return viewport;
1193b4be Poul Sander 2025-02-03 21:28 16088
}
1193b4be Poul Sander 2025-02-03 21:28 16089
1193b4be Poul Sander 2025-02-03 21:28 16090
static void ImGui::DestroyViewport(ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 16091
{
1193b4be Poul Sander 2025-02-03 21:28 16092
    // Clear references to this viewport in windows (window->ViewportId becomes the master data)
1193b4be Poul Sander 2025-02-03 21:28 16093
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16094
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 16095
    {
1193b4be Poul Sander 2025-02-03 21:28 16096
        if (window->Viewport != viewport)
1193b4be Poul Sander 2025-02-03 21:28 16097
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16098
        window->Viewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16099
        window->ViewportOwned = false;
1193b4be Poul Sander 2025-02-03 21:28 16100
    }
1193b4be Poul Sander 2025-02-03 21:28 16101
    if (viewport == g.MouseLastHoveredViewport)
1193b4be Poul Sander 2025-02-03 21:28 16102
        g.MouseLastHoveredViewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16103
1193b4be Poul Sander 2025-02-03 21:28 16104
    // Destroy
1193b4be Poul Sander 2025-02-03 21:28 16105
    IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
1193b4be Poul Sander 2025-02-03 21:28 16106
    DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
1193b4be Poul Sander 2025-02-03 21:28 16107
    IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
1193b4be Poul Sander 2025-02-03 21:28 16108
    IM_ASSERT(g.Viewports[viewport->Idx] == viewport);
1193b4be Poul Sander 2025-02-03 21:28 16109
    g.Viewports.erase(g.Viewports.Data + viewport->Idx);
1193b4be Poul Sander 2025-02-03 21:28 16110
    IM_DELETE(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16111
}
1193b4be Poul Sander 2025-02-03 21:28 16112
1193b4be Poul Sander 2025-02-03 21:28 16113
// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
1193b4be Poul Sander 2025-02-03 21:28 16114
static void ImGui::WindowSelectViewport(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 16115
{
1193b4be Poul Sander 2025-02-03 21:28 16116
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16117
    ImGuiWindowFlags flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 16118
    window->ViewportAllowPlatformMonitorExtend = -1;
1193b4be Poul Sander 2025-02-03 21:28 16119
1193b4be Poul Sander 2025-02-03 21:28 16120
    // Restore main viewport if multi-viewport is not supported by the backend
1193b4be Poul Sander 2025-02-03 21:28 16121
    ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 16122
    if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
1193b4be Poul Sander 2025-02-03 21:28 16123
    {
1193b4be Poul Sander 2025-02-03 21:28 16124
        SetWindowViewport(window, main_viewport);
1193b4be Poul Sander 2025-02-03 21:28 16125
        return;
1193b4be Poul Sander 2025-02-03 21:28 16126
    }
1193b4be Poul Sander 2025-02-03 21:28 16127
    window->ViewportOwned = false;
1193b4be Poul Sander 2025-02-03 21:28 16128
1193b4be Poul Sander 2025-02-03 21:28 16129
    // Appearing popups reset their viewport so they can inherit again
1193b4be Poul Sander 2025-02-03 21:28 16130
    if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 16131
    {
1193b4be Poul Sander 2025-02-03 21:28 16132
        window->Viewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16133
        window->ViewportId = 0;
1193b4be Poul Sander 2025-02-03 21:28 16134
    }
1193b4be Poul Sander 2025-02-03 21:28 16135
1193b4be Poul Sander 2025-02-03 21:28 16136
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
1193b4be Poul Sander 2025-02-03 21:28 16137
    {
1193b4be Poul Sander 2025-02-03 21:28 16138
        // By default inherit from parent window
1193b4be Poul Sander 2025-02-03 21:28 16139
        if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive))
1193b4be Poul Sander 2025-02-03 21:28 16140
            window->Viewport = window->ParentWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 16141
1193b4be Poul Sander 2025-02-03 21:28 16142
        // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file
1193b4be Poul Sander 2025-02-03 21:28 16143
        if (window->Viewport == NULL && window->ViewportId != 0)
1193b4be Poul Sander 2025-02-03 21:28 16144
        {
1193b4be Poul Sander 2025-02-03 21:28 16145
            window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
1193b4be Poul Sander 2025-02-03 21:28 16146
            if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
1193b4be Poul Sander 2025-02-03 21:28 16147
                window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 16148
        }
1193b4be Poul Sander 2025-02-03 21:28 16149
    }
1193b4be Poul Sander 2025-02-03 21:28 16150
1193b4be Poul Sander 2025-02-03 21:28 16151
    bool lock_viewport = false;
1193b4be Poul Sander 2025-02-03 21:28 16152
    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
1193b4be Poul Sander 2025-02-03 21:28 16153
    {
1193b4be Poul Sander 2025-02-03 21:28 16154
        // Code explicitly request a viewport
1193b4be Poul Sander 2025-02-03 21:28 16155
        window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
1193b4be Poul Sander 2025-02-03 21:28 16156
        window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
1193b4be Poul Sander 2025-02-03 21:28 16157
        if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL)
1193b4be Poul Sander 2025-02-03 21:28 16158
        {
1193b4be Poul Sander 2025-02-03 21:28 16159
            window->Viewport->Window = window;
1193b4be Poul Sander 2025-02-03 21:28 16160
            window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node)
1193b4be Poul Sander 2025-02-03 21:28 16161
        }
1193b4be Poul Sander 2025-02-03 21:28 16162
        lock_viewport = true;
1193b4be Poul Sander 2025-02-03 21:28 16163
    }
1193b4be Poul Sander 2025-02-03 21:28 16164
    else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
1193b4be Poul Sander 2025-02-03 21:28 16165
    {
1193b4be Poul Sander 2025-02-03 21:28 16166
        // Always inherit viewport from parent window
1193b4be Poul Sander 2025-02-03 21:28 16167
        if (window->DockNode && window->DockNode->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 16168
            IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 16169
        window->Viewport = window->ParentWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 16170
    }
1193b4be Poul Sander 2025-02-03 21:28 16171
    else if (window->DockNode && window->DockNode->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 16172
    {
1193b4be Poul Sander 2025-02-03 21:28 16173
        // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame
1193b4be Poul Sander 2025-02-03 21:28 16174
        window->Viewport = window->DockNode->HostWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 16175
    }
1193b4be Poul Sander 2025-02-03 21:28 16176
    else if (flags & ImGuiWindowFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 16177
    {
1193b4be Poul Sander 2025-02-03 21:28 16178
        window->Viewport = g.MouseViewport;
1193b4be Poul Sander 2025-02-03 21:28 16179
    }
1193b4be Poul Sander 2025-02-03 21:28 16180
    else if (GetWindowAlwaysWantOwnViewport(window))
1193b4be Poul Sander 2025-02-03 21:28 16181
    {
1193b4be Poul Sander 2025-02-03 21:28 16182
        window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 16183
    }
1193b4be Poul Sander 2025-02-03 21:28 16184
    else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid())
1193b4be Poul Sander 2025-02-03 21:28 16185
    {
1193b4be Poul Sander 2025-02-03 21:28 16186
        if (window->Viewport != NULL && window->Viewport->Window == window)
1193b4be Poul Sander 2025-02-03 21:28 16187
            window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 16188
    }
1193b4be Poul Sander 2025-02-03 21:28 16189
    else
1193b4be Poul Sander 2025-02-03 21:28 16190
    {
1193b4be Poul Sander 2025-02-03 21:28 16191
        // Merge into host viewport?
1193b4be Poul Sander 2025-02-03 21:28 16192
        // We cannot test window->ViewportOwned as it set lower in the function.
1193b4be Poul Sander 2025-02-03 21:28 16193
        // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212)
1193b4be Poul Sander 2025-02-03 21:28 16194
        bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap));
1193b4be Poul Sander 2025-02-03 21:28 16195
        if (try_to_merge_into_host_viewport)
1193b4be Poul Sander 2025-02-03 21:28 16196
            UpdateTryMergeWindowIntoHostViewports(window);
1193b4be Poul Sander 2025-02-03 21:28 16197
    }
1193b4be Poul Sander 2025-02-03 21:28 16198
1193b4be Poul Sander 2025-02-03 21:28 16199
    // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
1193b4be Poul Sander 2025-02-03 21:28 16200
    if (window->Viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 16201
        if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))
1193b4be Poul Sander 2025-02-03 21:28 16202
            window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
1193b4be Poul Sander 2025-02-03 21:28 16203
1193b4be Poul Sander 2025-02-03 21:28 16204
    // Mark window as allowed to protrude outside of its viewport and into the current monitor
1193b4be Poul Sander 2025-02-03 21:28 16205
    if (!lock_viewport)
1193b4be Poul Sander 2025-02-03 21:28 16206
    {
1193b4be Poul Sander 2025-02-03 21:28 16207
        if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
1193b4be Poul Sander 2025-02-03 21:28 16208
        {
1193b4be Poul Sander 2025-02-03 21:28 16209
            // We need to take account of the possibility that mouse may become invalid.
1193b4be Poul Sander 2025-02-03 21:28 16210
            // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
1193b4be Poul Sander 2025-02-03 21:28 16211
            ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
1193b4be Poul Sander 2025-02-03 21:28 16212
            bool use_mouse_ref = (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !g.NavWindow);
1193b4be Poul Sander 2025-02-03 21:28 16213
            bool mouse_valid = IsMousePosValid(&mouse_ref);
1193b4be Poul Sander 2025-02-03 21:28 16214
            if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
1193b4be Poul Sander 2025-02-03 21:28 16215
                window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
1193b4be Poul Sander 2025-02-03 21:28 16216
            else
1193b4be Poul Sander 2025-02-03 21:28 16217
                window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
1193b4be Poul Sander 2025-02-03 21:28 16218
        }
1193b4be Poul Sander 2025-02-03 21:28 16219
        else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL)
1193b4be Poul Sander 2025-02-03 21:28 16220
        {
1193b4be Poul Sander 2025-02-03 21:28 16221
            // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
1193b4be Poul Sander 2025-02-03 21:28 16222
            const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
1193b4be Poul Sander 2025-02-03 21:28 16223
            if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
1193b4be Poul Sander 2025-02-03 21:28 16224
            {
1193b4be Poul Sander 2025-02-03 21:28 16225
                // Steal/transfer ownership
1193b4be Poul Sander 2025-02-03 21:28 16226
                IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
1193b4be Poul Sander 2025-02-03 21:28 16227
                window->Viewport->Window = window;
1193b4be Poul Sander 2025-02-03 21:28 16228
                window->Viewport->ID = window->ID;
1193b4be Poul Sander 2025-02-03 21:28 16229
                window->Viewport->LastNameHash = 0;
1193b4be Poul Sander 2025-02-03 21:28 16230
            }
1193b4be Poul Sander 2025-02-03 21:28 16231
            else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
1193b4be Poul Sander 2025-02-03 21:28 16232
            {
1193b4be Poul Sander 2025-02-03 21:28 16233
                // New viewport
1193b4be Poul Sander 2025-02-03 21:28 16234
                window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
1193b4be Poul Sander 2025-02-03 21:28 16235
            }
1193b4be Poul Sander 2025-02-03 21:28 16236
        }
1193b4be Poul Sander 2025-02-03 21:28 16237
        else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
1193b4be Poul Sander 2025-02-03 21:28 16238
        {
1193b4be Poul Sander 2025-02-03 21:28 16239
            // Regular (non-child, non-popup) windows by default are also allowed to protrude
1193b4be Poul Sander 2025-02-03 21:28 16240
            // Child windows are kept contained within their parent.
1193b4be Poul Sander 2025-02-03 21:28 16241
            window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
1193b4be Poul Sander 2025-02-03 21:28 16242
        }
1193b4be Poul Sander 2025-02-03 21:28 16243
    }
1193b4be Poul Sander 2025-02-03 21:28 16244
1193b4be Poul Sander 2025-02-03 21:28 16245
    // Update flags
1193b4be Poul Sander 2025-02-03 21:28 16246
    window->ViewportOwned = (window == window->Viewport->Window);
1193b4be Poul Sander 2025-02-03 21:28 16247
    window->ViewportId = window->Viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 16248
1193b4be Poul Sander 2025-02-03 21:28 16249
    // If the OS window has a title bar, hide our imgui title bar
1193b4be Poul Sander 2025-02-03 21:28 16250
    //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
1193b4be Poul Sander 2025-02-03 21:28 16251
    //    window->Flags |= ImGuiWindowFlags_NoTitleBar;
1193b4be Poul Sander 2025-02-03 21:28 16252
}
1193b4be Poul Sander 2025-02-03 21:28 16253
1193b4be Poul Sander 2025-02-03 21:28 16254
void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack)
1193b4be Poul Sander 2025-02-03 21:28 16255
{
1193b4be Poul Sander 2025-02-03 21:28 16256
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16257
1193b4be Poul Sander 2025-02-03 21:28 16258
    bool viewport_rect_changed = false;
1193b4be Poul Sander 2025-02-03 21:28 16259
1193b4be Poul Sander 2025-02-03 21:28 16260
    // Synchronize window --> viewport in most situations
1193b4be Poul Sander 2025-02-03 21:28 16261
    // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
1193b4be Poul Sander 2025-02-03 21:28 16262
    if (window->Viewport->PlatformRequestMove)
1193b4be Poul Sander 2025-02-03 21:28 16263
    {
1193b4be Poul Sander 2025-02-03 21:28 16264
        window->Pos = window->Viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 16265
        MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 16266
    }
1193b4be Poul Sander 2025-02-03 21:28 16267
    else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 16268
    {
1193b4be Poul Sander 2025-02-03 21:28 16269
        viewport_rect_changed = true;
1193b4be Poul Sander 2025-02-03 21:28 16270
        window->Viewport->Pos = window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 16271
    }
1193b4be Poul Sander 2025-02-03 21:28 16272
1193b4be Poul Sander 2025-02-03 21:28 16273
    if (window->Viewport->PlatformRequestResize)
1193b4be Poul Sander 2025-02-03 21:28 16274
    {
1193b4be Poul Sander 2025-02-03 21:28 16275
        window->Size = window->SizeFull = window->Viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 16276
        MarkIniSettingsDirty(window);
1193b4be Poul Sander 2025-02-03 21:28 16277
    }
1193b4be Poul Sander 2025-02-03 21:28 16278
    else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
1193b4be Poul Sander 2025-02-03 21:28 16279
    {
1193b4be Poul Sander 2025-02-03 21:28 16280
        viewport_rect_changed = true;
1193b4be Poul Sander 2025-02-03 21:28 16281
        window->Viewport->Size = window->Size;
1193b4be Poul Sander 2025-02-03 21:28 16282
    }
1193b4be Poul Sander 2025-02-03 21:28 16283
    window->Viewport->UpdateWorkRect();
1193b4be Poul Sander 2025-02-03 21:28 16284
1193b4be Poul Sander 2025-02-03 21:28 16285
    // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
1193b4be Poul Sander 2025-02-03 21:28 16286
    // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
1193b4be Poul Sander 2025-02-03 21:28 16287
    if (viewport_rect_changed)
1193b4be Poul Sander 2025-02-03 21:28 16288
        UpdateViewportPlatformMonitor(window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 16289
1193b4be Poul Sander 2025-02-03 21:28 16290
    // Update common viewport flags
1193b4be Poul Sander 2025-02-03 21:28 16291
    const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
1193b4be Poul Sander 2025-02-03 21:28 16292
    ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
1193b4be Poul Sander 2025-02-03 21:28 16293
    ImGuiWindowFlags window_flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 16294
    const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0;
1193b4be Poul Sander 2025-02-03 21:28 16295
    const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
1193b4be Poul Sander 2025-02-03 21:28 16296
    if (window_flags & ImGuiWindowFlags_Tooltip)
1193b4be Poul Sander 2025-02-03 21:28 16297
        viewport_flags |= ImGuiViewportFlags_TopMost;
1193b4be Poul Sander 2025-02-03 21:28 16298
    if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)
1193b4be Poul Sander 2025-02-03 21:28 16299
        viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
1193b4be Poul Sander 2025-02-03 21:28 16300
    if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
1193b4be Poul Sander 2025-02-03 21:28 16301
        viewport_flags |= ImGuiViewportFlags_NoDecoration;
1193b4be Poul Sander 2025-02-03 21:28 16302
1193b4be Poul Sander 2025-02-03 21:28 16303
    // Not correct to set modal as topmost because:
1193b4be Poul Sander 2025-02-03 21:28 16304
    // - Because other popups can be stacked above a modal (e.g. combo box in a modal)
1193b4be Poul Sander 2025-02-03 21:28 16305
    // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
1193b4be Poul Sander 2025-02-03 21:28 16306
    //if (flags & ImGuiWindowFlags_Modal)
1193b4be Poul Sander 2025-02-03 21:28 16307
    //    viewport_flags |= ImGuiViewportFlags_TopMost;
1193b4be Poul Sander 2025-02-03 21:28 16308
1193b4be Poul Sander 2025-02-03 21:28 16309
    // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
1193b4be Poul Sander 2025-02-03 21:28 16310
    // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
1193b4be Poul Sander 2025-02-03 21:28 16311
    // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app,
1193b4be Poul Sander 2025-02-03 21:28 16312
    // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
1193b4be Poul Sander 2025-02-03 21:28 16313
    if (is_short_lived_floating_window && !is_modal)
1193b4be Poul Sander 2025-02-03 21:28 16314
        viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
1193b4be Poul Sander 2025-02-03 21:28 16315
1193b4be Poul Sander 2025-02-03 21:28 16316
    // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
1193b4be Poul Sander 2025-02-03 21:28 16317
    if (window->WindowClass.ViewportFlagsOverrideSet)
1193b4be Poul Sander 2025-02-03 21:28 16318
        viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
1193b4be Poul Sander 2025-02-03 21:28 16319
    if (window->WindowClass.ViewportFlagsOverrideClear)
1193b4be Poul Sander 2025-02-03 21:28 16320
        viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
1193b4be Poul Sander 2025-02-03 21:28 16321
1193b4be Poul Sander 2025-02-03 21:28 16322
    // We can also tell the backend that clearing the platform window won't be necessary,
1193b4be Poul Sander 2025-02-03 21:28 16323
    // as our window background is filling the viewport and we have disabled BgAlpha.
1193b4be Poul Sander 2025-02-03 21:28 16324
    // FIXME: Work on support for per-viewport transparency (#2766)
1193b4be Poul Sander 2025-02-03 21:28 16325
    if (!(window_flags & ImGuiWindowFlags_NoBackground))
1193b4be Poul Sander 2025-02-03 21:28 16326
        viewport_flags |= ImGuiViewportFlags_NoRendererClear;
1193b4be Poul Sander 2025-02-03 21:28 16327
1193b4be Poul Sander 2025-02-03 21:28 16328
    window->Viewport->Flags = viewport_flags;
1193b4be Poul Sander 2025-02-03 21:28 16329
1193b4be Poul Sander 2025-02-03 21:28 16330
    // Update parent viewport ID
1193b4be Poul Sander 2025-02-03 21:28 16331
    // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
1193b4be Poul Sander 2025-02-03 21:28 16332
    if (window->WindowClass.ParentViewportId != (ImGuiID)-1)
1193b4be Poul Sander 2025-02-03 21:28 16333
        window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
1193b4be Poul Sander 2025-02-03 21:28 16334
    else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive))
1193b4be Poul Sander 2025-02-03 21:28 16335
        window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 16336
    else
1193b4be Poul Sander 2025-02-03 21:28 16337
        window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
1193b4be Poul Sander 2025-02-03 21:28 16338
}
1193b4be Poul Sander 2025-02-03 21:28 16339
1193b4be Poul Sander 2025-02-03 21:28 16340
// Called by user at the end of the main loop, after EndFrame()
1193b4be Poul Sander 2025-02-03 21:28 16341
// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
1193b4be Poul Sander 2025-02-03 21:28 16342
void ImGui::UpdatePlatformWindows()
1193b4be Poul Sander 2025-02-03 21:28 16343
{
1193b4be Poul Sander 2025-02-03 21:28 16344
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16345
    IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
1193b4be Poul Sander 2025-02-03 21:28 16346
    IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 16347
    g.FrameCountPlatformEnded = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 16348
    if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
1193b4be Poul Sander 2025-02-03 21:28 16349
        return;
1193b4be Poul Sander 2025-02-03 21:28 16350
1193b4be Poul Sander 2025-02-03 21:28 16351
    // Create/resize/destroy platform windows to match each active viewport.
1193b4be Poul Sander 2025-02-03 21:28 16352
    // Skip the main viewport (index 0), which is always fully handled by the application!
1193b4be Poul Sander 2025-02-03 21:28 16353
    for (int i = 1; i < g.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16354
    {
1193b4be Poul Sander 2025-02-03 21:28 16355
        ImGuiViewportP* viewport = g.Viewports[i];
1193b4be Poul Sander 2025-02-03 21:28 16356
1193b4be Poul Sander 2025-02-03 21:28 16357
        // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
1193b4be Poul Sander 2025-02-03 21:28 16358
        // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
1193b4be Poul Sander 2025-02-03 21:28 16359
        bool destroy_platform_window = false;
1193b4be Poul Sander 2025-02-03 21:28 16360
        destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
1193b4be Poul Sander 2025-02-03 21:28 16361
        destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
1193b4be Poul Sander 2025-02-03 21:28 16362
        if (destroy_platform_window)
1193b4be Poul Sander 2025-02-03 21:28 16363
        {
1193b4be Poul Sander 2025-02-03 21:28 16364
            DestroyPlatformWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16365
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16366
        }
1193b4be Poul Sander 2025-02-03 21:28 16367
1193b4be Poul Sander 2025-02-03 21:28 16368
        // New windows that appears directly in a new viewport won't always have a size on their first frame
1193b4be Poul Sander 2025-02-03 21:28 16369
        if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
1193b4be Poul Sander 2025-02-03 21:28 16370
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16371
1193b4be Poul Sander 2025-02-03 21:28 16372
        // Create window
1193b4be Poul Sander 2025-02-03 21:28 16373
        const bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
1193b4be Poul Sander 2025-02-03 21:28 16374
        if (is_new_platform_window)
1193b4be Poul Sander 2025-02-03 21:28 16375
        {
1193b4be Poul Sander 2025-02-03 21:28 16376
            IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
1193b4be Poul Sander 2025-02-03 21:28 16377
            g.PlatformIO.Platform_CreateWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16378
            if (g.PlatformIO.Renderer_CreateWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 16379
                g.PlatformIO.Renderer_CreateWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16380
            g.PlatformWindowsCreatedCount++;
1193b4be Poul Sander 2025-02-03 21:28 16381
            viewport->LastNameHash = 0;
1193b4be Poul Sander 2025-02-03 21:28 16382
            viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?)
1193b4be Poul Sander 2025-02-03 21:28 16383
            viewport->LastRendererSize = viewport->Size;                                       // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
1193b4be Poul Sander 2025-02-03 21:28 16384
            viewport->PlatformWindowCreated = true;
1193b4be Poul Sander 2025-02-03 21:28 16385
        }
1193b4be Poul Sander 2025-02-03 21:28 16386
1193b4be Poul Sander 2025-02-03 21:28 16387
        // Apply Position and Size (from ImGui to Platform/Renderer backends)
1193b4be Poul Sander 2025-02-03 21:28 16388
        if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
1193b4be Poul Sander 2025-02-03 21:28 16389
            g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
1193b4be Poul Sander 2025-02-03 21:28 16390
        if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
1193b4be Poul Sander 2025-02-03 21:28 16391
            g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
1193b4be Poul Sander 2025-02-03 21:28 16392
        if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
1193b4be Poul Sander 2025-02-03 21:28 16393
            g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
1193b4be Poul Sander 2025-02-03 21:28 16394
        viewport->LastPlatformPos = viewport->Pos;
1193b4be Poul Sander 2025-02-03 21:28 16395
        viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 16396
1193b4be Poul Sander 2025-02-03 21:28 16397
        // Update title bar (if it changed)
1193b4be Poul Sander 2025-02-03 21:28 16398
        if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
1193b4be Poul Sander 2025-02-03 21:28 16399
        {
1193b4be Poul Sander 2025-02-03 21:28 16400
            const char* title_begin = window_for_title->Name;
1193b4be Poul Sander 2025-02-03 21:28 16401
            char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
1193b4be Poul Sander 2025-02-03 21:28 16402
            const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
1193b4be Poul Sander 2025-02-03 21:28 16403
            if (viewport->LastNameHash != title_hash)
1193b4be Poul Sander 2025-02-03 21:28 16404
            {
1193b4be Poul Sander 2025-02-03 21:28 16405
                char title_end_backup_c = *title_end;
1193b4be Poul Sander 2025-02-03 21:28 16406
                *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
1193b4be Poul Sander 2025-02-03 21:28 16407
                g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
1193b4be Poul Sander 2025-02-03 21:28 16408
                *title_end = title_end_backup_c;
1193b4be Poul Sander 2025-02-03 21:28 16409
                viewport->LastNameHash = title_hash;
1193b4be Poul Sander 2025-02-03 21:28 16410
            }
1193b4be Poul Sander 2025-02-03 21:28 16411
        }
1193b4be Poul Sander 2025-02-03 21:28 16412
1193b4be Poul Sander 2025-02-03 21:28 16413
        // Update alpha (if it changed)
1193b4be Poul Sander 2025-02-03 21:28 16414
        if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
1193b4be Poul Sander 2025-02-03 21:28 16415
            g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
1193b4be Poul Sander 2025-02-03 21:28 16416
        viewport->LastAlpha = viewport->Alpha;
1193b4be Poul Sander 2025-02-03 21:28 16417
1193b4be Poul Sander 2025-02-03 21:28 16418
        // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
1193b4be Poul Sander 2025-02-03 21:28 16419
        if (g.PlatformIO.Platform_UpdateWindow)
1193b4be Poul Sander 2025-02-03 21:28 16420
            g.PlatformIO.Platform_UpdateWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16421
1193b4be Poul Sander 2025-02-03 21:28 16422
        if (is_new_platform_window)
1193b4be Poul Sander 2025-02-03 21:28 16423
        {
1193b4be Poul Sander 2025-02-03 21:28 16424
            // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late)
1193b4be Poul Sander 2025-02-03 21:28 16425
            if (g.FrameCount < 3)
1193b4be Poul Sander 2025-02-03 21:28 16426
                viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
1193b4be Poul Sander 2025-02-03 21:28 16427
1193b4be Poul Sander 2025-02-03 21:28 16428
            // Show window
1193b4be Poul Sander 2025-02-03 21:28 16429
            g.PlatformIO.Platform_ShowWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16430
1193b4be Poul Sander 2025-02-03 21:28 16431
            // Even without focus, we assume the window becomes front-most.
1193b4be Poul Sander 2025-02-03 21:28 16432
            // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
1193b4be Poul Sander 2025-02-03 21:28 16433
            if (viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
1193b4be Poul Sander 2025-02-03 21:28 16434
                viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
1193b4be Poul Sander 2025-02-03 21:28 16435
        }
1193b4be Poul Sander 2025-02-03 21:28 16436
1193b4be Poul Sander 2025-02-03 21:28 16437
        // Clear request flags
1193b4be Poul Sander 2025-02-03 21:28 16438
        viewport->ClearRequestFlags();
1193b4be Poul Sander 2025-02-03 21:28 16439
    }
1193b4be Poul Sander 2025-02-03 21:28 16440
}
1193b4be Poul Sander 2025-02-03 21:28 16441
1193b4be Poul Sander 2025-02-03 21:28 16442
// This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
1193b4be Poul Sander 2025-02-03 21:28 16443
// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
1193b4be Poul Sander 2025-02-03 21:28 16444
// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
1193b4be Poul Sander 2025-02-03 21:28 16445
//
1193b4be Poul Sander 2025-02-03 21:28 16446
//    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1193b4be Poul Sander 2025-02-03 21:28 16447
//    for (int i = 1; i < platform_io.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16448
//        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
1193b4be Poul Sander 2025-02-03 21:28 16449
//            MyRenderFunction(platform_io.Viewports[i], my_args);
1193b4be Poul Sander 2025-02-03 21:28 16450
//    for (int i = 1; i < platform_io.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16451
//        if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
1193b4be Poul Sander 2025-02-03 21:28 16452
//            MySwapBufferFunction(platform_io.Viewports[i], my_args);
1193b4be Poul Sander 2025-02-03 21:28 16453
//
1193b4be Poul Sander 2025-02-03 21:28 16454
void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
1193b4be Poul Sander 2025-02-03 21:28 16455
{
1193b4be Poul Sander 2025-02-03 21:28 16456
    // Skip the main viewport (index 0), which is always fully handled by the application!
1193b4be Poul Sander 2025-02-03 21:28 16457
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1193b4be Poul Sander 2025-02-03 21:28 16458
    for (int i = 1; i < platform_io.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16459
    {
1193b4be Poul Sander 2025-02-03 21:28 16460
        ImGuiViewport* viewport = platform_io.Viewports[i];
1193b4be Poul Sander 2025-02-03 21:28 16461
        if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
1193b4be Poul Sander 2025-02-03 21:28 16462
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16463
        if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
1193b4be Poul Sander 2025-02-03 21:28 16464
        if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
1193b4be Poul Sander 2025-02-03 21:28 16465
    }
1193b4be Poul Sander 2025-02-03 21:28 16466
    for (int i = 1; i < platform_io.Viewports.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 16467
    {
1193b4be Poul Sander 2025-02-03 21:28 16468
        ImGuiViewport* viewport = platform_io.Viewports[i];
1193b4be Poul Sander 2025-02-03 21:28 16469
        if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
1193b4be Poul Sander 2025-02-03 21:28 16470
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16471
        if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
1193b4be Poul Sander 2025-02-03 21:28 16472
        if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
1193b4be Poul Sander 2025-02-03 21:28 16473
    }
1193b4be Poul Sander 2025-02-03 21:28 16474
}
1193b4be Poul Sander 2025-02-03 21:28 16475
1193b4be Poul Sander 2025-02-03 21:28 16476
static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
1193b4be Poul Sander 2025-02-03 21:28 16477
{
1193b4be Poul Sander 2025-02-03 21:28 16478
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16479
    for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
1193b4be Poul Sander 2025-02-03 21:28 16480
    {
1193b4be Poul Sander 2025-02-03 21:28 16481
        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
1193b4be Poul Sander 2025-02-03 21:28 16482
        if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
1193b4be Poul Sander 2025-02-03 21:28 16483
            return monitor_n;
1193b4be Poul Sander 2025-02-03 21:28 16484
    }
1193b4be Poul Sander 2025-02-03 21:28 16485
    return -1;
1193b4be Poul Sander 2025-02-03 21:28 16486
}
1193b4be Poul Sander 2025-02-03 21:28 16487
1193b4be Poul Sander 2025-02-03 21:28 16488
// Search for the monitor with the largest intersection area with the given rectangle
1193b4be Poul Sander 2025-02-03 21:28 16489
// We generally try to avoid searching loops but the monitor count should be very small here
1193b4be Poul Sander 2025-02-03 21:28 16490
// FIXME-OPT: We could test the last monitor used for that viewport first, and early
1193b4be Poul Sander 2025-02-03 21:28 16491
static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
1193b4be Poul Sander 2025-02-03 21:28 16492
{
1193b4be Poul Sander 2025-02-03 21:28 16493
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16494
1193b4be Poul Sander 2025-02-03 21:28 16495
    const int monitor_count = g.PlatformIO.Monitors.Size;
1193b4be Poul Sander 2025-02-03 21:28 16496
    if (monitor_count <= 1)
1193b4be Poul Sander 2025-02-03 21:28 16497
        return monitor_count - 1;
1193b4be Poul Sander 2025-02-03 21:28 16498
1193b4be Poul Sander 2025-02-03 21:28 16499
    // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position.
1193b4be Poul Sander 2025-02-03 21:28 16500
    // This is necessary for tooltips which always resize down to zero at first.
1193b4be Poul Sander 2025-02-03 21:28 16501
    const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 16502
    int best_monitor_n = -1;
1193b4be Poul Sander 2025-02-03 21:28 16503
    float best_monitor_surface = 0.001f;
1193b4be Poul Sander 2025-02-03 21:28 16504
1193b4be Poul Sander 2025-02-03 21:28 16505
    for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
1193b4be Poul Sander 2025-02-03 21:28 16506
    {
1193b4be Poul Sander 2025-02-03 21:28 16507
        const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
1193b4be Poul Sander 2025-02-03 21:28 16508
        const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
1193b4be Poul Sander 2025-02-03 21:28 16509
        if (monitor_rect.Contains(rect))
1193b4be Poul Sander 2025-02-03 21:28 16510
            return monitor_n;
1193b4be Poul Sander 2025-02-03 21:28 16511
        ImRect overlapping_rect = rect;
1193b4be Poul Sander 2025-02-03 21:28 16512
        overlapping_rect.ClipWithFull(monitor_rect);
1193b4be Poul Sander 2025-02-03 21:28 16513
        float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
1193b4be Poul Sander 2025-02-03 21:28 16514
        if (overlapping_surface < best_monitor_surface)
1193b4be Poul Sander 2025-02-03 21:28 16515
            continue;
1193b4be Poul Sander 2025-02-03 21:28 16516
        best_monitor_surface = overlapping_surface;
1193b4be Poul Sander 2025-02-03 21:28 16517
        best_monitor_n = monitor_n;
1193b4be Poul Sander 2025-02-03 21:28 16518
    }
1193b4be Poul Sander 2025-02-03 21:28 16519
    return best_monitor_n;
1193b4be Poul Sander 2025-02-03 21:28 16520
}
1193b4be Poul Sander 2025-02-03 21:28 16521
1193b4be Poul Sander 2025-02-03 21:28 16522
// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
1193b4be Poul Sander 2025-02-03 21:28 16523
static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 16524
{
1193b4be Poul Sander 2025-02-03 21:28 16525
    viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
1193b4be Poul Sander 2025-02-03 21:28 16526
}
1193b4be Poul Sander 2025-02-03 21:28 16527
1193b4be Poul Sander 2025-02-03 21:28 16528
// Return value is always != NULL, but don't hold on it across frames.
1193b4be Poul Sander 2025-02-03 21:28 16529
const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p)
1193b4be Poul Sander 2025-02-03 21:28 16530
{
1193b4be Poul Sander 2025-02-03 21:28 16531
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16532
    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p;
1193b4be Poul Sander 2025-02-03 21:28 16533
    int monitor_idx = viewport->PlatformMonitor;
1193b4be Poul Sander 2025-02-03 21:28 16534
    if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
1193b4be Poul Sander 2025-02-03 21:28 16535
        return &g.PlatformIO.Monitors[monitor_idx];
1193b4be Poul Sander 2025-02-03 21:28 16536
    return &g.FallbackMonitor;
1193b4be Poul Sander 2025-02-03 21:28 16537
}
1193b4be Poul Sander 2025-02-03 21:28 16538
1193b4be Poul Sander 2025-02-03 21:28 16539
void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 16540
{
1193b4be Poul Sander 2025-02-03 21:28 16541
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16542
    if (viewport->PlatformWindowCreated)
1193b4be Poul Sander 2025-02-03 21:28 16543
    {
1193b4be Poul Sander 2025-02-03 21:28 16544
        IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
1193b4be Poul Sander 2025-02-03 21:28 16545
        if (g.PlatformIO.Renderer_DestroyWindow)
1193b4be Poul Sander 2025-02-03 21:28 16546
            g.PlatformIO.Renderer_DestroyWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16547
        if (g.PlatformIO.Platform_DestroyWindow)
1193b4be Poul Sander 2025-02-03 21:28 16548
            g.PlatformIO.Platform_DestroyWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16549
        IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
1193b4be Poul Sander 2025-02-03 21:28 16550
1193b4be Poul Sander 2025-02-03 21:28 16551
        // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
1193b4be Poul Sander 2025-02-03 21:28 16552
        // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
1193b4be Poul Sander 2025-02-03 21:28 16553
        if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 16554
            viewport->PlatformWindowCreated = false;
1193b4be Poul Sander 2025-02-03 21:28 16555
    }
1193b4be Poul Sander 2025-02-03 21:28 16556
    else
1193b4be Poul Sander 2025-02-03 21:28 16557
    {
1193b4be Poul Sander 2025-02-03 21:28 16558
        IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
1193b4be Poul Sander 2025-02-03 21:28 16559
    }
1193b4be Poul Sander 2025-02-03 21:28 16560
    viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16561
    viewport->ClearRequestFlags();
1193b4be Poul Sander 2025-02-03 21:28 16562
}
1193b4be Poul Sander 2025-02-03 21:28 16563
1193b4be Poul Sander 2025-02-03 21:28 16564
void ImGui::DestroyPlatformWindows()
1193b4be Poul Sander 2025-02-03 21:28 16565
{
1193b4be Poul Sander 2025-02-03 21:28 16566
    // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend
1193b4be Poul Sander 2025-02-03 21:28 16567
    // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
1193b4be Poul Sander 2025-02-03 21:28 16568
    // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
1193b4be Poul Sander 2025-02-03 21:28 16569
    // code to operator a consistent manner.
1193b4be Poul Sander 2025-02-03 21:28 16570
    // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
1193b4be Poul Sander 2025-02-03 21:28 16571
    // crashing if it doesn't have data stored.
1193b4be Poul Sander 2025-02-03 21:28 16572
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 16573
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 16574
        DestroyPlatformWindow(viewport);
1193b4be Poul Sander 2025-02-03 21:28 16575
}
1193b4be Poul Sander 2025-02-03 21:28 16576
1193b4be Poul Sander 2025-02-03 21:28 16577
1193b4be Poul Sander 2025-02-03 21:28 16578
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16579
// [SECTION] DOCKING
1193b4be Poul Sander 2025-02-03 21:28 16580
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16581
// Docking: Internal Types
1193b4be Poul Sander 2025-02-03 21:28 16582
// Docking: Forward Declarations
1193b4be Poul Sander 2025-02-03 21:28 16583
// Docking: ImGuiDockContext
1193b4be Poul Sander 2025-02-03 21:28 16584
// Docking: ImGuiDockContext Docking/Undocking functions
1193b4be Poul Sander 2025-02-03 21:28 16585
// Docking: ImGuiDockNode
1193b4be Poul Sander 2025-02-03 21:28 16586
// Docking: ImGuiDockNode Tree manipulation functions
1193b4be Poul Sander 2025-02-03 21:28 16587
// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
1193b4be Poul Sander 2025-02-03 21:28 16588
// Docking: Builder Functions
1193b4be Poul Sander 2025-02-03 21:28 16589
// Docking: Begin/End Support Functions (called from Begin/End)
1193b4be Poul Sander 2025-02-03 21:28 16590
// Docking: Settings
1193b4be Poul Sander 2025-02-03 21:28 16591
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16592
1193b4be Poul Sander 2025-02-03 21:28 16593
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16594
// Typical Docking call flow: (root level is generally public API):
1193b4be Poul Sander 2025-02-03 21:28 16595
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16596
// - NewFrame()                               new dear imgui frame
1193b4be Poul Sander 2025-02-03 21:28 16597
//    | DockContextNewFrameUpdateUndocking()  - process queued undocking requests
1193b4be Poul Sander 2025-02-03 21:28 16598
//    | - DockContextProcessUndockWindow()    - process one window undocking request
1193b4be Poul Sander 2025-02-03 21:28 16599
//    | - DockContextProcessUndockNode()      - process one whole node undocking request
1193b4be Poul Sander 2025-02-03 21:28 16600
//    | DockContextNewFrameUpdateUndocking()  - process queue docking requests, create floating dock nodes
1193b4be Poul Sander 2025-02-03 21:28 16601
//    | - update g.HoveredDockNode            - [debug] update node hovered by mouse
1193b4be Poul Sander 2025-02-03 21:28 16602
//    | - DockContextProcessDock()            - process one docking request
1193b4be Poul Sander 2025-02-03 21:28 16603
//    | - DockNodeUpdate()
1193b4be Poul Sander 2025-02-03 21:28 16604
//    |   - DockNodeUpdateForRootNode()
1193b4be Poul Sander 2025-02-03 21:28 16605
//    |     - DockNodeUpdateFlagsAndCollapse()
1193b4be Poul Sander 2025-02-03 21:28 16606
//    |     - DockNodeFindInfo()
1193b4be Poul Sander 2025-02-03 21:28 16607
//    |   - destroy unused node or tab bar
1193b4be Poul Sander 2025-02-03 21:28 16608
//    |   - create dock node host window
1193b4be Poul Sander 2025-02-03 21:28 16609
//    |      - Begin() etc.
1193b4be Poul Sander 2025-02-03 21:28 16610
//    |   - DockNodeStartMouseMovingWindow()
1193b4be Poul Sander 2025-02-03 21:28 16611
//    |   - DockNodeTreeUpdatePosSize()
1193b4be Poul Sander 2025-02-03 21:28 16612
//    |   - DockNodeTreeUpdateSplitter()
1193b4be Poul Sander 2025-02-03 21:28 16613
//    |   - draw node background
1193b4be Poul Sander 2025-02-03 21:28 16614
//    |   - DockNodeUpdateTabBar()            - create/update tab bar for a docking node
1193b4be Poul Sander 2025-02-03 21:28 16615
//    |     - DockNodeAddTabBar()
1193b4be Poul Sander 2025-02-03 21:28 16616
//    |     - DockNodeWindowMenuUpdate()
1193b4be Poul Sander 2025-02-03 21:28 16617
//    |     - DockNodeCalcTabBarLayout()
1193b4be Poul Sander 2025-02-03 21:28 16618
//    |     - BeginTabBarEx()
1193b4be Poul Sander 2025-02-03 21:28 16619
//    |     - TabItemEx() calls
1193b4be Poul Sander 2025-02-03 21:28 16620
//    |     - EndTabBar()
1193b4be Poul Sander 2025-02-03 21:28 16621
//    |   - BeginDockableDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 16622
//    |      - DockNodeUpdate()               - recurse into child nodes...
1193b4be Poul Sander 2025-02-03 21:28 16623
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16624
// - DockSpace()                              user submit a dockspace into a window
1193b4be Poul Sander 2025-02-03 21:28 16625
//    | Begin(Child)                          - create a child window
1193b4be Poul Sander 2025-02-03 21:28 16626
//    | DockNodeUpdate()                      - call main dock node update function
1193b4be Poul Sander 2025-02-03 21:28 16627
//    | End(Child)
1193b4be Poul Sander 2025-02-03 21:28 16628
//    | ItemSize()
1193b4be Poul Sander 2025-02-03 21:28 16629
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16630
// - Begin()
1193b4be Poul Sander 2025-02-03 21:28 16631
//    | BeginDocked()
1193b4be Poul Sander 2025-02-03 21:28 16632
//    | BeginDockableDragDropSource()
1193b4be Poul Sander 2025-02-03 21:28 16633
//    | BeginDockableDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 16634
//    | - DockNodePreviewDockRender()
1193b4be Poul Sander 2025-02-03 21:28 16635
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16636
// - EndFrame()
1193b4be Poul Sander 2025-02-03 21:28 16637
//    | DockContextEndFrame()
1193b4be Poul Sander 2025-02-03 21:28 16638
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16639
1193b4be Poul Sander 2025-02-03 21:28 16640
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16641
// Docking: Internal Types
1193b4be Poul Sander 2025-02-03 21:28 16642
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16643
// - ImGuiDockRequestType
1193b4be Poul Sander 2025-02-03 21:28 16644
// - ImGuiDockRequest
1193b4be Poul Sander 2025-02-03 21:28 16645
// - ImGuiDockPreviewData
1193b4be Poul Sander 2025-02-03 21:28 16646
// - ImGuiDockNodeSettings
1193b4be Poul Sander 2025-02-03 21:28 16647
// - ImGuiDockContext
1193b4be Poul Sander 2025-02-03 21:28 16648
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16649
1193b4be Poul Sander 2025-02-03 21:28 16650
enum ImGuiDockRequestType
1193b4be Poul Sander 2025-02-03 21:28 16651
{
1193b4be Poul Sander 2025-02-03 21:28 16652
    ImGuiDockRequestType_None = 0,
1193b4be Poul Sander 2025-02-03 21:28 16653
    ImGuiDockRequestType_Dock,
1193b4be Poul Sander 2025-02-03 21:28 16654
    ImGuiDockRequestType_Undock,
1193b4be Poul Sander 2025-02-03 21:28 16655
    ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload
1193b4be Poul Sander 2025-02-03 21:28 16656
};
1193b4be Poul Sander 2025-02-03 21:28 16657
1193b4be Poul Sander 2025-02-03 21:28 16658
struct ImGuiDockRequest
1193b4be Poul Sander 2025-02-03 21:28 16659
{
1193b4be Poul Sander 2025-02-03 21:28 16660
    ImGuiDockRequestType    Type;
1193b4be Poul Sander 2025-02-03 21:28 16661
    ImGuiWindow*            DockTargetWindow;   // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL)
1193b4be Poul Sander 2025-02-03 21:28 16662
    ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into
1193b4be Poul Sander 2025-02-03 21:28 16663
    ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
1193b4be Poul Sander 2025-02-03 21:28 16664
    ImGuiDir                DockSplitDir;
1193b4be Poul Sander 2025-02-03 21:28 16665
    float                   DockSplitRatio;
1193b4be Poul Sander 2025-02-03 21:28 16666
    bool                    DockSplitOuter;
1193b4be Poul Sander 2025-02-03 21:28 16667
    ImGuiWindow*            UndockTargetWindow;
1193b4be Poul Sander 2025-02-03 21:28 16668
    ImGuiDockNode*          UndockTargetNode;
1193b4be Poul Sander 2025-02-03 21:28 16669
1193b4be Poul Sander 2025-02-03 21:28 16670
    ImGuiDockRequest()
1193b4be Poul Sander 2025-02-03 21:28 16671
    {
1193b4be Poul Sander 2025-02-03 21:28 16672
        Type = ImGuiDockRequestType_None;
1193b4be Poul Sander 2025-02-03 21:28 16673
        DockTargetWindow = DockPayload = UndockTargetWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16674
        DockTargetNode = UndockTargetNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16675
        DockSplitDir = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 16676
        DockSplitRatio = 0.5f;
1193b4be Poul Sander 2025-02-03 21:28 16677
        DockSplitOuter = false;
1193b4be Poul Sander 2025-02-03 21:28 16678
    }
1193b4be Poul Sander 2025-02-03 21:28 16679
};
1193b4be Poul Sander 2025-02-03 21:28 16680
1193b4be Poul Sander 2025-02-03 21:28 16681
struct ImGuiDockPreviewData
1193b4be Poul Sander 2025-02-03 21:28 16682
{
1193b4be Poul Sander 2025-02-03 21:28 16683
    ImGuiDockNode   FutureNode;
1193b4be Poul Sander 2025-02-03 21:28 16684
    bool            IsDropAllowed;
1193b4be Poul Sander 2025-02-03 21:28 16685
    bool            IsCenterAvailable;
1193b4be Poul Sander 2025-02-03 21:28 16686
    bool            IsSidesAvailable;           // Hold your breath, grammar freaks..
1193b4be Poul Sander 2025-02-03 21:28 16687
    bool            IsSplitDirExplicit;         // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
1193b4be Poul Sander 2025-02-03 21:28 16688
    ImGuiDockNode*  SplitNode;
1193b4be Poul Sander 2025-02-03 21:28 16689
    ImGuiDir        SplitDir;
1193b4be Poul Sander 2025-02-03 21:28 16690
    float           SplitRatio;
1193b4be Poul Sander 2025-02-03 21:28 16691
    ImRect          DropRectsDraw[ImGuiDir_COUNT + 1];  // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
1193b4be Poul Sander 2025-02-03 21:28 16692
1193b4be Poul Sander 2025-02-03 21:28 16693
    ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); }
1193b4be Poul Sander 2025-02-03 21:28 16694
};
1193b4be Poul Sander 2025-02-03 21:28 16695
1193b4be Poul Sander 2025-02-03 21:28 16696
// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
1193b4be Poul Sander 2025-02-03 21:28 16697
struct ImGuiDockNodeSettings
1193b4be Poul Sander 2025-02-03 21:28 16698
{
1193b4be Poul Sander 2025-02-03 21:28 16699
    ImGuiID             ID;
1193b4be Poul Sander 2025-02-03 21:28 16700
    ImGuiID             ParentNodeId;
1193b4be Poul Sander 2025-02-03 21:28 16701
    ImGuiID             ParentWindowId;
1193b4be Poul Sander 2025-02-03 21:28 16702
    ImGuiID             SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 16703
    signed char         SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 16704
    char                Depth;
1193b4be Poul Sander 2025-02-03 21:28 16705
    ImGuiDockNodeFlags  Flags;                  // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
1193b4be Poul Sander 2025-02-03 21:28 16706
    ImVec2ih            Pos;
1193b4be Poul Sander 2025-02-03 21:28 16707
    ImVec2ih            Size;
1193b4be Poul Sander 2025-02-03 21:28 16708
    ImVec2ih            SizeRef;
1193b4be Poul Sander 2025-02-03 21:28 16709
    ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }
1193b4be Poul Sander 2025-02-03 21:28 16710
};
1193b4be Poul Sander 2025-02-03 21:28 16711
1193b4be Poul Sander 2025-02-03 21:28 16712
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16713
// Docking: Forward Declarations
1193b4be Poul Sander 2025-02-03 21:28 16714
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16715
1193b4be Poul Sander 2025-02-03 21:28 16716
namespace ImGui
1193b4be Poul Sander 2025-02-03 21:28 16717
{
1193b4be Poul Sander 2025-02-03 21:28 16718
    // ImGuiDockContext
1193b4be Poul Sander 2025-02-03 21:28 16719
    static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
1193b4be Poul Sander 2025-02-03 21:28 16720
    static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
1193b4be Poul Sander 2025-02-03 21:28 16721
    static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16722
    static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
1193b4be Poul Sander 2025-02-03 21:28 16723
    static void             DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
1193b4be Poul Sander 2025-02-03 21:28 16724
    static ImGuiDockNode*   DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 16725
    static void             DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
1193b4be Poul Sander 2025-02-03 21:28 16726
    static void             DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id);                            // Use root_id==0 to add all
1193b4be Poul Sander 2025-02-03 21:28 16727
1193b4be Poul Sander 2025-02-03 21:28 16728
    // ImGuiDockNode
1193b4be Poul Sander 2025-02-03 21:28 16729
    static void             DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 16730
    static void             DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
1193b4be Poul Sander 2025-02-03 21:28 16731
    static void             DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
1193b4be Poul Sander 2025-02-03 21:28 16732
    static ImGuiWindow*     DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
1193b4be Poul Sander 2025-02-03 21:28 16733
    static void             DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16734
    static void             DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 16735
    static void             DockNodeHideHostWindow(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16736
    static void             DockNodeUpdate(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16737
    static void             DockNodeUpdateForRootNode(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16738
    static void             DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16739
    static void             DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16740
    static void             DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
1193b4be Poul Sander 2025-02-03 21:28 16741
    static void             DockNodeAddTabBar(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16742
    static void             DockNodeRemoveTabBar(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16743
    static void             DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 16744
    static void             DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16745
    static void             DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 16746
    static bool             DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
1193b4be Poul Sander 2025-02-03 21:28 16747
    static void             DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking);
1193b4be Poul Sander 2025-02-03 21:28 16748
    static void             DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
1193b4be Poul Sander 2025-02-03 21:28 16749
    static void             DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos);
1193b4be Poul Sander 2025-02-03 21:28 16750
    static void             DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
1193b4be Poul Sander 2025-02-03 21:28 16751
    static bool             DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos);
1193b4be Poul Sander 2025-02-03 21:28 16752
    static const char*      DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
1193b4be Poul Sander 2025-02-03 21:28 16753
    static int              DockNodeGetTabOrder(ImGuiWindow* window);
1193b4be Poul Sander 2025-02-03 21:28 16754
1193b4be Poul Sander 2025-02-03 21:28 16755
    // ImGuiDockNode tree manipulations
1193b4be Poul Sander 2025-02-03 21:28 16756
    static void             DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
1193b4be Poul Sander 2025-02-03 21:28 16757
    static void             DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
1193b4be Poul Sander 2025-02-03 21:28 16758
    static void             DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL);
1193b4be Poul Sander 2025-02-03 21:28 16759
    static void             DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16760
    static ImGuiDockNode*   DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
1193b4be Poul Sander 2025-02-03 21:28 16761
    static ImGuiDockNode*   DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
1193b4be Poul Sander 2025-02-03 21:28 16762
1193b4be Poul Sander 2025-02-03 21:28 16763
    // Settings
1193b4be Poul Sander 2025-02-03 21:28 16764
    static void             DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
1193b4be Poul Sander 2025-02-03 21:28 16765
    static void             DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
1193b4be Poul Sander 2025-02-03 21:28 16766
    static ImGuiDockNodeSettings*   DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
1193b4be Poul Sander 2025-02-03 21:28 16767
    static void             DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1193b4be Poul Sander 2025-02-03 21:28 16768
    static void             DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1193b4be Poul Sander 2025-02-03 21:28 16769
    static void*            DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1193b4be Poul Sander 2025-02-03 21:28 16770
    static void             DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1193b4be Poul Sander 2025-02-03 21:28 16771
    static void             DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
1193b4be Poul Sander 2025-02-03 21:28 16772
}
1193b4be Poul Sander 2025-02-03 21:28 16773
1193b4be Poul Sander 2025-02-03 21:28 16774
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16775
// Docking: ImGuiDockContext
1193b4be Poul Sander 2025-02-03 21:28 16776
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16777
// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
1193b4be Poul Sander 2025-02-03 21:28 16778
// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.
1193b4be Poul Sander 2025-02-03 21:28 16779
// At boot time only, we run a simple GC to remove nodes that have no references.
1193b4be Poul Sander 2025-02-03 21:28 16780
// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
1193b4be Poul Sander 2025-02-03 21:28 16781
// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
1193b4be Poul Sander 2025-02-03 21:28 16782
// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
1193b4be Poul Sander 2025-02-03 21:28 16783
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16784
// - DockContextInitialize()
1193b4be Poul Sander 2025-02-03 21:28 16785
// - DockContextShutdown()
1193b4be Poul Sander 2025-02-03 21:28 16786
// - DockContextClearNodes()
1193b4be Poul Sander 2025-02-03 21:28 16787
// - DockContextRebuildNodes()
1193b4be Poul Sander 2025-02-03 21:28 16788
// - DockContextNewFrameUpdateUndocking()
1193b4be Poul Sander 2025-02-03 21:28 16789
// - DockContextNewFrameUpdateDocking()
1193b4be Poul Sander 2025-02-03 21:28 16790
// - DockContextEndFrame()
1193b4be Poul Sander 2025-02-03 21:28 16791
// - DockContextFindNodeByID()
1193b4be Poul Sander 2025-02-03 21:28 16792
// - DockContextBindNodeToWindow()
1193b4be Poul Sander 2025-02-03 21:28 16793
// - DockContextGenNodeID()
1193b4be Poul Sander 2025-02-03 21:28 16794
// - DockContextAddNode()
1193b4be Poul Sander 2025-02-03 21:28 16795
// - DockContextRemoveNode()
1193b4be Poul Sander 2025-02-03 21:28 16796
// - ImGuiDockContextPruneNodeData
1193b4be Poul Sander 2025-02-03 21:28 16797
// - DockContextPruneUnusedSettingsNodes()
1193b4be Poul Sander 2025-02-03 21:28 16798
// - DockContextBuildNodesFromSettings()
1193b4be Poul Sander 2025-02-03 21:28 16799
// - DockContextBuildAddWindowsToNodes()
1193b4be Poul Sander 2025-02-03 21:28 16800
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 16801
1193b4be Poul Sander 2025-02-03 21:28 16802
void ImGui::DockContextInitialize(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16803
{
1193b4be Poul Sander 2025-02-03 21:28 16804
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16805
1193b4be Poul Sander 2025-02-03 21:28 16806
    // Add .ini handle for persistent docking data
1193b4be Poul Sander 2025-02-03 21:28 16807
    ImGuiSettingsHandler ini_handler;
1193b4be Poul Sander 2025-02-03 21:28 16808
    ini_handler.TypeName = "Docking";
1193b4be Poul Sander 2025-02-03 21:28 16809
    ini_handler.TypeHash = ImHashStr("Docking");
1193b4be Poul Sander 2025-02-03 21:28 16810
    ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
1193b4be Poul Sander 2025-02-03 21:28 16811
    ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
1193b4be Poul Sander 2025-02-03 21:28 16812
    ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
1193b4be Poul Sander 2025-02-03 21:28 16813
    ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
1193b4be Poul Sander 2025-02-03 21:28 16814
    ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
1193b4be Poul Sander 2025-02-03 21:28 16815
    ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
1193b4be Poul Sander 2025-02-03 21:28 16816
    g.SettingsHandlers.push_back(ini_handler);
1193b4be Poul Sander 2025-02-03 21:28 16817
1193b4be Poul Sander 2025-02-03 21:28 16818
    g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default;
1193b4be Poul Sander 2025-02-03 21:28 16819
}
1193b4be Poul Sander 2025-02-03 21:28 16820
1193b4be Poul Sander 2025-02-03 21:28 16821
void ImGui::DockContextShutdown(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16822
{
1193b4be Poul Sander 2025-02-03 21:28 16823
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16824
    for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 16825
        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 16826
            IM_DELETE(node);
1193b4be Poul Sander 2025-02-03 21:28 16827
}
1193b4be Poul Sander 2025-02-03 21:28 16828
1193b4be Poul Sander 2025-02-03 21:28 16829
void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
1193b4be Poul Sander 2025-02-03 21:28 16830
{
1193b4be Poul Sander 2025-02-03 21:28 16831
    IM_UNUSED(ctx);
1193b4be Poul Sander 2025-02-03 21:28 16832
    IM_ASSERT(ctx == GImGui);
1193b4be Poul Sander 2025-02-03 21:28 16833
    DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
1193b4be Poul Sander 2025-02-03 21:28 16834
    DockBuilderRemoveNodeChildNodes(root_id);
1193b4be Poul Sander 2025-02-03 21:28 16835
}
1193b4be Poul Sander 2025-02-03 21:28 16836
1193b4be Poul Sander 2025-02-03 21:28 16837
// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
1193b4be Poul Sander 2025-02-03 21:28 16838
// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
1193b4be Poul Sander 2025-02-03 21:28 16839
void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16840
{
1193b4be Poul Sander 2025-02-03 21:28 16841
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16842
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16843
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRebuildNodes\n");
1193b4be Poul Sander 2025-02-03 21:28 16844
    SaveIniSettingsToMemory();
1193b4be Poul Sander 2025-02-03 21:28 16845
    ImGuiID root_id = 0; // Rebuild all
1193b4be Poul Sander 2025-02-03 21:28 16846
    DockContextClearNodes(ctx, root_id, false);
1193b4be Poul Sander 2025-02-03 21:28 16847
    DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
1193b4be Poul Sander 2025-02-03 21:28 16848
    DockContextBuildAddWindowsToNodes(ctx, root_id);
1193b4be Poul Sander 2025-02-03 21:28 16849
}
1193b4be Poul Sander 2025-02-03 21:28 16850
1193b4be Poul Sander 2025-02-03 21:28 16851
// Docking context update function, called by NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 16852
void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16853
{
1193b4be Poul Sander 2025-02-03 21:28 16854
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16855
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16856
    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
1193b4be Poul Sander 2025-02-03 21:28 16857
    {
1193b4be Poul Sander 2025-02-03 21:28 16858
        if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 16859
            DockContextClearNodes(ctx, 0, true);
1193b4be Poul Sander 2025-02-03 21:28 16860
        return;
1193b4be Poul Sander 2025-02-03 21:28 16861
    }
1193b4be Poul Sander 2025-02-03 21:28 16862
1193b4be Poul Sander 2025-02-03 21:28 16863
    // Setting NoSplit at runtime merges all nodes
1193b4be Poul Sander 2025-02-03 21:28 16864
    if (g.IO.ConfigDockingNoSplit)
1193b4be Poul Sander 2025-02-03 21:28 16865
        for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 16866
            if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 16867
                if (node->IsRootNode() && node->IsSplitNode())
1193b4be Poul Sander 2025-02-03 21:28 16868
                {
1193b4be Poul Sander 2025-02-03 21:28 16869
                    DockBuilderRemoveNodeChildNodes(node->ID);
1193b4be Poul Sander 2025-02-03 21:28 16870
                    //dc->WantFullRebuild = true;
1193b4be Poul Sander 2025-02-03 21:28 16871
                }
1193b4be Poul Sander 2025-02-03 21:28 16872
1193b4be Poul Sander 2025-02-03 21:28 16873
    // Process full rebuild
1193b4be Poul Sander 2025-02-03 21:28 16874
#if 0
1193b4be Poul Sander 2025-02-03 21:28 16875
    if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
1193b4be Poul Sander 2025-02-03 21:28 16876
        dc->WantFullRebuild = true;
1193b4be Poul Sander 2025-02-03 21:28 16877
#endif
1193b4be Poul Sander 2025-02-03 21:28 16878
    if (dc->WantFullRebuild)
1193b4be Poul Sander 2025-02-03 21:28 16879
    {
1193b4be Poul Sander 2025-02-03 21:28 16880
        DockContextRebuildNodes(ctx);
1193b4be Poul Sander 2025-02-03 21:28 16881
        dc->WantFullRebuild = false;
1193b4be Poul Sander 2025-02-03 21:28 16882
    }
1193b4be Poul Sander 2025-02-03 21:28 16883
1193b4be Poul Sander 2025-02-03 21:28 16884
    // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
1193b4be Poul Sander 2025-02-03 21:28 16885
    for (ImGuiDockRequest& req : dc->Requests)
1193b4be Poul Sander 2025-02-03 21:28 16886
    {
1193b4be Poul Sander 2025-02-03 21:28 16887
        if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow)
1193b4be Poul Sander 2025-02-03 21:28 16888
            DockContextProcessUndockWindow(ctx, req.UndockTargetWindow);
1193b4be Poul Sander 2025-02-03 21:28 16889
        else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode)
1193b4be Poul Sander 2025-02-03 21:28 16890
            DockContextProcessUndockNode(ctx, req.UndockTargetNode);
1193b4be Poul Sander 2025-02-03 21:28 16891
    }
1193b4be Poul Sander 2025-02-03 21:28 16892
}
1193b4be Poul Sander 2025-02-03 21:28 16893
1193b4be Poul Sander 2025-02-03 21:28 16894
// Docking context update function, called by NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 16895
void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16896
{
1193b4be Poul Sander 2025-02-03 21:28 16897
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16898
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16899
    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
1193b4be Poul Sander 2025-02-03 21:28 16900
        return;
1193b4be Poul Sander 2025-02-03 21:28 16901
1193b4be Poul Sander 2025-02-03 21:28 16902
    // [DEBUG] Store hovered dock node.
1193b4be Poul Sander 2025-02-03 21:28 16903
    // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
1193b4be Poul Sander 2025-02-03 21:28 16904
    // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
1193b4be Poul Sander 2025-02-03 21:28 16905
    g.DebugHoveredDockNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16906
    if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
1193b4be Poul Sander 2025-02-03 21:28 16907
    {
1193b4be Poul Sander 2025-02-03 21:28 16908
        if (hovered_window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 16909
            g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
1193b4be Poul Sander 2025-02-03 21:28 16910
        else if (hovered_window->RootWindow->DockNode)
1193b4be Poul Sander 2025-02-03 21:28 16911
            g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 16912
    }
1193b4be Poul Sander 2025-02-03 21:28 16913
1193b4be Poul Sander 2025-02-03 21:28 16914
    // Process Docking requests
1193b4be Poul Sander 2025-02-03 21:28 16915
    for (ImGuiDockRequest& req : dc->Requests)
1193b4be Poul Sander 2025-02-03 21:28 16916
        if (req.Type == ImGuiDockRequestType_Dock)
1193b4be Poul Sander 2025-02-03 21:28 16917
            DockContextProcessDock(ctx, &req);
1193b4be Poul Sander 2025-02-03 21:28 16918
    dc->Requests.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 16919
1193b4be Poul Sander 2025-02-03 21:28 16920
    // Create windows for each automatic docking nodes
1193b4be Poul Sander 2025-02-03 21:28 16921
    // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high)
1193b4be Poul Sander 2025-02-03 21:28 16922
    for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 16923
        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 16924
            if (node->IsFloatingNode())
1193b4be Poul Sander 2025-02-03 21:28 16925
                DockNodeUpdate(node);
1193b4be Poul Sander 2025-02-03 21:28 16926
}
1193b4be Poul Sander 2025-02-03 21:28 16927
1193b4be Poul Sander 2025-02-03 21:28 16928
void ImGui::DockContextEndFrame(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16929
{
1193b4be Poul Sander 2025-02-03 21:28 16930
    // Draw backgrounds of node missing their window
1193b4be Poul Sander 2025-02-03 21:28 16931
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16932
    ImGuiDockContext* dc = &g.DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16933
    for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 16934
        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 16935
            if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame)
1193b4be Poul Sander 2025-02-03 21:28 16936
            {
1193b4be Poul Sander 2025-02-03 21:28 16937
                ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size);
1193b4be Poul Sander 2025-02-03 21:28 16938
                ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize);
1193b4be Poul Sander 2025-02-03 21:28 16939
                node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
1193b4be Poul Sander 2025-02-03 21:28 16940
                node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags);
1193b4be Poul Sander 2025-02-03 21:28 16941
            }
1193b4be Poul Sander 2025-02-03 21:28 16942
}
1193b4be Poul Sander 2025-02-03 21:28 16943
1193b4be Poul Sander 2025-02-03 21:28 16944
ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 16945
{
1193b4be Poul Sander 2025-02-03 21:28 16946
    return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
1193b4be Poul Sander 2025-02-03 21:28 16947
}
1193b4be Poul Sander 2025-02-03 21:28 16948
1193b4be Poul Sander 2025-02-03 21:28 16949
ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 16950
{
1193b4be Poul Sander 2025-02-03 21:28 16951
    // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
1193b4be Poul Sander 2025-02-03 21:28 16952
    // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0
1193b4be Poul Sander 2025-02-03 21:28 16953
    // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted.
1193b4be Poul Sander 2025-02-03 21:28 16954
    ImGuiID id = 0x0001;
1193b4be Poul Sander 2025-02-03 21:28 16955
    while (DockContextFindNodeByID(ctx, id) != NULL)
1193b4be Poul Sander 2025-02-03 21:28 16956
        id++;
1193b4be Poul Sander 2025-02-03 21:28 16957
    return id;
1193b4be Poul Sander 2025-02-03 21:28 16958
}
1193b4be Poul Sander 2025-02-03 21:28 16959
1193b4be Poul Sander 2025-02-03 21:28 16960
static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 16961
{
1193b4be Poul Sander 2025-02-03 21:28 16962
    // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.
1193b4be Poul Sander 2025-02-03 21:28 16963
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16964
    if (id == 0)
1193b4be Poul Sander 2025-02-03 21:28 16965
        id = DockContextGenNodeID(ctx);
1193b4be Poul Sander 2025-02-03 21:28 16966
    else
1193b4be Poul Sander 2025-02-03 21:28 16967
        IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
1193b4be Poul Sander 2025-02-03 21:28 16968
1193b4be Poul Sander 2025-02-03 21:28 16969
    // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
1193b4be Poul Sander 2025-02-03 21:28 16970
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextAddNode 0x%08X\n", id);
1193b4be Poul Sander 2025-02-03 21:28 16971
    ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
1193b4be Poul Sander 2025-02-03 21:28 16972
    ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
1193b4be Poul Sander 2025-02-03 21:28 16973
    return node;
1193b4be Poul Sander 2025-02-03 21:28 16974
}
1193b4be Poul Sander 2025-02-03 21:28 16975
1193b4be Poul Sander 2025-02-03 21:28 16976
static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
1193b4be Poul Sander 2025-02-03 21:28 16977
{
1193b4be Poul Sander 2025-02-03 21:28 16978
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 16979
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 16980
1193b4be Poul Sander 2025-02-03 21:28 16981
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRemoveNode 0x%08X\n", node->ID);
1193b4be Poul Sander 2025-02-03 21:28 16982
    IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
1193b4be Poul Sander 2025-02-03 21:28 16983
    IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
1193b4be Poul Sander 2025-02-03 21:28 16984
    IM_ASSERT(node->Windows.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 16985
1193b4be Poul Sander 2025-02-03 21:28 16986
    if (node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 16987
        node->HostWindow->DockNodeAsHost = NULL;
1193b4be Poul Sander 2025-02-03 21:28 16988
1193b4be Poul Sander 2025-02-03 21:28 16989
    ImGuiDockNode* parent_node = node->ParentNode;
1193b4be Poul Sander 2025-02-03 21:28 16990
    const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
1193b4be Poul Sander 2025-02-03 21:28 16991
    if (merge)
1193b4be Poul Sander 2025-02-03 21:28 16992
    {
1193b4be Poul Sander 2025-02-03 21:28 16993
        IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
1193b4be Poul Sander 2025-02-03 21:28 16994
        ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
1193b4be Poul Sander 2025-02-03 21:28 16995
        DockNodeTreeMerge(&g, parent_node, sibling_node);
1193b4be Poul Sander 2025-02-03 21:28 16996
    }
1193b4be Poul Sander 2025-02-03 21:28 16997
    else
1193b4be Poul Sander 2025-02-03 21:28 16998
    {
1193b4be Poul Sander 2025-02-03 21:28 16999
        for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
1193b4be Poul Sander 2025-02-03 21:28 17000
            if (parent_node->ChildNodes[n] == node)
1193b4be Poul Sander 2025-02-03 21:28 17001
                node->ParentNode->ChildNodes[n] = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17002
        dc->Nodes.SetVoidPtr(node->ID, NULL);
1193b4be Poul Sander 2025-02-03 21:28 17003
        IM_DELETE(node);
1193b4be Poul Sander 2025-02-03 21:28 17004
    }
1193b4be Poul Sander 2025-02-03 21:28 17005
}
1193b4be Poul Sander 2025-02-03 21:28 17006
1193b4be Poul Sander 2025-02-03 21:28 17007
static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
1193b4be Poul Sander 2025-02-03 21:28 17008
{
1193b4be Poul Sander 2025-02-03 21:28 17009
    const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
1193b4be Poul Sander 2025-02-03 21:28 17010
    const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
1193b4be Poul Sander 2025-02-03 21:28 17011
    return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
1193b4be Poul Sander 2025-02-03 21:28 17012
}
1193b4be Poul Sander 2025-02-03 21:28 17013
1193b4be Poul Sander 2025-02-03 21:28 17014
// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
1193b4be Poul Sander 2025-02-03 21:28 17015
struct ImGuiDockContextPruneNodeData
1193b4be Poul Sander 2025-02-03 21:28 17016
{
1193b4be Poul Sander 2025-02-03 21:28 17017
    int         CountWindows, CountChildWindows, CountChildNodes;
1193b4be Poul Sander 2025-02-03 21:28 17018
    ImGuiID     RootId;
1193b4be Poul Sander 2025-02-03 21:28 17019
    ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; }
1193b4be Poul Sander 2025-02-03 21:28 17020
};
1193b4be Poul Sander 2025-02-03 21:28 17021
1193b4be Poul Sander 2025-02-03 21:28 17022
// Garbage collect unused nodes (run once at init time)
1193b4be Poul Sander 2025-02-03 21:28 17023
static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 17024
{
1193b4be Poul Sander 2025-02-03 21:28 17025
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 17026
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 17027
    IM_ASSERT(g.Windows.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 17028
1193b4be Poul Sander 2025-02-03 21:28 17029
    ImPool<ImGuiDockContextPruneNodeData> pool;
1193b4be Poul Sander 2025-02-03 21:28 17030
    pool.Reserve(dc->NodesSettings.Size);
1193b4be Poul Sander 2025-02-03 21:28 17031
1193b4be Poul Sander 2025-02-03 21:28 17032
    // Count child nodes and compute RootID
1193b4be Poul Sander 2025-02-03 21:28 17033
    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
1193b4be Poul Sander 2025-02-03 21:28 17034
    {
1193b4be Poul Sander 2025-02-03 21:28 17035
        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
1193b4be Poul Sander 2025-02-03 21:28 17036
        ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
1193b4be Poul Sander 2025-02-03 21:28 17037
        pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
1193b4be Poul Sander 2025-02-03 21:28 17038
        if (settings->ParentNodeId)
1193b4be Poul Sander 2025-02-03 21:28 17039
            pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
1193b4be Poul Sander 2025-02-03 21:28 17040
    }
1193b4be Poul Sander 2025-02-03 21:28 17041
1193b4be Poul Sander 2025-02-03 21:28 17042
    // Count reference to dock ids from dockspaces
1193b4be Poul Sander 2025-02-03 21:28 17043
    // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
1193b4be Poul Sander 2025-02-03 21:28 17044
    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
1193b4be Poul Sander 2025-02-03 21:28 17045
    {
1193b4be Poul Sander 2025-02-03 21:28 17046
        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
1193b4be Poul Sander 2025-02-03 21:28 17047
        if (settings->ParentWindowId != 0)
1193b4be Poul Sander 2025-02-03 21:28 17048
            if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId))
1193b4be Poul Sander 2025-02-03 21:28 17049
                if (window_settings->DockId)
1193b4be Poul Sander 2025-02-03 21:28 17050
                    if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
1193b4be Poul Sander 2025-02-03 21:28 17051
                        data->CountChildNodes++;
1193b4be Poul Sander 2025-02-03 21:28 17052
    }
1193b4be Poul Sander 2025-02-03 21:28 17053
1193b4be Poul Sander 2025-02-03 21:28 17054
    // Count reference to dock ids from window settings
1193b4be Poul Sander 2025-02-03 21:28 17055
    // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
1193b4be Poul Sander 2025-02-03 21:28 17056
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 17057
        if (ImGuiID dock_id = settings->DockId)
1193b4be Poul Sander 2025-02-03 21:28 17058
            if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
1193b4be Poul Sander 2025-02-03 21:28 17059
            {
1193b4be Poul Sander 2025-02-03 21:28 17060
                data->CountWindows++;
1193b4be Poul Sander 2025-02-03 21:28 17061
                if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
1193b4be Poul Sander 2025-02-03 21:28 17062
                    data_root->CountChildWindows++;
1193b4be Poul Sander 2025-02-03 21:28 17063
            }
1193b4be Poul Sander 2025-02-03 21:28 17064
1193b4be Poul Sander 2025-02-03 21:28 17065
    // Prune
1193b4be Poul Sander 2025-02-03 21:28 17066
    for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
1193b4be Poul Sander 2025-02-03 21:28 17067
    {
1193b4be Poul Sander 2025-02-03 21:28 17068
        ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
1193b4be Poul Sander 2025-02-03 21:28 17069
        ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
1193b4be Poul Sander 2025-02-03 21:28 17070
        if (data->CountWindows > 1)
1193b4be Poul Sander 2025-02-03 21:28 17071
            continue;
1193b4be Poul Sander 2025-02-03 21:28 17072
        ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
1193b4be Poul Sander 2025-02-03 21:28 17073
1193b4be Poul Sander 2025-02-03 21:28 17074
        bool remove = false;
1193b4be Poul Sander 2025-02-03 21:28 17075
        remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode));  // Floating root node with only 1 window
1193b4be Poul Sander 2025-02-03 21:28 17076
        remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
1193b4be Poul Sander 2025-02-03 21:28 17077
        remove |= (data_root->CountChildWindows == 0);
1193b4be Poul Sander 2025-02-03 21:28 17078
        if (remove)
1193b4be Poul Sander 2025-02-03 21:28 17079
        {
1193b4be Poul Sander 2025-02-03 21:28 17080
            IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
1193b4be Poul Sander 2025-02-03 21:28 17081
            DockSettingsRemoveNodeReferences(&settings->ID, 1);
1193b4be Poul Sander 2025-02-03 21:28 17082
            settings->ID = 0;
1193b4be Poul Sander 2025-02-03 21:28 17083
        }
1193b4be Poul Sander 2025-02-03 21:28 17084
    }
1193b4be Poul Sander 2025-02-03 21:28 17085
}
1193b4be Poul Sander 2025-02-03 21:28 17086
1193b4be Poul Sander 2025-02-03 21:28 17087
static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
1193b4be Poul Sander 2025-02-03 21:28 17088
{
1193b4be Poul Sander 2025-02-03 21:28 17089
    // Build nodes
1193b4be Poul Sander 2025-02-03 21:28 17090
    for (int node_n = 0; node_n < node_settings_count; node_n++)
1193b4be Poul Sander 2025-02-03 21:28 17091
    {
1193b4be Poul Sander 2025-02-03 21:28 17092
        ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
1193b4be Poul Sander 2025-02-03 21:28 17093
        if (settings->ID == 0)
1193b4be Poul Sander 2025-02-03 21:28 17094
            continue;
1193b4be Poul Sander 2025-02-03 21:28 17095
        ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
1193b4be Poul Sander 2025-02-03 21:28 17096
        node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 17097
        node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
1193b4be Poul Sander 2025-02-03 21:28 17098
        node->Size = ImVec2(settings->Size.x, settings->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 17099
        node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
1193b4be Poul Sander 2025-02-03 21:28 17100
        node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 17101
        if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17102
            node->ParentNode->ChildNodes[0] = node;
1193b4be Poul Sander 2025-02-03 21:28 17103
        else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17104
            node->ParentNode->ChildNodes[1] = node;
1193b4be Poul Sander 2025-02-03 21:28 17105
        node->SelectedTabId = settings->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 17106
        node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 17107
        node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
1193b4be Poul Sander 2025-02-03 21:28 17108
1193b4be Poul Sander 2025-02-03 21:28 17109
        // Bind host window immediately if it already exist (in case of a rebuild)
1193b4be Poul Sander 2025-02-03 21:28 17110
        // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
1193b4be Poul Sander 2025-02-03 21:28 17111
        char host_window_title[20];
1193b4be Poul Sander 2025-02-03 21:28 17112
        ImGuiDockNode* root_node = DockNodeGetRootNode(node);
1193b4be Poul Sander 2025-02-03 21:28 17113
        node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
1193b4be Poul Sander 2025-02-03 21:28 17114
    }
1193b4be Poul Sander 2025-02-03 21:28 17115
}
1193b4be Poul Sander 2025-02-03 21:28 17116
1193b4be Poul Sander 2025-02-03 21:28 17117
void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
1193b4be Poul Sander 2025-02-03 21:28 17118
{
1193b4be Poul Sander 2025-02-03 21:28 17119
    // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
1193b4be Poul Sander 2025-02-03 21:28 17120
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 17121
    for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 17122
    {
1193b4be Poul Sander 2025-02-03 21:28 17123
        if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
1193b4be Poul Sander 2025-02-03 21:28 17124
            continue;
1193b4be Poul Sander 2025-02-03 21:28 17125
        if (window->DockNode != NULL)
1193b4be Poul Sander 2025-02-03 21:28 17126
            continue;
1193b4be Poul Sander 2025-02-03 21:28 17127
1193b4be Poul Sander 2025-02-03 21:28 17128
        ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
1193b4be Poul Sander 2025-02-03 21:28 17129
        IM_ASSERT(node != NULL);   // This should have been called after DockContextBuildNodesFromSettings()
1193b4be Poul Sander 2025-02-03 21:28 17130
        if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
1193b4be Poul Sander 2025-02-03 21:28 17131
            DockNodeAddWindow(node, window, true);
1193b4be Poul Sander 2025-02-03 21:28 17132
    }
1193b4be Poul Sander 2025-02-03 21:28 17133
}
1193b4be Poul Sander 2025-02-03 21:28 17134
1193b4be Poul Sander 2025-02-03 21:28 17135
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17136
// Docking: ImGuiDockContext Docking/Undocking functions
1193b4be Poul Sander 2025-02-03 21:28 17137
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17138
// - DockContextQueueDock()
1193b4be Poul Sander 2025-02-03 21:28 17139
// - DockContextQueueUndockWindow()
1193b4be Poul Sander 2025-02-03 21:28 17140
// - DockContextQueueUndockNode()
1193b4be Poul Sander 2025-02-03 21:28 17141
// - DockContextQueueNotifyRemovedNode()
1193b4be Poul Sander 2025-02-03 21:28 17142
// - DockContextProcessDock()
1193b4be Poul Sander 2025-02-03 21:28 17143
// - DockContextProcessUndockWindow()
1193b4be Poul Sander 2025-02-03 21:28 17144
// - DockContextProcessUndockNode()
1193b4be Poul Sander 2025-02-03 21:28 17145
// - DockContextCalcDropPosForDocking()
1193b4be Poul Sander 2025-02-03 21:28 17146
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17147
1193b4be Poul Sander 2025-02-03 21:28 17148
void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
1193b4be Poul Sander 2025-02-03 21:28 17149
{
1193b4be Poul Sander 2025-02-03 21:28 17150
    IM_ASSERT(target != payload);
1193b4be Poul Sander 2025-02-03 21:28 17151
    ImGuiDockRequest req;
1193b4be Poul Sander 2025-02-03 21:28 17152
    req.Type = ImGuiDockRequestType_Dock;
1193b4be Poul Sander 2025-02-03 21:28 17153
    req.DockTargetWindow = target;
1193b4be Poul Sander 2025-02-03 21:28 17154
    req.DockTargetNode = target_node;
1193b4be Poul Sander 2025-02-03 21:28 17155
    req.DockPayload = payload;
1193b4be Poul Sander 2025-02-03 21:28 17156
    req.DockSplitDir = split_dir;
1193b4be Poul Sander 2025-02-03 21:28 17157
    req.DockSplitRatio = split_ratio;
1193b4be Poul Sander 2025-02-03 21:28 17158
    req.DockSplitOuter = split_outer;
1193b4be Poul Sander 2025-02-03 21:28 17159
    ctx->DockContext.Requests.push_back(req);
1193b4be Poul Sander 2025-02-03 21:28 17160
}
1193b4be Poul Sander 2025-02-03 21:28 17161
1193b4be Poul Sander 2025-02-03 21:28 17162
void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 17163
{
1193b4be Poul Sander 2025-02-03 21:28 17164
    ImGuiDockRequest req;
1193b4be Poul Sander 2025-02-03 21:28 17165
    req.Type = ImGuiDockRequestType_Undock;
1193b4be Poul Sander 2025-02-03 21:28 17166
    req.UndockTargetWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 17167
    ctx->DockContext.Requests.push_back(req);
1193b4be Poul Sander 2025-02-03 21:28 17168
}
1193b4be Poul Sander 2025-02-03 21:28 17169
1193b4be Poul Sander 2025-02-03 21:28 17170
void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17171
{
1193b4be Poul Sander 2025-02-03 21:28 17172
    ImGuiDockRequest req;
1193b4be Poul Sander 2025-02-03 21:28 17173
    req.Type = ImGuiDockRequestType_Undock;
1193b4be Poul Sander 2025-02-03 21:28 17174
    req.UndockTargetNode = node;
1193b4be Poul Sander 2025-02-03 21:28 17175
    ctx->DockContext.Requests.push_back(req);
1193b4be Poul Sander 2025-02-03 21:28 17176
}
1193b4be Poul Sander 2025-02-03 21:28 17177
1193b4be Poul Sander 2025-02-03 21:28 17178
void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17179
{
1193b4be Poul Sander 2025-02-03 21:28 17180
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 17181
    for (ImGuiDockRequest& req : dc->Requests)
1193b4be Poul Sander 2025-02-03 21:28 17182
        if (req.DockTargetNode == node)
1193b4be Poul Sander 2025-02-03 21:28 17183
            req.Type = ImGuiDockRequestType_None;
1193b4be Poul Sander 2025-02-03 21:28 17184
}
1193b4be Poul Sander 2025-02-03 21:28 17185
1193b4be Poul Sander 2025-02-03 21:28 17186
void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
1193b4be Poul Sander 2025-02-03 21:28 17187
{
1193b4be Poul Sander 2025-02-03 21:28 17188
    IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
1193b4be Poul Sander 2025-02-03 21:28 17189
    IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
1193b4be Poul Sander 2025-02-03 21:28 17190
1193b4be Poul Sander 2025-02-03 21:28 17191
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 17192
    IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 17193
1193b4be Poul Sander 2025-02-03 21:28 17194
    ImGuiWindow* payload_window = req->DockPayload;     // Optional
1193b4be Poul Sander 2025-02-03 21:28 17195
    ImGuiWindow* target_window = req->DockTargetWindow;
1193b4be Poul Sander 2025-02-03 21:28 17196
    ImGuiDockNode* node = req->DockTargetNode;
1193b4be Poul Sander 2025-02-03 21:28 17197
    if (payload_window)
1193b4be Poul Sander 2025-02-03 21:28 17198
        IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window->Name, req->DockSplitDir);
1193b4be Poul Sander 2025-02-03 21:28 17199
    else
1193b4be Poul Sander 2025-02-03 21:28 17200
        IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
1193b4be Poul Sander 2025-02-03 21:28 17201
1193b4be Poul Sander 2025-02-03 21:28 17202
    // Decide which Tab will be selected at the end of the operation
1193b4be Poul Sander 2025-02-03 21:28 17203
    ImGuiID next_selected_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 17204
    ImGuiDockNode* payload_node = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17205
    if (payload_window)
1193b4be Poul Sander 2025-02-03 21:28 17206
    {
1193b4be Poul Sander 2025-02-03 21:28 17207
        payload_node = payload_window->DockNodeAsHost;
1193b4be Poul Sander 2025-02-03 21:28 17208
        payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
1193b4be Poul Sander 2025-02-03 21:28 17209
        if (payload_node && payload_node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 17210
            next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 17211
        if (payload_node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17212
            next_selected_id = payload_window->TabId;
1193b4be Poul Sander 2025-02-03 21:28 17213
    }
1193b4be Poul Sander 2025-02-03 21:28 17214
1193b4be Poul Sander 2025-02-03 21:28 17215
    // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
1193b4be Poul Sander 2025-02-03 21:28 17216
    // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
1193b4be Poul Sander 2025-02-03 21:28 17217
    if (node)
1193b4be Poul Sander 2025-02-03 21:28 17218
        IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 17219
    if (node && target_window && node == target_window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 17220
        IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
1193b4be Poul Sander 2025-02-03 21:28 17221
1193b4be Poul Sander 2025-02-03 21:28 17222
    // Create new node and add existing window to it
1193b4be Poul Sander 2025-02-03 21:28 17223
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17224
    {
1193b4be Poul Sander 2025-02-03 21:28 17225
        node = DockContextAddNode(ctx, 0);
1193b4be Poul Sander 2025-02-03 21:28 17226
        node->Pos = target_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 17227
        node->Size = target_window->Size;
1193b4be Poul Sander 2025-02-03 21:28 17228
        if (target_window->DockNodeAsHost == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17229
        {
1193b4be Poul Sander 2025-02-03 21:28 17230
            DockNodeAddWindow(node, target_window, true);
1193b4be Poul Sander 2025-02-03 21:28 17231
            node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
1193b4be Poul Sander 2025-02-03 21:28 17232
            target_window->DockIsActive = true;
1193b4be Poul Sander 2025-02-03 21:28 17233
        }
1193b4be Poul Sander 2025-02-03 21:28 17234
    }
1193b4be Poul Sander 2025-02-03 21:28 17235
1193b4be Poul Sander 2025-02-03 21:28 17236
    ImGuiDir split_dir = req->DockSplitDir;
1193b4be Poul Sander 2025-02-03 21:28 17237
    if (split_dir != ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 17238
    {
1193b4be Poul Sander 2025-02-03 21:28 17239
        // Split into two, one side will be our payload node unless we are dropping a loose window
1193b4be Poul Sander 2025-02-03 21:28 17240
        const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
1193b4be Poul Sander 2025-02-03 21:28 17241
        const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side
1193b4be Poul Sander 2025-02-03 21:28 17242
        const float split_ratio = req->DockSplitRatio;
1193b4be Poul Sander 2025-02-03 21:28 17243
        DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node);  // payload_node may be NULL here!
1193b4be Poul Sander 2025-02-03 21:28 17244
        ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
1193b4be Poul Sander 2025-02-03 21:28 17245
        new_node->HostWindow = node->HostWindow;
1193b4be Poul Sander 2025-02-03 21:28 17246
        node = new_node;
1193b4be Poul Sander 2025-02-03 21:28 17247
    }
1193b4be Poul Sander 2025-02-03 21:28 17248
    node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
1193b4be Poul Sander 2025-02-03 21:28 17249
1193b4be Poul Sander 2025-02-03 21:28 17250
    if (node != payload_node)
1193b4be Poul Sander 2025-02-03 21:28 17251
    {
1193b4be Poul Sander 2025-02-03 21:28 17252
        // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
1193b4be Poul Sander 2025-02-03 21:28 17253
        if (node->Windows.Size > 0 && node->TabBar == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17254
        {
1193b4be Poul Sander 2025-02-03 21:28 17255
            DockNodeAddTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 17256
            for (int n = 0; n < node->Windows.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 17257
                TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
1193b4be Poul Sander 2025-02-03 21:28 17258
        }
1193b4be Poul Sander 2025-02-03 21:28 17259
1193b4be Poul Sander 2025-02-03 21:28 17260
        if (payload_node != NULL)
1193b4be Poul Sander 2025-02-03 21:28 17261
        {
1193b4be Poul Sander 2025-02-03 21:28 17262
            // Transfer full payload node (with 1+ child windows or child nodes)
1193b4be Poul Sander 2025-02-03 21:28 17263
            if (payload_node->IsSplitNode())
1193b4be Poul Sander 2025-02-03 21:28 17264
            {
1193b4be Poul Sander 2025-02-03 21:28 17265
                if (node->Windows.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 17266
                {
1193b4be Poul Sander 2025-02-03 21:28 17267
                    // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node.
1193b4be Poul Sander 2025-02-03 21:28 17268
                    // In this situation, we move the windows of the target node into the currently visible node of the payload.
1193b4be Poul Sander 2025-02-03 21:28 17269
                    // This allows us to preserve some of the underlying dock tree settings nicely.
1193b4be Poul Sander 2025-02-03 21:28 17270
                    IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
1193b4be Poul Sander 2025-02-03 21:28 17271
                    ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
1193b4be Poul Sander 2025-02-03 21:28 17272
                    if (visible_node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17273
                        IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 17274
                    DockNodeMoveWindows(node, visible_node);
1193b4be Poul Sander 2025-02-03 21:28 17275
                    DockNodeMoveWindows(visible_node, node);
1193b4be Poul Sander 2025-02-03 21:28 17276
                    DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17277
                }
1193b4be Poul Sander 2025-02-03 21:28 17278
                if (node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 17279
                {
1193b4be Poul Sander 2025-02-03 21:28 17280
                    // Central node property needs to be moved to a leaf node, pick the last focused one.
1193b4be Poul Sander 2025-02-03 21:28 17281
                    // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
1193b4be Poul Sander 2025-02-03 21:28 17282
                    ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
1193b4be Poul Sander 2025-02-03 21:28 17283
                    IM_ASSERT(last_focused_node != NULL);
1193b4be Poul Sander 2025-02-03 21:28 17284
                    ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
1193b4be Poul Sander 2025-02-03 21:28 17285
                    IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
1193b4be Poul Sander 2025-02-03 21:28 17286
                    last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 17287
                    node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 17288
                    last_focused_root_node->CentralNode = last_focused_node;
1193b4be Poul Sander 2025-02-03 21:28 17289
                }
1193b4be Poul Sander 2025-02-03 21:28 17290
1193b4be Poul Sander 2025-02-03 21:28 17291
                IM_ASSERT(node->Windows.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 17292
                DockNodeMoveChildNodes(node, payload_node);
1193b4be Poul Sander 2025-02-03 21:28 17293
            }
1193b4be Poul Sander 2025-02-03 21:28 17294
            else
1193b4be Poul Sander 2025-02-03 21:28 17295
            {
1193b4be Poul Sander 2025-02-03 21:28 17296
                const ImGuiID payload_dock_id = payload_node->ID;
1193b4be Poul Sander 2025-02-03 21:28 17297
                DockNodeMoveWindows(node, payload_node);
1193b4be Poul Sander 2025-02-03 21:28 17298
                DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17299
            }
1193b4be Poul Sander 2025-02-03 21:28 17300
            DockContextRemoveNode(ctx, payload_node, true);
1193b4be Poul Sander 2025-02-03 21:28 17301
        }
1193b4be Poul Sander 2025-02-03 21:28 17302
        else if (payload_window)
1193b4be Poul Sander 2025-02-03 21:28 17303
        {
1193b4be Poul Sander 2025-02-03 21:28 17304
            // Transfer single window
1193b4be Poul Sander 2025-02-03 21:28 17305
            const ImGuiID payload_dock_id = payload_window->DockId;
1193b4be Poul Sander 2025-02-03 21:28 17306
            node->VisibleWindow = payload_window;
1193b4be Poul Sander 2025-02-03 21:28 17307
            DockNodeAddWindow(node, payload_window, true);
1193b4be Poul Sander 2025-02-03 21:28 17308
            if (payload_dock_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 17309
                DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17310
        }
1193b4be Poul Sander 2025-02-03 21:28 17311
    }
1193b4be Poul Sander 2025-02-03 21:28 17312
    else
1193b4be Poul Sander 2025-02-03 21:28 17313
    {
1193b4be Poul Sander 2025-02-03 21:28 17314
        // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
1193b4be Poul Sander 2025-02-03 21:28 17315
        node->WantHiddenTabBarUpdate = true;
1193b4be Poul Sander 2025-02-03 21:28 17316
    }
1193b4be Poul Sander 2025-02-03 21:28 17317
1193b4be Poul Sander 2025-02-03 21:28 17318
    // Update selection immediately
1193b4be Poul Sander 2025-02-03 21:28 17319
    if (ImGuiTabBar* tab_bar = node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17320
        tab_bar->NextSelectedTabId = next_selected_id;
1193b4be Poul Sander 2025-02-03 21:28 17321
    MarkIniSettingsDirty();
1193b4be Poul Sander 2025-02-03 21:28 17322
}
1193b4be Poul Sander 2025-02-03 21:28 17323
1193b4be Poul Sander 2025-02-03 21:28 17324
// Problem:
1193b4be Poul Sander 2025-02-03 21:28 17325
//   Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more
1193b4be Poul Sander 2025-02-03 21:28 17326
//   than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic
1193b4be Poul Sander 2025-02-03 21:28 17327
//   with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be
1193b4be Poul Sander 2025-02-03 21:28 17328
//   due to missing ImGuiBackendFlags_HasMouseCursors backend flag).
1193b4be Poul Sander 2025-02-03 21:28 17329
// Solution:
1193b4be Poul Sander 2025-02-03 21:28 17330
//   When undocking a window we currently force its maximum size to 90% of the host viewport or monitor.
1193b4be Poul Sander 2025-02-03 21:28 17331
// Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch).
1193b4be Poul Sander 2025-02-03 21:28 17332
static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport)
1193b4be Poul Sander 2025-02-03 21:28 17333
{
1193b4be Poul Sander 2025-02-03 21:28 17334
    if (ref_viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17335
        return size;
1193b4be Poul Sander 2025-02-03 21:28 17336
1193b4be Poul Sander 2025-02-03 21:28 17337
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 17338
    ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f);
1193b4be Poul Sander 2025-02-03 21:28 17339
    if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
1193b4be Poul Sander 2025-02-03 21:28 17340
    {
1193b4be Poul Sander 2025-02-03 21:28 17341
        const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport);
1193b4be Poul Sander 2025-02-03 21:28 17342
        max_size = ImTrunc(monitor->WorkSize * 0.90f);
1193b4be Poul Sander 2025-02-03 21:28 17343
    }
1193b4be Poul Sander 2025-02-03 21:28 17344
    return ImMin(size, max_size);
1193b4be Poul Sander 2025-02-03 21:28 17345
}
1193b4be Poul Sander 2025-02-03 21:28 17346
1193b4be Poul Sander 2025-02-03 21:28 17347
void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
1193b4be Poul Sander 2025-02-03 21:28 17348
{
1193b4be Poul Sander 2025-02-03 21:28 17349
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 17350
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
1193b4be Poul Sander 2025-02-03 21:28 17351
    if (window->DockNode)
1193b4be Poul Sander 2025-02-03 21:28 17352
        DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
1193b4be Poul Sander 2025-02-03 21:28 17353
    else
1193b4be Poul Sander 2025-02-03 21:28 17354
        window->DockId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17355
    window->Collapsed = false;
1193b4be Poul Sander 2025-02-03 21:28 17356
    window->DockIsActive = false;
1193b4be Poul Sander 2025-02-03 21:28 17357
    window->DockNodeIsVisible = window->DockTabIsVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 17358
    window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 17359
1193b4be Poul Sander 2025-02-03 21:28 17360
    MarkIniSettingsDirty();
1193b4be Poul Sander 2025-02-03 21:28 17361
}
1193b4be Poul Sander 2025-02-03 21:28 17362
1193b4be Poul Sander 2025-02-03 21:28 17363
void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17364
{
1193b4be Poul Sander 2025-02-03 21:28 17365
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 17366
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockNode node %08X\n", node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17367
    IM_ASSERT(node->IsLeafNode());
1193b4be Poul Sander 2025-02-03 21:28 17368
    IM_ASSERT(node->Windows.Size >= 1);
1193b4be Poul Sander 2025-02-03 21:28 17369
1193b4be Poul Sander 2025-02-03 21:28 17370
    if (node->IsRootNode() || node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 17371
    {
1193b4be Poul Sander 2025-02-03 21:28 17372
        // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload.
1193b4be Poul Sander 2025-02-03 21:28 17373
        ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
1193b4be Poul Sander 2025-02-03 21:28 17374
        new_node->Pos = node->Pos;
1193b4be Poul Sander 2025-02-03 21:28 17375
        new_node->Size = node->Size;
1193b4be Poul Sander 2025-02-03 21:28 17376
        new_node->SizeRef = node->SizeRef;
1193b4be Poul Sander 2025-02-03 21:28 17377
        DockNodeMoveWindows(new_node, node);
1193b4be Poul Sander 2025-02-03 21:28 17378
        DockSettingsRenameNodeReferences(node->ID, new_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17379
        node = new_node;
1193b4be Poul Sander 2025-02-03 21:28 17380
    }
1193b4be Poul Sander 2025-02-03 21:28 17381
    else
1193b4be Poul Sander 2025-02-03 21:28 17382
    {
1193b4be Poul Sander 2025-02-03 21:28 17383
        // Otherwise extract our node and merge our sibling back into the parent node.
1193b4be Poul Sander 2025-02-03 21:28 17384
        IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
1193b4be Poul Sander 2025-02-03 21:28 17385
        int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
1193b4be Poul Sander 2025-02-03 21:28 17386
        node->ParentNode->ChildNodes[index_in_parent] = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17387
        DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
1193b4be Poul Sander 2025-02-03 21:28 17388
        node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport
1193b4be Poul Sander 2025-02-03 21:28 17389
        node->ParentNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17390
    }
1193b4be Poul Sander 2025-02-03 21:28 17391
    for (ImGuiWindow* window : node->Windows)
1193b4be Poul Sander 2025-02-03 21:28 17392
    {
1193b4be Poul Sander 2025-02-03 21:28 17393
        window->Flags &= ~ImGuiWindowFlags_ChildWindow;
1193b4be Poul Sander 2025-02-03 21:28 17394
        if (window->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 17395
            window->ParentWindow->DC.ChildWindows.find_erase(window);
1193b4be Poul Sander 2025-02-03 21:28 17396
        UpdateWindowParentAndRootLinks(window, window->Flags, NULL);
1193b4be Poul Sander 2025-02-03 21:28 17397
    }
1193b4be Poul Sander 2025-02-03 21:28 17398
    node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 17399
    node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 17400
    node->WantMouseMove = true;
1193b4be Poul Sander 2025-02-03 21:28 17401
    MarkIniSettingsDirty();
1193b4be Poul Sander 2025-02-03 21:28 17402
}
1193b4be Poul Sander 2025-02-03 21:28 17403
1193b4be Poul Sander 2025-02-03 21:28 17404
// This is mostly used for automation.
1193b4be Poul Sander 2025-02-03 21:28 17405
bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
1193b4be Poul Sander 2025-02-03 21:28 17406
{
1193b4be Poul Sander 2025-02-03 21:28 17407
    if (target != NULL && target_node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17408
        target_node = target->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 17409
1193b4be Poul Sander 2025-02-03 21:28 17410
    // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects
1193b4be Poul Sander 2025-02-03 21:28 17411
    // (which would be functionally identical) we only show the outer one. Reflect this here.
1193b4be Poul Sander 2025-02-03 21:28 17412
    if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 17413
        split_outer = true;
1193b4be Poul Sander 2025-02-03 21:28 17414
    ImGuiDockPreviewData split_data;
1193b4be Poul Sander 2025-02-03 21:28 17415
    DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer);
1193b4be Poul Sander 2025-02-03 21:28 17416
    if (split_data.DropRectsDraw[split_dir+1].IsInverted())
1193b4be Poul Sander 2025-02-03 21:28 17417
        return false;
1193b4be Poul Sander 2025-02-03 21:28 17418
    *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
1193b4be Poul Sander 2025-02-03 21:28 17419
    return true;
1193b4be Poul Sander 2025-02-03 21:28 17420
}
1193b4be Poul Sander 2025-02-03 21:28 17421
1193b4be Poul Sander 2025-02-03 21:28 17422
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17423
// Docking: ImGuiDockNode
1193b4be Poul Sander 2025-02-03 21:28 17424
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17425
// - DockNodeGetTabOrder()
1193b4be Poul Sander 2025-02-03 21:28 17426
// - DockNodeAddWindow()
1193b4be Poul Sander 2025-02-03 21:28 17427
// - DockNodeRemoveWindow()
1193b4be Poul Sander 2025-02-03 21:28 17428
// - DockNodeMoveChildNodes()
1193b4be Poul Sander 2025-02-03 21:28 17429
// - DockNodeMoveWindows()
1193b4be Poul Sander 2025-02-03 21:28 17430
// - DockNodeApplyPosSizeToWindows()
1193b4be Poul Sander 2025-02-03 21:28 17431
// - DockNodeHideHostWindow()
1193b4be Poul Sander 2025-02-03 21:28 17432
// - ImGuiDockNodeFindInfoResults
1193b4be Poul Sander 2025-02-03 21:28 17433
// - DockNodeFindInfo()
1193b4be Poul Sander 2025-02-03 21:28 17434
// - DockNodeFindWindowByID()
1193b4be Poul Sander 2025-02-03 21:28 17435
// - DockNodeUpdateFlagsAndCollapse()
1193b4be Poul Sander 2025-02-03 21:28 17436
// - DockNodeUpdateHasCentralNodeFlag()
1193b4be Poul Sander 2025-02-03 21:28 17437
// - DockNodeUpdateVisibleFlag()
1193b4be Poul Sander 2025-02-03 21:28 17438
// - DockNodeStartMouseMovingWindow()
1193b4be Poul Sander 2025-02-03 21:28 17439
// - DockNodeUpdate()
1193b4be Poul Sander 2025-02-03 21:28 17440
// - DockNodeUpdateWindowMenu()
1193b4be Poul Sander 2025-02-03 21:28 17441
// - DockNodeBeginAmendTabBar()
1193b4be Poul Sander 2025-02-03 21:28 17442
// - DockNodeEndAmendTabBar()
1193b4be Poul Sander 2025-02-03 21:28 17443
// - DockNodeUpdateTabBar()
1193b4be Poul Sander 2025-02-03 21:28 17444
// - DockNodeAddTabBar()
1193b4be Poul Sander 2025-02-03 21:28 17445
// - DockNodeRemoveTabBar()
1193b4be Poul Sander 2025-02-03 21:28 17446
// - DockNodeIsDropAllowedOne()
1193b4be Poul Sander 2025-02-03 21:28 17447
// - DockNodeIsDropAllowed()
1193b4be Poul Sander 2025-02-03 21:28 17448
// - DockNodeCalcTabBarLayout()
1193b4be Poul Sander 2025-02-03 21:28 17449
// - DockNodeCalcSplitRects()
1193b4be Poul Sander 2025-02-03 21:28 17450
// - DockNodeCalcDropRectsAndTestMousePos()
1193b4be Poul Sander 2025-02-03 21:28 17451
// - DockNodePreviewDockSetup()
1193b4be Poul Sander 2025-02-03 21:28 17452
// - DockNodePreviewDockRender()
1193b4be Poul Sander 2025-02-03 21:28 17453
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 17454
1193b4be Poul Sander 2025-02-03 21:28 17455
ImGuiDockNode::ImGuiDockNode(ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 17456
{
1193b4be Poul Sander 2025-02-03 21:28 17457
    ID = id;
1193b4be Poul Sander 2025-02-03 21:28 17458
    SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 17459
    ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17460
    TabBar = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17461
    SplitAxis = ImGuiAxis_None;
1193b4be Poul Sander 2025-02-03 21:28 17462
1193b4be Poul Sander 2025-02-03 21:28 17463
    State = ImGuiDockNodeState_Unknown;
1193b4be Poul Sander 2025-02-03 21:28 17464
    LastBgColor = IM_COL32_WHITE;
1193b4be Poul Sander 2025-02-03 21:28 17465
    HostWindow = VisibleWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17466
    CentralNode = OnlyNodeWithWindows = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17467
    CountNodeWithWindows = 0;
1193b4be Poul Sander 2025-02-03 21:28 17468
    LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
1193b4be Poul Sander 2025-02-03 21:28 17469
    LastFocusedNodeId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17470
    SelectedTabId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17471
    WantCloseTabId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17472
    RefViewportId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17473
    AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 17474
    AuthorityForViewport = ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 17475
    IsVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 17476
    IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;
1193b4be Poul Sander 2025-02-03 21:28 17477
    IsBgDrawnThisFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 17478
    WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
1193b4be Poul Sander 2025-02-03 21:28 17479
}
1193b4be Poul Sander 2025-02-03 21:28 17480
1193b4be Poul Sander 2025-02-03 21:28 17481
ImGuiDockNode::~ImGuiDockNode()
1193b4be Poul Sander 2025-02-03 21:28 17482
{
1193b4be Poul Sander 2025-02-03 21:28 17483
    IM_DELETE(TabBar);
1193b4be Poul Sander 2025-02-03 21:28 17484
    TabBar = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17485
    ChildNodes[0] = ChildNodes[1] = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17486
}
1193b4be Poul Sander 2025-02-03 21:28 17487
1193b4be Poul Sander 2025-02-03 21:28 17488
int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 17489
{
1193b4be Poul Sander 2025-02-03 21:28 17490
    ImGuiTabBar* tab_bar = window->DockNode->TabBar;
1193b4be Poul Sander 2025-02-03 21:28 17491
    if (tab_bar == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17492
        return -1;
1193b4be Poul Sander 2025-02-03 21:28 17493
    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);
1193b4be Poul Sander 2025-02-03 21:28 17494
    return tab ? TabBarGetTabOrder(tab_bar, tab) : -1;
1193b4be Poul Sander 2025-02-03 21:28 17495
}
1193b4be Poul Sander 2025-02-03 21:28 17496
1193b4be Poul Sander 2025-02-03 21:28 17497
static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 17498
{
1193b4be Poul Sander 2025-02-03 21:28 17499
    window->Hidden = true;
1193b4be Poul Sander 2025-02-03 21:28 17500
    window->HiddenFramesCanSkipItems = window->Active ? 1 : 2;
1193b4be Poul Sander 2025-02-03 21:28 17501
}
1193b4be Poul Sander 2025-02-03 21:28 17502
1193b4be Poul Sander 2025-02-03 21:28 17503
static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 17504
{
1193b4be Poul Sander 2025-02-03 21:28 17505
    ImGuiContext& g = *GImGui; (void)g;
1193b4be Poul Sander 2025-02-03 21:28 17506
    if (window->DockNode)
1193b4be Poul Sander 2025-02-03 21:28 17507
    {
1193b4be Poul Sander 2025-02-03 21:28 17508
        // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
1193b4be Poul Sander 2025-02-03 21:28 17509
        IM_ASSERT(window->DockNode->ID != node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17510
        DockNodeRemoveWindow(window->DockNode, window, 0);
1193b4be Poul Sander 2025-02-03 21:28 17511
    }
1193b4be Poul Sander 2025-02-03 21:28 17512
    IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
1193b4be Poul Sander 2025-02-03 21:28 17513
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 17514
1193b4be Poul Sander 2025-02-03 21:28 17515
    // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,
1193b4be Poul Sander 2025-02-03 21:28 17516
    // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame).
1193b4be Poul Sander 2025-02-03 21:28 17517
    // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()
1193b4be Poul Sander 2025-02-03 21:28 17518
    if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false)
1193b4be Poul Sander 2025-02-03 21:28 17519
        DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]);
1193b4be Poul Sander 2025-02-03 21:28 17520
1193b4be Poul Sander 2025-02-03 21:28 17521
    node->Windows.push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 17522
    node->WantHiddenTabBarUpdate = true;
1193b4be Poul Sander 2025-02-03 21:28 17523
    window->DockNode = node;
1193b4be Poul Sander 2025-02-03 21:28 17524
    window->DockId = node->ID;
1193b4be Poul Sander 2025-02-03 21:28 17525
    window->DockIsActive = (node->Windows.Size > 1);
1193b4be Poul Sander 2025-02-03 21:28 17526
    window->DockTabWantClose = false;
1193b4be Poul Sander 2025-02-03 21:28 17527
1193b4be Poul Sander 2025-02-03 21:28 17528
    // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
1193b4be Poul Sander 2025-02-03 21:28 17529
    // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
1193b4be Poul Sander 2025-02-03 21:28 17530
    if (node->HostWindow == NULL && node->IsFloatingNode())
1193b4be Poul Sander 2025-02-03 21:28 17531
    {
1193b4be Poul Sander 2025-02-03 21:28 17532
        if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
1193b4be Poul Sander 2025-02-03 21:28 17533
            node->AuthorityForPos = ImGuiDataAuthority_Window;
1193b4be Poul Sander 2025-02-03 21:28 17534
        if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
1193b4be Poul Sander 2025-02-03 21:28 17535
            node->AuthorityForSize = ImGuiDataAuthority_Window;
1193b4be Poul Sander 2025-02-03 21:28 17536
        if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
1193b4be Poul Sander 2025-02-03 21:28 17537
            node->AuthorityForViewport = ImGuiDataAuthority_Window;
1193b4be Poul Sander 2025-02-03 21:28 17538
    }
1193b4be Poul Sander 2025-02-03 21:28 17539
1193b4be Poul Sander 2025-02-03 21:28 17540
    // Add to tab bar if requested
1193b4be Poul Sander 2025-02-03 21:28 17541
    if (add_to_tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 17542
    {
1193b4be Poul Sander 2025-02-03 21:28 17543
        if (node->TabBar == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17544
        {
1193b4be Poul Sander 2025-02-03 21:28 17545
            DockNodeAddTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 17546
            node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 17547
1193b4be Poul Sander 2025-02-03 21:28 17548
            // Add existing windows
1193b4be Poul Sander 2025-02-03 21:28 17549
            for (int n = 0; n < node->Windows.Size - 1; n++)
1193b4be Poul Sander 2025-02-03 21:28 17550
                TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
1193b4be Poul Sander 2025-02-03 21:28 17551
        }
1193b4be Poul Sander 2025-02-03 21:28 17552
        TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
1193b4be Poul Sander 2025-02-03 21:28 17553
    }
1193b4be Poul Sander 2025-02-03 21:28 17554
1193b4be Poul Sander 2025-02-03 21:28 17555
    DockNodeUpdateVisibleFlag(node);
1193b4be Poul Sander 2025-02-03 21:28 17556
1193b4be Poul Sander 2025-02-03 21:28 17557
    // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame.
1193b4be Poul Sander 2025-02-03 21:28 17558
    if (node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 17559
        UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 17560
}
1193b4be Poul Sander 2025-02-03 21:28 17561
1193b4be Poul Sander 2025-02-03 21:28 17562
static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
1193b4be Poul Sander 2025-02-03 21:28 17563
{
1193b4be Poul Sander 2025-02-03 21:28 17564
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 17565
    IM_ASSERT(window->DockNode == node);
1193b4be Poul Sander 2025-02-03 21:28 17566
    //IM_ASSERT(window->RootWindowDockTree == node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 17567
    //IM_ASSERT(window->LastFrameActive < g.FrameCount);    // We may call this from Begin()
1193b4be Poul Sander 2025-02-03 21:28 17568
    IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17569
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 17570
1193b4be Poul Sander 2025-02-03 21:28 17571
    window->DockNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17572
    window->DockIsActive = window->DockTabWantClose = false;
1193b4be Poul Sander 2025-02-03 21:28 17573
    window->DockId = save_dock_id;
1193b4be Poul Sander 2025-02-03 21:28 17574
    window->Flags &= ~ImGuiWindowFlags_ChildWindow;
1193b4be Poul Sander 2025-02-03 21:28 17575
    if (window->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 17576
        window->ParentWindow->DC.ChildWindows.find_erase(window);
1193b4be Poul Sander 2025-02-03 21:28 17577
    UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately
1193b4be Poul Sander 2025-02-03 21:28 17578
1193b4be Poul Sander 2025-02-03 21:28 17579
    if (node->HostWindow && node->HostWindow->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 17580
    {
1193b4be Poul Sander 2025-02-03 21:28 17581
        // When undocking from a user interaction this will always run in NewFrame() and have not much effect.
1193b4be Poul Sander 2025-02-03 21:28 17582
        // But mid-frame, if we clear viewport we need to mark window as hidden as well.
1193b4be Poul Sander 2025-02-03 21:28 17583
        window->Viewport = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17584
        window->ViewportId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17585
        window->ViewportOwned = false;
1193b4be Poul Sander 2025-02-03 21:28 17586
        window->Hidden = true;
1193b4be Poul Sander 2025-02-03 21:28 17587
    }
1193b4be Poul Sander 2025-02-03 21:28 17588
1193b4be Poul Sander 2025-02-03 21:28 17589
    // Remove window
1193b4be Poul Sander 2025-02-03 21:28 17590
    bool erased = false;
1193b4be Poul Sander 2025-02-03 21:28 17591
    for (int n = 0; n < node->Windows.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 17592
        if (node->Windows[n] == window)
1193b4be Poul Sander 2025-02-03 21:28 17593
        {
1193b4be Poul Sander 2025-02-03 21:28 17594
            node->Windows.erase(node->Windows.Data + n);
1193b4be Poul Sander 2025-02-03 21:28 17595
            erased = true;
1193b4be Poul Sander 2025-02-03 21:28 17596
            break;
1193b4be Poul Sander 2025-02-03 21:28 17597
        }
1193b4be Poul Sander 2025-02-03 21:28 17598
    if (!erased)
1193b4be Poul Sander 2025-02-03 21:28 17599
        IM_ASSERT(erased);
1193b4be Poul Sander 2025-02-03 21:28 17600
    if (node->VisibleWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 17601
        node->VisibleWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17602
1193b4be Poul Sander 2025-02-03 21:28 17603
    // Remove tab and possibly tab bar
1193b4be Poul Sander 2025-02-03 21:28 17604
    node->WantHiddenTabBarUpdate = true;
1193b4be Poul Sander 2025-02-03 21:28 17605
    if (node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17606
    {
1193b4be Poul Sander 2025-02-03 21:28 17607
        TabBarRemoveTab(node->TabBar, window->TabId);
1193b4be Poul Sander 2025-02-03 21:28 17608
        const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
1193b4be Poul Sander 2025-02-03 21:28 17609
        if (node->Windows.Size < tab_count_threshold_for_tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 17610
            DockNodeRemoveTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 17611
    }
1193b4be Poul Sander 2025-02-03 21:28 17612
1193b4be Poul Sander 2025-02-03 21:28 17613
    if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
1193b4be Poul Sander 2025-02-03 21:28 17614
    {
1193b4be Poul Sander 2025-02-03 21:28 17615
        // Automatic dock node delete themselves if they are not holding at least one tab
1193b4be Poul Sander 2025-02-03 21:28 17616
        DockContextRemoveNode(&g, node, true);
1193b4be Poul Sander 2025-02-03 21:28 17617
        return;
1193b4be Poul Sander 2025-02-03 21:28 17618
    }
1193b4be Poul Sander 2025-02-03 21:28 17619
1193b4be Poul Sander 2025-02-03 21:28 17620
    if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 17621
    {
1193b4be Poul Sander 2025-02-03 21:28 17622
        ImGuiWindow* remaining_window = node->Windows[0];
1193b4be Poul Sander 2025-02-03 21:28 17623
        // Note: we used to transport viewport ownership here.
1193b4be Poul Sander 2025-02-03 21:28 17624
        remaining_window->Collapsed = node->HostWindow->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 17625
    }
1193b4be Poul Sander 2025-02-03 21:28 17626
1193b4be Poul Sander 2025-02-03 21:28 17627
    // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
1193b4be Poul Sander 2025-02-03 21:28 17628
    DockNodeUpdateVisibleFlag(node);
1193b4be Poul Sander 2025-02-03 21:28 17629
}
1193b4be Poul Sander 2025-02-03 21:28 17630
1193b4be Poul Sander 2025-02-03 21:28 17631
static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
1193b4be Poul Sander 2025-02-03 21:28 17632
{
1193b4be Poul Sander 2025-02-03 21:28 17633
    IM_ASSERT(dst_node->Windows.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 17634
    dst_node->ChildNodes[0] = src_node->ChildNodes[0];
1193b4be Poul Sander 2025-02-03 21:28 17635
    dst_node->ChildNodes[1] = src_node->ChildNodes[1];
1193b4be Poul Sander 2025-02-03 21:28 17636
    if (dst_node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 17637
        dst_node->ChildNodes[0]->ParentNode = dst_node;
1193b4be Poul Sander 2025-02-03 21:28 17638
    if (dst_node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 17639
        dst_node->ChildNodes[1]->ParentNode = dst_node;
1193b4be Poul Sander 2025-02-03 21:28 17640
    dst_node->SplitAxis = src_node->SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 17641
    dst_node->SizeRef = src_node->SizeRef;
1193b4be Poul Sander 2025-02-03 21:28 17642
    src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17643
}
1193b4be Poul Sander 2025-02-03 21:28 17644
1193b4be Poul Sander 2025-02-03 21:28 17645
static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
1193b4be Poul Sander 2025-02-03 21:28 17646
{
1193b4be Poul Sander 2025-02-03 21:28 17647
    // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
1193b4be Poul Sander 2025-02-03 21:28 17648
    IM_ASSERT(src_node && dst_node && dst_node != src_node);
1193b4be Poul Sander 2025-02-03 21:28 17649
    ImGuiTabBar* src_tab_bar = src_node->TabBar;
1193b4be Poul Sander 2025-02-03 21:28 17650
    if (src_tab_bar != NULL)
1193b4be Poul Sander 2025-02-03 21:28 17651
        IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);
1193b4be Poul Sander 2025-02-03 21:28 17652
1193b4be Poul Sander 2025-02-03 21:28 17653
    // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
1193b4be Poul Sander 2025-02-03 21:28 17654
    bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
1193b4be Poul Sander 2025-02-03 21:28 17655
    if (move_tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 17656
    {
1193b4be Poul Sander 2025-02-03 21:28 17657
        dst_node->TabBar = src_node->TabBar;
1193b4be Poul Sander 2025-02-03 21:28 17658
        src_node->TabBar = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17659
    }
1193b4be Poul Sander 2025-02-03 21:28 17660
1193b4be Poul Sander 2025-02-03 21:28 17661
    // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar().
1193b4be Poul Sander 2025-02-03 21:28 17662
    for (ImGuiWindow* window : src_node->Windows)
1193b4be Poul Sander 2025-02-03 21:28 17663
    {
1193b4be Poul Sander 2025-02-03 21:28 17664
        window->DockNode = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17665
        window->DockIsActive = false;
1193b4be Poul Sander 2025-02-03 21:28 17666
        DockNodeAddWindow(dst_node, window, !move_tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 17667
    }
1193b4be Poul Sander 2025-02-03 21:28 17668
    src_node->Windows.clear();
1193b4be Poul Sander 2025-02-03 21:28 17669
1193b4be Poul Sander 2025-02-03 21:28 17670
    if (!move_tab_bar && src_node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17671
    {
1193b4be Poul Sander 2025-02-03 21:28 17672
        if (dst_node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17673
            dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 17674
        DockNodeRemoveTabBar(src_node);
1193b4be Poul Sander 2025-02-03 21:28 17675
    }
1193b4be Poul Sander 2025-02-03 21:28 17676
}
1193b4be Poul Sander 2025-02-03 21:28 17677
1193b4be Poul Sander 2025-02-03 21:28 17678
static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17679
{
1193b4be Poul Sander 2025-02-03 21:28 17680
    for (ImGuiWindow* window : node->Windows)
1193b4be Poul Sander 2025-02-03 21:28 17681
    {
1193b4be Poul Sander 2025-02-03 21:28 17682
        SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame
1193b4be Poul Sander 2025-02-03 21:28 17683
        SetWindowSize(window, node->Size, ImGuiCond_Always);
1193b4be Poul Sander 2025-02-03 21:28 17684
    }
1193b4be Poul Sander 2025-02-03 21:28 17685
}
1193b4be Poul Sander 2025-02-03 21:28 17686
1193b4be Poul Sander 2025-02-03 21:28 17687
static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17688
{
1193b4be Poul Sander 2025-02-03 21:28 17689
    if (node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 17690
    {
1193b4be Poul Sander 2025-02-03 21:28 17691
        if (node->HostWindow->DockNodeAsHost == node)
1193b4be Poul Sander 2025-02-03 21:28 17692
            node->HostWindow->DockNodeAsHost = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17693
        node->HostWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17694
    }
1193b4be Poul Sander 2025-02-03 21:28 17695
1193b4be Poul Sander 2025-02-03 21:28 17696
    if (node->Windows.Size == 1)
1193b4be Poul Sander 2025-02-03 21:28 17697
    {
1193b4be Poul Sander 2025-02-03 21:28 17698
        node->VisibleWindow = node->Windows[0];
1193b4be Poul Sander 2025-02-03 21:28 17699
        node->Windows[0]->DockIsActive = false;
1193b4be Poul Sander 2025-02-03 21:28 17700
    }
1193b4be Poul Sander 2025-02-03 21:28 17701
1193b4be Poul Sander 2025-02-03 21:28 17702
    if (node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 17703
        DockNodeRemoveTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 17704
}
1193b4be Poul Sander 2025-02-03 21:28 17705
1193b4be Poul Sander 2025-02-03 21:28 17706
// Search function called once by root node in DockNodeUpdate()
1193b4be Poul Sander 2025-02-03 21:28 17707
struct ImGuiDockNodeTreeInfo
1193b4be Poul Sander 2025-02-03 21:28 17708
{
1193b4be Poul Sander 2025-02-03 21:28 17709
    ImGuiDockNode*      CentralNode;
1193b4be Poul Sander 2025-02-03 21:28 17710
    ImGuiDockNode*      FirstNodeWithWindows;
1193b4be Poul Sander 2025-02-03 21:28 17711
    int                 CountNodesWithWindows;
1193b4be Poul Sander 2025-02-03 21:28 17712
    //ImGuiWindowClass  WindowClassForMerges;
1193b4be Poul Sander 2025-02-03 21:28 17713
1193b4be Poul Sander 2025-02-03 21:28 17714
    ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); }
1193b4be Poul Sander 2025-02-03 21:28 17715
};
1193b4be Poul Sander 2025-02-03 21:28 17716
1193b4be Poul Sander 2025-02-03 21:28 17717
static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)
1193b4be Poul Sander 2025-02-03 21:28 17718
{
1193b4be Poul Sander 2025-02-03 21:28 17719
    if (node->Windows.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 17720
    {
1193b4be Poul Sander 2025-02-03 21:28 17721
        if (info->FirstNodeWithWindows == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17722
            info->FirstNodeWithWindows = node;
1193b4be Poul Sander 2025-02-03 21:28 17723
        info->CountNodesWithWindows++;
1193b4be Poul Sander 2025-02-03 21:28 17724
    }
1193b4be Poul Sander 2025-02-03 21:28 17725
    if (node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 17726
    {
1193b4be Poul Sander 2025-02-03 21:28 17727
        IM_ASSERT(info->CentralNode == NULL); // Should be only one
1193b4be Poul Sander 2025-02-03 21:28 17728
        IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
1193b4be Poul Sander 2025-02-03 21:28 17729
        info->CentralNode = node;
1193b4be Poul Sander 2025-02-03 21:28 17730
    }
1193b4be Poul Sander 2025-02-03 21:28 17731
    if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)
1193b4be Poul Sander 2025-02-03 21:28 17732
        return;
1193b4be Poul Sander 2025-02-03 21:28 17733
    if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 17734
        DockNodeFindInfo(node->ChildNodes[0], info);
1193b4be Poul Sander 2025-02-03 21:28 17735
    if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 17736
        DockNodeFindInfo(node->ChildNodes[1], info);
1193b4be Poul Sander 2025-02-03 21:28 17737
}
1193b4be Poul Sander 2025-02-03 21:28 17738
1193b4be Poul Sander 2025-02-03 21:28 17739
static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 17740
{
1193b4be Poul Sander 2025-02-03 21:28 17741
    IM_ASSERT(id != 0);
1193b4be Poul Sander 2025-02-03 21:28 17742
    for (ImGuiWindow* window : node->Windows)
1193b4be Poul Sander 2025-02-03 21:28 17743
        if (window->ID == id)
1193b4be Poul Sander 2025-02-03 21:28 17744
            return window;
1193b4be Poul Sander 2025-02-03 21:28 17745
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 17746
}
1193b4be Poul Sander 2025-02-03 21:28 17747
1193b4be Poul Sander 2025-02-03 21:28 17748
// - Remove inactive windows/nodes.
1193b4be Poul Sander 2025-02-03 21:28 17749
// - Update visibility flag.
1193b4be Poul Sander 2025-02-03 21:28 17750
static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17751
{
1193b4be Poul Sander 2025-02-03 21:28 17752
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 17753
    IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
1193b4be Poul Sander 2025-02-03 21:28 17754
1193b4be Poul Sander 2025-02-03 21:28 17755
    // Inherit most flags
1193b4be Poul Sander 2025-02-03 21:28 17756
    if (node->ParentNode)
1193b4be Poul Sander 2025-02-03 21:28 17757
        node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
1193b4be Poul Sander 2025-02-03 21:28 17758
1193b4be Poul Sander 2025-02-03 21:28 17759
    // Recurse into children
1193b4be Poul Sander 2025-02-03 21:28 17760
    // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
1193b4be Poul Sander 2025-02-03 21:28 17761
    // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
1193b4be Poul Sander 2025-02-03 21:28 17762
    // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless)
1193b4be Poul Sander 2025-02-03 21:28 17763
    node->HasCentralNodeChild = false;
1193b4be Poul Sander 2025-02-03 21:28 17764
    if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 17765
        DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]);
1193b4be Poul Sander 2025-02-03 21:28 17766
    if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 17767
        DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);
1193b4be Poul Sander 2025-02-03 21:28 17768
1193b4be Poul Sander 2025-02-03 21:28 17769
    // Remove inactive windows, collapse nodes
1193b4be Poul Sander 2025-02-03 21:28 17770
    // Merge node flags overrides stored in windows
1193b4be Poul Sander 2025-02-03 21:28 17771
    node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 17772
    for (int window_n = 0; window_n < node->Windows.Size; window_n++)
1193b4be Poul Sander 2025-02-03 21:28 17773
    {
1193b4be Poul Sander 2025-02-03 21:28 17774
        ImGuiWindow* window = node->Windows[window_n];
1193b4be Poul Sander 2025-02-03 21:28 17775
        IM_ASSERT(window->DockNode == node);
1193b4be Poul Sander 2025-02-03 21:28 17776
1193b4be Poul Sander 2025-02-03 21:28 17777
        bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 17778
        bool remove = false;
1193b4be Poul Sander 2025-02-03 21:28 17779
        remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 17780
        remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument);  // Submit all _expected_ closure from last frame
1193b4be Poul Sander 2025-02-03 21:28 17781
        remove |= (window->DockTabWantClose);
1193b4be Poul Sander 2025-02-03 21:28 17782
        if (remove)
1193b4be Poul Sander 2025-02-03 21:28 17783
        {
1193b4be Poul Sander 2025-02-03 21:28 17784
            window->DockTabWantClose = false;
1193b4be Poul Sander 2025-02-03 21:28 17785
            if (node->Windows.Size == 1 && !node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 17786
            {
1193b4be Poul Sander 2025-02-03 21:28 17787
                DockNodeHideHostWindow(node);
1193b4be Poul Sander 2025-02-03 21:28 17788
                node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
1193b4be Poul Sander 2025-02-03 21:28 17789
                DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
1193b4be Poul Sander 2025-02-03 21:28 17790
                return;
1193b4be Poul Sander 2025-02-03 21:28 17791
            }
1193b4be Poul Sander 2025-02-03 21:28 17792
            DockNodeRemoveWindow(node, window, node->ID);
1193b4be Poul Sander 2025-02-03 21:28 17793
            window_n--;
1193b4be Poul Sander 2025-02-03 21:28 17794
            continue;
1193b4be Poul Sander 2025-02-03 21:28 17795
        }
1193b4be Poul Sander 2025-02-03 21:28 17796
1193b4be Poul Sander 2025-02-03 21:28 17797
        // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
1193b4be Poul Sander 2025-02-03 21:28 17798
        //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
1193b4be Poul Sander 2025-02-03 21:28 17799
        node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
1193b4be Poul Sander 2025-02-03 21:28 17800
    }
1193b4be Poul Sander 2025-02-03 21:28 17801
    node->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 17802
1193b4be Poul Sander 2025-02-03 21:28 17803
    // Auto-hide tab bar option
1193b4be Poul Sander 2025-02-03 21:28 17804
    ImGuiDockNodeFlags node_flags = node->MergedFlags;
1193b4be Poul Sander 2025-02-03 21:28 17805
    if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
1193b4be Poul Sander 2025-02-03 21:28 17806
        node->WantHiddenTabBarToggle = true;
1193b4be Poul Sander 2025-02-03 21:28 17807
    node->WantHiddenTabBarUpdate = false;
1193b4be Poul Sander 2025-02-03 21:28 17808
1193b4be Poul Sander 2025-02-03 21:28 17809
    // Cancel toggling if we know our tab bar is enforced to be hidden at all times
1193b4be Poul Sander 2025-02-03 21:28 17810
    if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
1193b4be Poul Sander 2025-02-03 21:28 17811
        node->WantHiddenTabBarToggle = false;
1193b4be Poul Sander 2025-02-03 21:28 17812
1193b4be Poul Sander 2025-02-03 21:28 17813
    // Apply toggles at a single point of the frame (here!)
1193b4be Poul Sander 2025-02-03 21:28 17814
    if (node->Windows.Size > 1)
1193b4be Poul Sander 2025-02-03 21:28 17815
        node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
1193b4be Poul Sander 2025-02-03 21:28 17816
    else if (node->WantHiddenTabBarToggle)
1193b4be Poul Sander 2025-02-03 21:28 17817
        node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);
1193b4be Poul Sander 2025-02-03 21:28 17818
    node->WantHiddenTabBarToggle = false;
1193b4be Poul Sander 2025-02-03 21:28 17819
1193b4be Poul Sander 2025-02-03 21:28 17820
    DockNodeUpdateVisibleFlag(node);
1193b4be Poul Sander 2025-02-03 21:28 17821
}
1193b4be Poul Sander 2025-02-03 21:28 17822
1193b4be Poul Sander 2025-02-03 21:28 17823
// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.
1193b4be Poul Sander 2025-02-03 21:28 17824
static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17825
{
1193b4be Poul Sander 2025-02-03 21:28 17826
    node->HasCentralNodeChild = false;
1193b4be Poul Sander 2025-02-03 21:28 17827
    if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 17828
        DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]);
1193b4be Poul Sander 2025-02-03 21:28 17829
    if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 17830
        DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]);
1193b4be Poul Sander 2025-02-03 21:28 17831
    if (node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 17832
    {
1193b4be Poul Sander 2025-02-03 21:28 17833
        ImGuiDockNode* mark_node = node->CentralNode;
1193b4be Poul Sander 2025-02-03 21:28 17834
        while (mark_node)
1193b4be Poul Sander 2025-02-03 21:28 17835
        {
1193b4be Poul Sander 2025-02-03 21:28 17836
            mark_node->HasCentralNodeChild = true;
1193b4be Poul Sander 2025-02-03 21:28 17837
            mark_node = mark_node->ParentNode;
1193b4be Poul Sander 2025-02-03 21:28 17838
        }
1193b4be Poul Sander 2025-02-03 21:28 17839
    }
1193b4be Poul Sander 2025-02-03 21:28 17840
}
1193b4be Poul Sander 2025-02-03 21:28 17841
1193b4be Poul Sander 2025-02-03 21:28 17842
static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17843
{
1193b4be Poul Sander 2025-02-03 21:28 17844
    // Update visibility flag
1193b4be Poul Sander 2025-02-03 21:28 17845
    bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
1193b4be Poul Sander 2025-02-03 21:28 17846
    is_visible |= (node->Windows.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 17847
    is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
1193b4be Poul Sander 2025-02-03 21:28 17848
    is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
1193b4be Poul Sander 2025-02-03 21:28 17849
    node->IsVisible = is_visible;
1193b4be Poul Sander 2025-02-03 21:28 17850
}
1193b4be Poul Sander 2025-02-03 21:28 17851
1193b4be Poul Sander 2025-02-03 21:28 17852
static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 17853
{
1193b4be Poul Sander 2025-02-03 21:28 17854
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 17855
    IM_ASSERT(node->WantMouseMove == true);
1193b4be Poul Sander 2025-02-03 21:28 17856
    StartMouseMovingWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 17857
    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
1193b4be Poul Sander 2025-02-03 21:28 17858
    g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
1193b4be Poul Sander 2025-02-03 21:28 17859
    node->WantMouseMove = false;
1193b4be Poul Sander 2025-02-03 21:28 17860
}
1193b4be Poul Sander 2025-02-03 21:28 17861
1193b4be Poul Sander 2025-02-03 21:28 17862
// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
1193b4be Poul Sander 2025-02-03 21:28 17863
static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17864
{
1193b4be Poul Sander 2025-02-03 21:28 17865
    DockNodeUpdateFlagsAndCollapse(node);
1193b4be Poul Sander 2025-02-03 21:28 17866
1193b4be Poul Sander 2025-02-03 21:28 17867
    // - Setup central node pointers
1193b4be Poul Sander 2025-02-03 21:28 17868
    // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!)
1193b4be Poul Sander 2025-02-03 21:28 17869
    // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing
1193b4be Poul Sander 2025-02-03 21:28 17870
    ImGuiDockNodeTreeInfo info;
1193b4be Poul Sander 2025-02-03 21:28 17871
    DockNodeFindInfo(node, &info);
1193b4be Poul Sander 2025-02-03 21:28 17872
    node->CentralNode = info.CentralNode;
1193b4be Poul Sander 2025-02-03 21:28 17873
    node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL;
1193b4be Poul Sander 2025-02-03 21:28 17874
    node->CountNodeWithWindows = info.CountNodesWithWindows;
1193b4be Poul Sander 2025-02-03 21:28 17875
    if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL)
1193b4be Poul Sander 2025-02-03 21:28 17876
        node->LastFocusedNodeId = info.FirstNodeWithWindows->ID;
1193b4be Poul Sander 2025-02-03 21:28 17877
1193b4be Poul Sander 2025-02-03 21:28 17878
    // Copy the window class from of our first window so it can be used for proper dock filtering.
1193b4be Poul Sander 2025-02-03 21:28 17879
    // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
1193b4be Poul Sander 2025-02-03 21:28 17880
    // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
1193b4be Poul Sander 2025-02-03 21:28 17881
    if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows)
1193b4be Poul Sander 2025-02-03 21:28 17882
    {
1193b4be Poul Sander 2025-02-03 21:28 17883
        node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
1193b4be Poul Sander 2025-02-03 21:28 17884
        for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 17885
            if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
1193b4be Poul Sander 2025-02-03 21:28 17886
            {
1193b4be Poul Sander 2025-02-03 21:28 17887
                node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
1193b4be Poul Sander 2025-02-03 21:28 17888
                break;
1193b4be Poul Sander 2025-02-03 21:28 17889
            }
1193b4be Poul Sander 2025-02-03 21:28 17890
    }
1193b4be Poul Sander 2025-02-03 21:28 17891
1193b4be Poul Sander 2025-02-03 21:28 17892
    ImGuiDockNode* mark_node = node->CentralNode;
1193b4be Poul Sander 2025-02-03 21:28 17893
    while (mark_node)
1193b4be Poul Sander 2025-02-03 21:28 17894
    {
1193b4be Poul Sander 2025-02-03 21:28 17895
        mark_node->HasCentralNodeChild = true;
1193b4be Poul Sander 2025-02-03 21:28 17896
        mark_node = mark_node->ParentNode;
1193b4be Poul Sander 2025-02-03 21:28 17897
    }
1193b4be Poul Sander 2025-02-03 21:28 17898
}
1193b4be Poul Sander 2025-02-03 21:28 17899
1193b4be Poul Sander 2025-02-03 21:28 17900
static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)
1193b4be Poul Sander 2025-02-03 21:28 17901
{
1193b4be Poul Sander 2025-02-03 21:28 17902
    // Remove ourselves from any previous different host window
1193b4be Poul Sander 2025-02-03 21:28 17903
    // This can happen if a user mistakenly does (see #4295 for details):
1193b4be Poul Sander 2025-02-03 21:28 17904
    //  - N+0: DockBuilderAddNode(id, 0)    // missing ImGuiDockNodeFlags_DockSpace
1193b4be Poul Sander 2025-02-03 21:28 17905
    //  - N+1: NewFrame()                   // will create floating host window for that node
1193b4be Poul Sander 2025-02-03 21:28 17906
    //  - N+1: DockSpace(id)                // requalify node as dockspace, moving host window
1193b4be Poul Sander 2025-02-03 21:28 17907
    if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node)
1193b4be Poul Sander 2025-02-03 21:28 17908
        node->HostWindow->DockNodeAsHost = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17909
1193b4be Poul Sander 2025-02-03 21:28 17910
    host_window->DockNodeAsHost = node;
1193b4be Poul Sander 2025-02-03 21:28 17911
    node->HostWindow = host_window;
1193b4be Poul Sander 2025-02-03 21:28 17912
}
1193b4be Poul Sander 2025-02-03 21:28 17913
1193b4be Poul Sander 2025-02-03 21:28 17914
static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 17915
{
1193b4be Poul Sander 2025-02-03 21:28 17916
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 17917
    IM_ASSERT(node->LastFrameActive != g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 17918
    node->LastFrameAlive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 17919
    node->IsBgDrawnThisFrame = false;
1193b4be Poul Sander 2025-02-03 21:28 17920
1193b4be Poul Sander 2025-02-03 21:28 17921
    node->CentralNode = node->OnlyNodeWithWindows = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17922
    if (node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 17923
        DockNodeUpdateForRootNode(node);
1193b4be Poul Sander 2025-02-03 21:28 17924
1193b4be Poul Sander 2025-02-03 21:28 17925
    // Remove tab bar if not needed
1193b4be Poul Sander 2025-02-03 21:28 17926
    if (node->TabBar && node->IsNoTabBar())
1193b4be Poul Sander 2025-02-03 21:28 17927
        DockNodeRemoveTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 17928
1193b4be Poul Sander 2025-02-03 21:28 17929
    // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId)
1193b4be Poul Sander 2025-02-03 21:28 17930
    bool want_to_hide_host_window = false;
1193b4be Poul Sander 2025-02-03 21:28 17931
    if (node->IsFloatingNode())
1193b4be Poul Sander 2025-02-03 21:28 17932
    {
1193b4be Poul Sander 2025-02-03 21:28 17933
        if (node->Windows.Size <= 1 && node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 17934
            if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
1193b4be Poul Sander 2025-02-03 21:28 17935
                want_to_hide_host_window = true;
1193b4be Poul Sander 2025-02-03 21:28 17936
        if (node->CountNodeWithWindows == 0)
1193b4be Poul Sander 2025-02-03 21:28 17937
            want_to_hide_host_window = true;
1193b4be Poul Sander 2025-02-03 21:28 17938
    }
1193b4be Poul Sander 2025-02-03 21:28 17939
    if (want_to_hide_host_window)
1193b4be Poul Sander 2025-02-03 21:28 17940
    {
1193b4be Poul Sander 2025-02-03 21:28 17941
        if (node->Windows.Size == 1)
1193b4be Poul Sander 2025-02-03 21:28 17942
        {
1193b4be Poul Sander 2025-02-03 21:28 17943
            // Floating window pos/size is authoritative
1193b4be Poul Sander 2025-02-03 21:28 17944
            ImGuiWindow* single_window = node->Windows[0];
1193b4be Poul Sander 2025-02-03 21:28 17945
            node->Pos = single_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 17946
            node->Size = single_window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 17947
            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
1193b4be Poul Sander 2025-02-03 21:28 17948
1193b4be Poul Sander 2025-02-03 21:28 17949
            // Transfer focus immediately so when we revert to a regular window it is immediately selected
1193b4be Poul Sander 2025-02-03 21:28 17950
            if (node->HostWindow && g.NavWindow == node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 17951
                FocusWindow(single_window);
1193b4be Poul Sander 2025-02-03 21:28 17952
            if (node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 17953
            {
1193b4be Poul Sander 2025-02-03 21:28 17954
                IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name);
1193b4be Poul Sander 2025-02-03 21:28 17955
                single_window->Viewport = node->HostWindow->Viewport;
1193b4be Poul Sander 2025-02-03 21:28 17956
                single_window->ViewportId = node->HostWindow->ViewportId;
1193b4be Poul Sander 2025-02-03 21:28 17957
                if (node->HostWindow->ViewportOwned)
1193b4be Poul Sander 2025-02-03 21:28 17958
                {
1193b4be Poul Sander 2025-02-03 21:28 17959
                    single_window->Viewport->ID = single_window->ID;
1193b4be Poul Sander 2025-02-03 21:28 17960
                    single_window->Viewport->Window = single_window;
1193b4be Poul Sander 2025-02-03 21:28 17961
                    single_window->ViewportOwned = true;
1193b4be Poul Sander 2025-02-03 21:28 17962
                }
1193b4be Poul Sander 2025-02-03 21:28 17963
            }
1193b4be Poul Sander 2025-02-03 21:28 17964
            node->RefViewportId = single_window->ViewportId;
1193b4be Poul Sander 2025-02-03 21:28 17965
        }
1193b4be Poul Sander 2025-02-03 21:28 17966
1193b4be Poul Sander 2025-02-03 21:28 17967
        DockNodeHideHostWindow(node);
1193b4be Poul Sander 2025-02-03 21:28 17968
        node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
1193b4be Poul Sander 2025-02-03 21:28 17969
        node->WantCloseAll = false;
1193b4be Poul Sander 2025-02-03 21:28 17970
        node->WantCloseTabId = 0;
1193b4be Poul Sander 2025-02-03 21:28 17971
        node->HasCloseButton = node->HasWindowMenuButton = false;
1193b4be Poul Sander 2025-02-03 21:28 17972
        node->LastFrameActive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 17973
1193b4be Poul Sander 2025-02-03 21:28 17974
        if (node->WantMouseMove && node->Windows.Size == 1)
1193b4be Poul Sander 2025-02-03 21:28 17975
            DockNodeStartMouseMovingWindow(node, node->Windows[0]);
1193b4be Poul Sander 2025-02-03 21:28 17976
        return;
1193b4be Poul Sander 2025-02-03 21:28 17977
    }
1193b4be Poul Sander 2025-02-03 21:28 17978
1193b4be Poul Sander 2025-02-03 21:28 17979
    // In some circumstance we will defer creating the host window (so everything will be kept hidden),
1193b4be Poul Sander 2025-02-03 21:28 17980
    // while the expected visible window is resizing itself.
1193b4be Poul Sander 2025-02-03 21:28 17981
    // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
1193b4be Poul Sander 2025-02-03 21:28 17982
    // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
1193b4be Poul Sander 2025-02-03 21:28 17983
    //   N+0: Begin(): window created (with no known size), node is created
1193b4be Poul Sander 2025-02-03 21:28 17984
    //   N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
1193b4be Poul Sander 2025-02-03 21:28 17985
    //   N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
1193b4be Poul Sander 2025-02-03 21:28 17986
    // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
1193b4be Poul Sander 2025-02-03 21:28 17987
    // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
1193b4be Poul Sander 2025-02-03 21:28 17988
    // In reality it isn't very important as user quickly ends up with size data in .ini file.
1193b4be Poul Sander 2025-02-03 21:28 17989
    if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 17990
    {
1193b4be Poul Sander 2025-02-03 21:28 17991
        IM_ASSERT(node->Windows.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 17992
        ImGuiWindow* ref_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 17993
        if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
1193b4be Poul Sander 2025-02-03 21:28 17994
            ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
1193b4be Poul Sander 2025-02-03 21:28 17995
        if (ref_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 17996
            ref_window = node->Windows[0];
1193b4be Poul Sander 2025-02-03 21:28 17997
        if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
1193b4be Poul Sander 2025-02-03 21:28 17998
        {
1193b4be Poul Sander 2025-02-03 21:28 17999
            node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
1193b4be Poul Sander 2025-02-03 21:28 18000
            return;
1193b4be Poul Sander 2025-02-03 21:28 18001
        }
1193b4be Poul Sander 2025-02-03 21:28 18002
    }
1193b4be Poul Sander 2025-02-03 21:28 18003
1193b4be Poul Sander 2025-02-03 21:28 18004
    const ImGuiDockNodeFlags node_flags = node->MergedFlags;
1193b4be Poul Sander 2025-02-03 21:28 18005
1193b4be Poul Sander 2025-02-03 21:28 18006
    // Decide if the node will have a close button and a window menu button
1193b4be Poul Sander 2025-02-03 21:28 18007
    node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
1193b4be Poul Sander 2025-02-03 21:28 18008
    node->HasCloseButton = false;
1193b4be Poul Sander 2025-02-03 21:28 18009
    for (ImGuiWindow* window : node->Windows)
1193b4be Poul Sander 2025-02-03 21:28 18010
    {
1193b4be Poul Sander 2025-02-03 21:28 18011
        // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
1193b4be Poul Sander 2025-02-03 21:28 18012
        node->HasCloseButton |= window->HasCloseButton;
1193b4be Poul Sander 2025-02-03 21:28 18013
        window->DockIsActive = (node->Windows.Size > 1);
1193b4be Poul Sander 2025-02-03 21:28 18014
    }
1193b4be Poul Sander 2025-02-03 21:28 18015
    if (node_flags & ImGuiDockNodeFlags_NoCloseButton)
1193b4be Poul Sander 2025-02-03 21:28 18016
        node->HasCloseButton = false;
1193b4be Poul Sander 2025-02-03 21:28 18017
1193b4be Poul Sander 2025-02-03 21:28 18018
    // Bind or create host window
1193b4be Poul Sander 2025-02-03 21:28 18019
    ImGuiWindow* host_window = NULL;
1193b4be Poul Sander 2025-02-03 21:28 18020
    bool beginned_into_host_window = false;
1193b4be Poul Sander 2025-02-03 21:28 18021
    if (node->IsDockSpace())
1193b4be Poul Sander 2025-02-03 21:28 18022
    {
1193b4be Poul Sander 2025-02-03 21:28 18023
        // [Explicit root dockspace node]
1193b4be Poul Sander 2025-02-03 21:28 18024
        IM_ASSERT(node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 18025
        host_window = node->HostWindow;
1193b4be Poul Sander 2025-02-03 21:28 18026
    }
1193b4be Poul Sander 2025-02-03 21:28 18027
    else
1193b4be Poul Sander 2025-02-03 21:28 18028
    {
1193b4be Poul Sander 2025-02-03 21:28 18029
        // [Automatic root or child nodes]
1193b4be Poul Sander 2025-02-03 21:28 18030
        if (node->IsRootNode() && node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 18031
        {
1193b4be Poul Sander 2025-02-03 21:28 18032
            ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
1193b4be Poul Sander 2025-02-03 21:28 18033
1193b4be Poul Sander 2025-02-03 21:28 18034
            // Sync Pos
1193b4be Poul Sander 2025-02-03 21:28 18035
            if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
1193b4be Poul Sander 2025-02-03 21:28 18036
                SetNextWindowPos(ref_window->Pos);
1193b4be Poul Sander 2025-02-03 21:28 18037
            else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
1193b4be Poul Sander 2025-02-03 21:28 18038
                SetNextWindowPos(node->Pos);
1193b4be Poul Sander 2025-02-03 21:28 18039
1193b4be Poul Sander 2025-02-03 21:28 18040
            // Sync Size
1193b4be Poul Sander 2025-02-03 21:28 18041
            if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
1193b4be Poul Sander 2025-02-03 21:28 18042
                SetNextWindowSize(ref_window->SizeFull);
1193b4be Poul Sander 2025-02-03 21:28 18043
            else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
1193b4be Poul Sander 2025-02-03 21:28 18044
                SetNextWindowSize(node->Size);
1193b4be Poul Sander 2025-02-03 21:28 18045
1193b4be Poul Sander 2025-02-03 21:28 18046
            // Sync Collapsed
1193b4be Poul Sander 2025-02-03 21:28 18047
            if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
1193b4be Poul Sander 2025-02-03 21:28 18048
                SetNextWindowCollapsed(ref_window->Collapsed);
1193b4be Poul Sander 2025-02-03 21:28 18049
1193b4be Poul Sander 2025-02-03 21:28 18050
            // Sync Viewport
1193b4be Poul Sander 2025-02-03 21:28 18051
            if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
1193b4be Poul Sander 2025-02-03 21:28 18052
                SetNextWindowViewport(ref_window->ViewportId);
1193b4be Poul Sander 2025-02-03 21:28 18053
            else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0)
1193b4be Poul Sander 2025-02-03 21:28 18054
                SetNextWindowViewport(node->RefViewportId);
1193b4be Poul Sander 2025-02-03 21:28 18055
1193b4be Poul Sander 2025-02-03 21:28 18056
            SetNextWindowClass(&node->WindowClass);
1193b4be Poul Sander 2025-02-03 21:28 18057
1193b4be Poul Sander 2025-02-03 21:28 18058
            // Begin into the host window
1193b4be Poul Sander 2025-02-03 21:28 18059
            char window_label[20];
1193b4be Poul Sander 2025-02-03 21:28 18060
            DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
1193b4be Poul Sander 2025-02-03 21:28 18061
            ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
1193b4be Poul Sander 2025-02-03 21:28 18062
            window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
1193b4be Poul Sander 2025-02-03 21:28 18063
            window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
1193b4be Poul Sander 2025-02-03 21:28 18064
            window_flags |= ImGuiWindowFlags_NoTitleBar;
1193b4be Poul Sander 2025-02-03 21:28 18065
1193b4be Poul Sander 2025-02-03 21:28 18066
            SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders
1193b4be Poul Sander 2025-02-03 21:28 18067
            PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
1193b4be Poul Sander 2025-02-03 21:28 18068
            Begin(window_label, NULL, window_flags);
1193b4be Poul Sander 2025-02-03 21:28 18069
            PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 18070
            beginned_into_host_window = true;
1193b4be Poul Sander 2025-02-03 21:28 18071
1193b4be Poul Sander 2025-02-03 21:28 18072
            host_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 18073
            DockNodeSetupHostWindow(node, host_window);
1193b4be Poul Sander 2025-02-03 21:28 18074
            host_window->DC.CursorPos = host_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 18075
            node->Pos = host_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 18076
            node->Size = host_window->Size;
1193b4be Poul Sander 2025-02-03 21:28 18077
1193b4be Poul Sander 2025-02-03 21:28 18078
            // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
1193b4be Poul Sander 2025-02-03 21:28 18079
            // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
1193b4be Poul Sander 2025-02-03 21:28 18080
            // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again.
1193b4be Poul Sander 2025-02-03 21:28 18081
            // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window
1193b4be Poul Sander 2025-02-03 21:28 18082
            // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back
1193b4be Poul Sander 2025-02-03 21:28 18083
            // after the dock host window, losing their top-most status.
1193b4be Poul Sander 2025-02-03 21:28 18084
            if (node->HostWindow->Appearing)
1193b4be Poul Sander 2025-02-03 21:28 18085
                BringWindowToDisplayFront(node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 18086
1193b4be Poul Sander 2025-02-03 21:28 18087
            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 18088
        }
1193b4be Poul Sander 2025-02-03 21:28 18089
        else if (node->ParentNode)
1193b4be Poul Sander 2025-02-03 21:28 18090
        {
1193b4be Poul Sander 2025-02-03 21:28 18091
            node->HostWindow = host_window = node->ParentNode->HostWindow;
1193b4be Poul Sander 2025-02-03 21:28 18092
            node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 18093
        }
1193b4be Poul Sander 2025-02-03 21:28 18094
        if (node->WantMouseMove && node->HostWindow)
1193b4be Poul Sander 2025-02-03 21:28 18095
            DockNodeStartMouseMovingWindow(node, node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 18096
    }
1193b4be Poul Sander 2025-02-03 21:28 18097
    node->RefViewportId = 0; // Clear when we have a host window
1193b4be Poul Sander 2025-02-03 21:28 18098
1193b4be Poul Sander 2025-02-03 21:28 18099
    // Update focused node (the one whose title bar is highlight) within a node tree
1193b4be Poul Sander 2025-02-03 21:28 18100
    if (node->IsSplitNode())
1193b4be Poul Sander 2025-02-03 21:28 18101
        IM_ASSERT(node->TabBar == NULL);
1193b4be Poul Sander 2025-02-03 21:28 18102
    if (node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 18103
        if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL)
1193b4be Poul Sander 2025-02-03 21:28 18104
            while (p_window != NULL && p_window->DockNode != NULL)
1193b4be Poul Sander 2025-02-03 21:28 18105
            {
1193b4be Poul Sander 2025-02-03 21:28 18106
                ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode);
1193b4be Poul Sander 2025-02-03 21:28 18107
                if (p_node == node)
1193b4be Poul Sander 2025-02-03 21:28 18108
                {
1193b4be Poul Sander 2025-02-03 21:28 18109
                    node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID!
1193b4be Poul Sander 2025-02-03 21:28 18110
                    break;
1193b4be Poul Sander 2025-02-03 21:28 18111
                }
1193b4be Poul Sander 2025-02-03 21:28 18112
                p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL;
1193b4be Poul Sander 2025-02-03 21:28 18113
            }
1193b4be Poul Sander 2025-02-03 21:28 18114
1193b4be Poul Sander 2025-02-03 21:28 18115
    // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
1193b4be Poul Sander 2025-02-03 21:28 18116
    ImGuiDockNode* central_node = node->CentralNode;
1193b4be Poul Sander 2025-02-03 21:28 18117
    const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
1193b4be Poul Sander 2025-02-03 21:28 18118
    bool central_node_hole_register_hit_test_hole = central_node_hole;
1193b4be Poul Sander 2025-02-03 21:28 18119
    if (central_node_hole)
1193b4be Poul Sander 2025-02-03 21:28 18120
        if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
1193b4be Poul Sander 2025-02-03 21:28 18121
            if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
1193b4be Poul Sander 2025-02-03 21:28 18122
                central_node_hole_register_hit_test_hole = false;
1193b4be Poul Sander 2025-02-03 21:28 18123
    if (central_node_hole_register_hit_test_hole)
1193b4be Poul Sander 2025-02-03 21:28 18124
    {
1193b4be Poul Sander 2025-02-03 21:28 18125
        // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
1193b4be Poul Sander 2025-02-03 21:28 18126
        // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen
1193b4be Poul Sander 2025-02-03 21:28 18127
        // covering passthru node we'd have a gap on the edge not covered by the hole)
1193b4be Poul Sander 2025-02-03 21:28 18128
        IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode
1193b4be Poul Sander 2025-02-03 21:28 18129
        ImGuiDockNode* root_node = DockNodeGetRootNode(central_node);
1193b4be Poul Sander 2025-02-03 21:28 18130
        ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size);
1193b4be Poul Sander 2025-02-03 21:28 18131
        ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size);
1193b4be Poul Sander 2025-02-03 21:28 18132
        if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_HOVER_PADDING; }
1193b4be Poul Sander 2025-02-03 21:28 18133
        if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; }
1193b4be Poul Sander 2025-02-03 21:28 18134
        if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; }
1193b4be Poul Sander 2025-02-03 21:28 18135
        if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; }
1193b4be Poul Sander 2025-02-03 21:28 18136
        //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 18137
        if (central_node_hole && !hole_rect.IsInverted())
1193b4be Poul Sander 2025-02-03 21:28 18138
        {
1193b4be Poul Sander 2025-02-03 21:28 18139
            SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min);
1193b4be Poul Sander 2025-02-03 21:28 18140
            if (host_window->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 18141
                SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min);
1193b4be Poul Sander 2025-02-03 21:28 18142
        }
1193b4be Poul Sander 2025-02-03 21:28 18143
    }
1193b4be Poul Sander 2025-02-03 21:28 18144
1193b4be Poul Sander 2025-02-03 21:28 18145
    // Update position/size, process and draw resizing splitters
1193b4be Poul Sander 2025-02-03 21:28 18146
    if (node->IsRootNode() && host_window)
1193b4be Poul Sander 2025-02-03 21:28 18147
    {
1193b4be Poul Sander 2025-02-03 21:28 18148
        DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
1193b4be Poul Sander 2025-02-03 21:28 18149
        PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]);
1193b4be Poul Sander 2025-02-03 21:28 18150
        PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]);
1193b4be Poul Sander 2025-02-03 21:28 18151
        PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]);
1193b4be Poul Sander 2025-02-03 21:28 18152
        DockNodeTreeUpdateSplitter(node);
1193b4be Poul Sander 2025-02-03 21:28 18153
        PopStyleColor(3);
1193b4be Poul Sander 2025-02-03 21:28 18154
    }
1193b4be Poul Sander 2025-02-03 21:28 18155
1193b4be Poul Sander 2025-02-03 21:28 18156
    // Draw empty node background (currently can only be the Central Node)
1193b4be Poul Sander 2025-02-03 21:28 18157
    if (host_window && node->IsEmpty() && node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 18158
    {
1193b4be Poul Sander 2025-02-03 21:28 18159
        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
1193b4be Poul Sander 2025-02-03 21:28 18160
        node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg);
1193b4be Poul Sander 2025-02-03 21:28 18161
        if (node->LastBgColor != 0)
1193b4be Poul Sander 2025-02-03 21:28 18162
            host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor);
1193b4be Poul Sander 2025-02-03 21:28 18163
        node->IsBgDrawnThisFrame = true;
1193b4be Poul Sander 2025-02-03 21:28 18164
    }
1193b4be Poul Sander 2025-02-03 21:28 18165
1193b4be Poul Sander 2025-02-03 21:28 18166
    // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
1193b4be Poul Sander 2025-02-03 21:28 18167
    // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
1193b4be Poul Sander 2025-02-03 21:28 18168
    // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
1193b4be Poul Sander 2025-02-03 21:28 18169
    const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
1193b4be Poul Sander 2025-02-03 21:28 18170
    if (render_dockspace_bg && node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 18171
    {
1193b4be Poul Sander 2025-02-03 21:28 18172
        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
1193b4be Poul Sander 2025-02-03 21:28 18173
        if (central_node_hole)
1193b4be Poul Sander 2025-02-03 21:28 18174
            RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 18175
        else
1193b4be Poul Sander 2025-02-03 21:28 18176
            host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 18177
    }
1193b4be Poul Sander 2025-02-03 21:28 18178
1193b4be Poul Sander 2025-02-03 21:28 18179
    // Draw and populate Tab Bar
1193b4be Poul Sander 2025-02-03 21:28 18180
    if (host_window)
1193b4be Poul Sander 2025-02-03 21:28 18181
        host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
1193b4be Poul Sander 2025-02-03 21:28 18182
    if (host_window && node->Windows.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 18183
    {
1193b4be Poul Sander 2025-02-03 21:28 18184
        DockNodeUpdateTabBar(node, host_window);
1193b4be Poul Sander 2025-02-03 21:28 18185
    }
1193b4be Poul Sander 2025-02-03 21:28 18186
    else
1193b4be Poul Sander 2025-02-03 21:28 18187
    {
1193b4be Poul Sander 2025-02-03 21:28 18188
        node->WantCloseAll = false;
1193b4be Poul Sander 2025-02-03 21:28 18189
        node->WantCloseTabId = 0;
1193b4be Poul Sander 2025-02-03 21:28 18190
        node->IsFocused = false;
1193b4be Poul Sander 2025-02-03 21:28 18191
    }
1193b4be Poul Sander 2025-02-03 21:28 18192
    if (node->TabBar && node->TabBar->SelectedTabId)
1193b4be Poul Sander 2025-02-03 21:28 18193
        node->SelectedTabId = node->TabBar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18194
    else if (node->Windows.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 18195
        node->SelectedTabId = node->Windows[0]->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18196
1193b4be Poul Sander 2025-02-03 21:28 18197
    // Draw payload drop target
1193b4be Poul Sander 2025-02-03 21:28 18198
    if (host_window && node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 18199
        if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))
1193b4be Poul Sander 2025-02-03 21:28 18200
            BeginDockableDragDropTarget(host_window);
1193b4be Poul Sander 2025-02-03 21:28 18201
1193b4be Poul Sander 2025-02-03 21:28 18202
    // We update this after DockNodeUpdateTabBar()
1193b4be Poul Sander 2025-02-03 21:28 18203
    node->LastFrameActive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 18204
1193b4be Poul Sander 2025-02-03 21:28 18205
    // Recurse into children
1193b4be Poul Sander 2025-02-03 21:28 18206
    // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
1193b4be Poul Sander 2025-02-03 21:28 18207
    if (host_window)
1193b4be Poul Sander 2025-02-03 21:28 18208
    {
1193b4be Poul Sander 2025-02-03 21:28 18209
        if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 18210
            DockNodeUpdate(node->ChildNodes[0]);
1193b4be Poul Sander 2025-02-03 21:28 18211
        if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 18212
            DockNodeUpdate(node->ChildNodes[1]);
1193b4be Poul Sander 2025-02-03 21:28 18213
1193b4be Poul Sander 2025-02-03 21:28 18214
        // Render outer borders last (after the tab bar)
1193b4be Poul Sander 2025-02-03 21:28 18215
        if (node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 18216
            RenderWindowOuterBorders(host_window);
1193b4be Poul Sander 2025-02-03 21:28 18217
    }
1193b4be Poul Sander 2025-02-03 21:28 18218
1193b4be Poul Sander 2025-02-03 21:28 18219
    // End host window
1193b4be Poul Sander 2025-02-03 21:28 18220
    if (beginned_into_host_window) //-V1020
1193b4be Poul Sander 2025-02-03 21:28 18221
        End();
1193b4be Poul Sander 2025-02-03 21:28 18222
}
1193b4be Poul Sander 2025-02-03 21:28 18223
1193b4be Poul Sander 2025-02-03 21:28 18224
// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame.
1193b4be Poul Sander 2025-02-03 21:28 18225
static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
1193b4be Poul Sander 2025-02-03 21:28 18226
{
1193b4be Poul Sander 2025-02-03 21:28 18227
    ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
1193b4be Poul Sander 2025-02-03 21:28 18228
    ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
1193b4be Poul Sander 2025-02-03 21:28 18229
    if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
1193b4be Poul Sander 2025-02-03 21:28 18230
        return d;
1193b4be Poul Sander 2025-02-03 21:28 18231
    return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
1193b4be Poul Sander 2025-02-03 21:28 18232
}
1193b4be Poul Sander 2025-02-03 21:28 18233
1193b4be Poul Sander 2025-02-03 21:28 18234
// Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node.
1193b4be Poul Sander 2025-02-03 21:28 18235
// This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented)
1193b4be Poul Sander 2025-02-03 21:28 18236
// Custom overrides may want to decorate, group, sort entries.
1193b4be Poul Sander 2025-02-03 21:28 18237
// Please note those are internal structures: if you copy this expect occasional breakage.
1193b4be Poul Sander 2025-02-03 21:28 18238
// (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler)
1193b4be Poul Sander 2025-02-03 21:28 18239
void ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 18240
{
1193b4be Poul Sander 2025-02-03 21:28 18241
    IM_UNUSED(ctx);
1193b4be Poul Sander 2025-02-03 21:28 18242
    if (tab_bar->Tabs.Size == 1)
1193b4be Poul Sander 2025-02-03 21:28 18243
    {
1193b4be Poul Sander 2025-02-03 21:28 18244
        // "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table.
1193b4be Poul Sander 2025-02-03 21:28 18245
        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar()))
1193b4be Poul Sander 2025-02-03 21:28 18246
            node->WantHiddenTabBarToggle = true;
1193b4be Poul Sander 2025-02-03 21:28 18247
    }
1193b4be Poul Sander 2025-02-03 21:28 18248
    else
1193b4be Poul Sander 2025-02-03 21:28 18249
    {
1193b4be Poul Sander 2025-02-03 21:28 18250
        // Display a selectable list of windows in this docking node
1193b4be Poul Sander 2025-02-03 21:28 18251
        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
1193b4be Poul Sander 2025-02-03 21:28 18252
        {
1193b4be Poul Sander 2025-02-03 21:28 18253
            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
1193b4be Poul Sander 2025-02-03 21:28 18254
            if (tab->Flags & ImGuiTabItemFlags_Button)
1193b4be Poul Sander 2025-02-03 21:28 18255
                continue;
1193b4be Poul Sander 2025-02-03 21:28 18256
            if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId))
1193b4be Poul Sander 2025-02-03 21:28 18257
                TabBarQueueFocus(tab_bar, tab);
1193b4be Poul Sander 2025-02-03 21:28 18258
            SameLine();
1193b4be Poul Sander 2025-02-03 21:28 18259
            Text("   ");
1193b4be Poul Sander 2025-02-03 21:28 18260
        }
1193b4be Poul Sander 2025-02-03 21:28 18261
    }
1193b4be Poul Sander 2025-02-03 21:28 18262
}
1193b4be Poul Sander 2025-02-03 21:28 18263
1193b4be Poul Sander 2025-02-03 21:28 18264
static void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
1193b4be Poul Sander 2025-02-03 21:28 18265
{
1193b4be Poul Sander 2025-02-03 21:28 18266
    // Try to position the menu so it is more likely to stays within the same viewport
1193b4be Poul Sander 2025-02-03 21:28 18267
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18268
    if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
1193b4be Poul Sander 2025-02-03 21:28 18269
        SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 18270
    else
1193b4be Poul Sander 2025-02-03 21:28 18271
        SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 18272
    if (BeginPopup("#WindowMenu"))
1193b4be Poul Sander 2025-02-03 21:28 18273
    {
1193b4be Poul Sander 2025-02-03 21:28 18274
        node->IsFocused = true;
1193b4be Poul Sander 2025-02-03 21:28 18275
        g.DockNodeWindowMenuHandler(&g, node, tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 18276
        EndPopup();
1193b4be Poul Sander 2025-02-03 21:28 18277
    }
1193b4be Poul Sander 2025-02-03 21:28 18278
}
1193b4be Poul Sander 2025-02-03 21:28 18279
1193b4be Poul Sander 2025-02-03 21:28 18280
// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
1193b4be Poul Sander 2025-02-03 21:28 18281
bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 18282
{
1193b4be Poul Sander 2025-02-03 21:28 18283
    if (node->TabBar == NULL || node->HostWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18284
        return false;
1193b4be Poul Sander 2025-02-03 21:28 18285
    if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
1193b4be Poul Sander 2025-02-03 21:28 18286
        return false;
1193b4be Poul Sander 2025-02-03 21:28 18287
    if (node->TabBar->ID == 0)
1193b4be Poul Sander 2025-02-03 21:28 18288
        return false;
1193b4be Poul Sander 2025-02-03 21:28 18289
    Begin(node->HostWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 18290
    PushOverrideID(node->ID);
1193b4be Poul Sander 2025-02-03 21:28 18291
    bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags);
1193b4be Poul Sander 2025-02-03 21:28 18292
    IM_UNUSED(ret);
1193b4be Poul Sander 2025-02-03 21:28 18293
    IM_ASSERT(ret);
1193b4be Poul Sander 2025-02-03 21:28 18294
    return true;
1193b4be Poul Sander 2025-02-03 21:28 18295
}
1193b4be Poul Sander 2025-02-03 21:28 18296
1193b4be Poul Sander 2025-02-03 21:28 18297
void ImGui::DockNodeEndAmendTabBar()
1193b4be Poul Sander 2025-02-03 21:28 18298
{
1193b4be Poul Sander 2025-02-03 21:28 18299
    EndTabBar();
1193b4be Poul Sander 2025-02-03 21:28 18300
    PopID();
1193b4be Poul Sander 2025-02-03 21:28 18301
    End();
1193b4be Poul Sander 2025-02-03 21:28 18302
}
1193b4be Poul Sander 2025-02-03 21:28 18303
1193b4be Poul Sander 2025-02-03 21:28 18304
static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node)
1193b4be Poul Sander 2025-02-03 21:28 18305
{
1193b4be Poul Sander 2025-02-03 21:28 18306
    // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy)
1193b4be Poul Sander 2025-02-03 21:28 18307
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18308
    if (g.NavWindowingTarget)
1193b4be Poul Sander 2025-02-03 21:28 18309
        return (g.NavWindowingTarget->DockNode == node);
1193b4be Poul Sander 2025-02-03 21:28 18310
1193b4be Poul Sander 2025-02-03 21:28 18311
    // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window)
1193b4be Poul Sander 2025-02-03 21:28 18312
    if (g.NavWindow && root_node->LastFocusedNodeId == node->ID)
1193b4be Poul Sander 2025-02-03 21:28 18313
    {
1193b4be Poul Sander 2025-02-03 21:28 18314
        // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node)
1193b4be Poul Sander 2025-02-03 21:28 18315
        ImGuiWindow* parent_window = g.NavWindow->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 18316
        while (parent_window->Flags & ImGuiWindowFlags_ChildMenu)
1193b4be Poul Sander 2025-02-03 21:28 18317
            parent_window = parent_window->ParentWindow->RootWindow;
1193b4be Poul Sander 2025-02-03 21:28 18318
        ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 18319
        for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL)
1193b4be Poul Sander 2025-02-03 21:28 18320
            if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node)
1193b4be Poul Sander 2025-02-03 21:28 18321
                return true;
1193b4be Poul Sander 2025-02-03 21:28 18322
    }
1193b4be Poul Sander 2025-02-03 21:28 18323
    return false;
1193b4be Poul Sander 2025-02-03 21:28 18324
}
1193b4be Poul Sander 2025-02-03 21:28 18325
1193b4be Poul Sander 2025-02-03 21:28 18326
// Submit the tab bar corresponding to a dock node and various housekeeping details.
1193b4be Poul Sander 2025-02-03 21:28 18327
static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
1193b4be Poul Sander 2025-02-03 21:28 18328
{
1193b4be Poul Sander 2025-02-03 21:28 18329
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18330
    ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 18331
1193b4be Poul Sander 2025-02-03 21:28 18332
    const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 18333
    const bool closed_all = node->WantCloseAll && node_was_active;
1193b4be Poul Sander 2025-02-03 21:28 18334
    const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
1193b4be Poul Sander 2025-02-03 21:28 18335
    node->WantCloseAll = false;
1193b4be Poul Sander 2025-02-03 21:28 18336
    node->WantCloseTabId = 0;
1193b4be Poul Sander 2025-02-03 21:28 18337
1193b4be Poul Sander 2025-02-03 21:28 18338
    // Decide if we should use a focused title bar color
1193b4be Poul Sander 2025-02-03 21:28 18339
    bool is_focused = false;
1193b4be Poul Sander 2025-02-03 21:28 18340
    ImGuiDockNode* root_node = DockNodeGetRootNode(node);
1193b4be Poul Sander 2025-02-03 21:28 18341
    if (IsDockNodeTitleBarHighlighted(node, root_node))
1193b4be Poul Sander 2025-02-03 21:28 18342
        is_focused = true;
1193b4be Poul Sander 2025-02-03 21:28 18343
1193b4be Poul Sander 2025-02-03 21:28 18344
    // Hidden tab bar will show a triangle on the upper-left (in Begin)
1193b4be Poul Sander 2025-02-03 21:28 18345
    if (node->IsHiddenTabBar() || node->IsNoTabBar())
1193b4be Poul Sander 2025-02-03 21:28 18346
    {
1193b4be Poul Sander 2025-02-03 21:28 18347
        node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
1193b4be Poul Sander 2025-02-03 21:28 18348
        node->IsFocused = is_focused;
1193b4be Poul Sander 2025-02-03 21:28 18349
        if (is_focused)
1193b4be Poul Sander 2025-02-03 21:28 18350
            node->LastFrameFocused = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 18351
        if (node->VisibleWindow)
1193b4be Poul Sander 2025-02-03 21:28 18352
        {
1193b4be Poul Sander 2025-02-03 21:28 18353
            // Notify root of visible window (used to display title in OS task bar)
1193b4be Poul Sander 2025-02-03 21:28 18354
            if (is_focused || root_node->VisibleWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18355
                root_node->VisibleWindow = node->VisibleWindow;
1193b4be Poul Sander 2025-02-03 21:28 18356
            if (node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 18357
                node->TabBar->VisibleTabId = node->VisibleWindow->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18358
        }
1193b4be Poul Sander 2025-02-03 21:28 18359
        return;
1193b4be Poul Sander 2025-02-03 21:28 18360
    }
1193b4be Poul Sander 2025-02-03 21:28 18361
1193b4be Poul Sander 2025-02-03 21:28 18362
    // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed
1193b4be Poul Sander 2025-02-03 21:28 18363
    bool backup_skip_item = host_window->SkipItems;
1193b4be Poul Sander 2025-02-03 21:28 18364
    if (!node->IsDockSpace())
1193b4be Poul Sander 2025-02-03 21:28 18365
    {
1193b4be Poul Sander 2025-02-03 21:28 18366
        host_window->SkipItems = false;
1193b4be Poul Sander 2025-02-03 21:28 18367
        host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
1193b4be Poul Sander 2025-02-03 21:28 18368
    }
1193b4be Poul Sander 2025-02-03 21:28 18369
1193b4be Poul Sander 2025-02-03 21:28 18370
    // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
1193b4be Poul Sander 2025-02-03 21:28 18371
    // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs,
1193b4be Poul Sander 2025-02-03 21:28 18372
    // as docked windows themselves will override the stack with their own root ID.
1193b4be Poul Sander 2025-02-03 21:28 18373
    PushOverrideID(node->ID);
1193b4be Poul Sander 2025-02-03 21:28 18374
    ImGuiTabBar* tab_bar = node->TabBar;
1193b4be Poul Sander 2025-02-03 21:28 18375
    bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
1193b4be Poul Sander 2025-02-03 21:28 18376
    if (tab_bar == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18377
    {
1193b4be Poul Sander 2025-02-03 21:28 18378
        DockNodeAddTabBar(node);
1193b4be Poul Sander 2025-02-03 21:28 18379
        tab_bar = node->TabBar;
1193b4be Poul Sander 2025-02-03 21:28 18380
    }
1193b4be Poul Sander 2025-02-03 21:28 18381
1193b4be Poul Sander 2025-02-03 21:28 18382
    ImGuiID focus_tab_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 18383
    node->IsFocused = is_focused;
1193b4be Poul Sander 2025-02-03 21:28 18384
1193b4be Poul Sander 2025-02-03 21:28 18385
    const ImGuiDockNodeFlags node_flags = node->MergedFlags;
1193b4be Poul Sander 2025-02-03 21:28 18386
    const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 18387
1193b4be Poul Sander 2025-02-03 21:28 18388
    // In a dock node, the Collapse Button turns into the Window Menu button.
1193b4be Poul Sander 2025-02-03 21:28 18389
    // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
1193b4be Poul Sander 2025-02-03 21:28 18390
    if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
1193b4be Poul Sander 2025-02-03 21:28 18391
    {
1193b4be Poul Sander 2025-02-03 21:28 18392
        ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18393
        DockNodeWindowMenuUpdate(node, tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 18394
        if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id)
1193b4be Poul Sander 2025-02-03 21:28 18395
            focus_tab_id = tab_bar->NextSelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18396
        is_focused |= node->IsFocused;
1193b4be Poul Sander 2025-02-03 21:28 18397
    }
1193b4be Poul Sander 2025-02-03 21:28 18398
1193b4be Poul Sander 2025-02-03 21:28 18399
    // Layout
1193b4be Poul Sander 2025-02-03 21:28 18400
    ImRect title_bar_rect, tab_bar_rect;
1193b4be Poul Sander 2025-02-03 21:28 18401
    ImVec2 window_menu_button_pos;
1193b4be Poul Sander 2025-02-03 21:28 18402
    ImVec2 close_button_pos;
1193b4be Poul Sander 2025-02-03 21:28 18403
    DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos);
1193b4be Poul Sander 2025-02-03 21:28 18404
1193b4be Poul Sander 2025-02-03 21:28 18405
    // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.
1193b4be Poul Sander 2025-02-03 21:28 18406
    const int tabs_count_old = tab_bar->Tabs.Size;
1193b4be Poul Sander 2025-02-03 21:28 18407
    for (int window_n = 0; window_n < node->Windows.Size; window_n++)
1193b4be Poul Sander 2025-02-03 21:28 18408
    {
1193b4be Poul Sander 2025-02-03 21:28 18409
        ImGuiWindow* window = node->Windows[window_n];
1193b4be Poul Sander 2025-02-03 21:28 18410
        if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18411
            TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
1193b4be Poul Sander 2025-02-03 21:28 18412
    }
1193b4be Poul Sander 2025-02-03 21:28 18413
1193b4be Poul Sander 2025-02-03 21:28 18414
    // Title bar
1193b4be Poul Sander 2025-02-03 21:28 18415
    if (is_focused)
1193b4be Poul Sander 2025-02-03 21:28 18416
        node->LastFrameFocused = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 18417
    ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
1193b4be Poul Sander 2025-02-03 21:28 18418
    ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize);
1193b4be Poul Sander 2025-02-03 21:28 18419
    host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags);
1193b4be Poul Sander 2025-02-03 21:28 18420
1193b4be Poul Sander 2025-02-03 21:28 18421
    // Docking/Collapse button
1193b4be Poul Sander 2025-02-03 21:28 18422
    if (has_window_menu_button)
1193b4be Poul Sander 2025-02-03 21:28 18423
    {
1193b4be Poul Sander 2025-02-03 21:28 18424
        if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)
1193b4be Poul Sander 2025-02-03 21:28 18425
            OpenPopup("#WindowMenu");
1193b4be Poul Sander 2025-02-03 21:28 18426
        if (IsItemActive())
1193b4be Poul Sander 2025-02-03 21:28 18427
            focus_tab_id = tab_bar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18428
        if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f)
1193b4be Poul Sander 2025-02-03 21:28 18429
            SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode));
1193b4be Poul Sander 2025-02-03 21:28 18430
    }
1193b4be Poul Sander 2025-02-03 21:28 18431
1193b4be Poul Sander 2025-02-03 21:28 18432
    // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
1193b4be Poul Sander 2025-02-03 21:28 18433
    int tabs_unsorted_start = tab_bar->Tabs.Size;
1193b4be Poul Sander 2025-02-03 21:28 18434
    for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
1193b4be Poul Sander 2025-02-03 21:28 18435
    {
1193b4be Poul Sander 2025-02-03 21:28 18436
        // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting?
1193b4be Poul Sander 2025-02-03 21:28 18437
        tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
1193b4be Poul Sander 2025-02-03 21:28 18438
        tabs_unsorted_start = tab_n;
1193b4be Poul Sander 2025-02-03 21:28 18439
    }
1193b4be Poul Sander 2025-02-03 21:28 18440
    if (tab_bar->Tabs.Size > tabs_unsorted_start)
1193b4be Poul Sander 2025-02-03 21:28 18441
    {
1193b4be Poul Sander 2025-02-03 21:28 18442
        IMGUI_DEBUG_LOG_DOCKING("[docking] In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : "");
1193b4be Poul Sander 2025-02-03 21:28 18443
        for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
1193b4be Poul Sander 2025-02-03 21:28 18444
        {
1193b4be Poul Sander 2025-02-03 21:28 18445
            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
1193b4be Poul Sander 2025-02-03 21:28 18446
            IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1);
1193b4be Poul Sander 2025-02-03 21:28 18447
        }
1193b4be Poul Sander 2025-02-03 21:28 18448
        IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1);
1193b4be Poul Sander 2025-02-03 21:28 18449
        if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
1193b4be Poul Sander 2025-02-03 21:28 18450
            ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
1193b4be Poul Sander 2025-02-03 21:28 18451
    }
1193b4be Poul Sander 2025-02-03 21:28 18452
1193b4be Poul Sander 2025-02-03 21:28 18453
    // Apply NavWindow focus back to the tab bar
1193b4be Poul Sander 2025-02-03 21:28 18454
    if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node)
1193b4be Poul Sander 2025-02-03 21:28 18455
        tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18456
1193b4be Poul Sander 2025-02-03 21:28 18457
    // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
1193b4be Poul Sander 2025-02-03 21:28 18458
    if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
1193b4be Poul Sander 2025-02-03 21:28 18459
        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18460
    else if (tab_bar->Tabs.Size > tabs_count_old)
1193b4be Poul Sander 2025-02-03 21:28 18461
        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18462
1193b4be Poul Sander 2025-02-03 21:28 18463
    // Begin tab bar
1193b4be Poul Sander 2025-02-03 21:28 18464
    ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
1193b4be Poul Sander 2025-02-03 21:28 18465
    tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;// | ImGuiTabBarFlags_FittingPolicyScroll;
1193b4be Poul Sander 2025-02-03 21:28 18466
    tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
1193b4be Poul Sander 2025-02-03 21:28 18467
    if (!host_window->Collapsed && is_focused)
1193b4be Poul Sander 2025-02-03 21:28 18468
        tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
1193b4be Poul Sander 2025-02-03 21:28 18469
    tab_bar->ID = GetID("#TabBar");
1193b4be Poul Sander 2025-02-03 21:28 18470
    tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width
1193b4be Poul Sander 2025-02-03 21:28 18471
    tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 18472
    BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags);
1193b4be Poul Sander 2025-02-03 21:28 18473
    //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
1193b4be Poul Sander 2025-02-03 21:28 18474
1193b4be Poul Sander 2025-02-03 21:28 18475
    // Backup style colors
1193b4be Poul Sander 2025-02-03 21:28 18476
    ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
1193b4be Poul Sander 2025-02-03 21:28 18477
    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
1193b4be Poul Sander 2025-02-03 21:28 18478
        backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];
1193b4be Poul Sander 2025-02-03 21:28 18479
1193b4be Poul Sander 2025-02-03 21:28 18480
    // Submit actual tabs
1193b4be Poul Sander 2025-02-03 21:28 18481
    node->VisibleWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 18482
    for (int window_n = 0; window_n < node->Windows.Size; window_n++)
1193b4be Poul Sander 2025-02-03 21:28 18483
    {
1193b4be Poul Sander 2025-02-03 21:28 18484
        ImGuiWindow* window = node->Windows[window_n];
1193b4be Poul Sander 2025-02-03 21:28 18485
        if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
1193b4be Poul Sander 2025-02-03 21:28 18486
            continue;
1193b4be Poul Sander 2025-02-03 21:28 18487
        if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
1193b4be Poul Sander 2025-02-03 21:28 18488
        {
1193b4be Poul Sander 2025-02-03 21:28 18489
            ImGuiTabItemFlags tab_item_flags = 0;
1193b4be Poul Sander 2025-02-03 21:28 18490
            tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;
1193b4be Poul Sander 2025-02-03 21:28 18491
            if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
1193b4be Poul Sander 2025-02-03 21:28 18492
                tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
1193b4be Poul Sander 2025-02-03 21:28 18493
            if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
1193b4be Poul Sander 2025-02-03 21:28 18494
                tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
1193b4be Poul Sander 2025-02-03 21:28 18495
1193b4be Poul Sander 2025-02-03 21:28 18496
            // Apply stored style overrides for the window
1193b4be Poul Sander 2025-02-03 21:28 18497
            for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
1193b4be Poul Sander 2025-02-03 21:28 18498
                g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
1193b4be Poul Sander 2025-02-03 21:28 18499
1193b4be Poul Sander 2025-02-03 21:28 18500
            // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)
1193b4be Poul Sander 2025-02-03 21:28 18501
            bool tab_open = true;
1193b4be Poul Sander 2025-02-03 21:28 18502
            TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
1193b4be Poul Sander 2025-02-03 21:28 18503
            if (!tab_open)
1193b4be Poul Sander 2025-02-03 21:28 18504
                node->WantCloseTabId = window->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18505
            if (tab_bar->VisibleTabId == window->TabId)
1193b4be Poul Sander 2025-02-03 21:28 18506
                node->VisibleWindow = window;
1193b4be Poul Sander 2025-02-03 21:28 18507
1193b4be Poul Sander 2025-02-03 21:28 18508
            // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
1193b4be Poul Sander 2025-02-03 21:28 18509
            window->DockTabItemStatusFlags = g.LastItemData.StatusFlags;
1193b4be Poul Sander 2025-02-03 21:28 18510
            window->DockTabItemRect = g.LastItemData.Rect;
1193b4be Poul Sander 2025-02-03 21:28 18511
1193b4be Poul Sander 2025-02-03 21:28 18512
            // Update navigation ID on menu layer
1193b4be Poul Sander 2025-02-03 21:28 18513
            if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 18514
                host_window->NavLastIds[1] = window->TabId;
1193b4be Poul Sander 2025-02-03 21:28 18515
        }
1193b4be Poul Sander 2025-02-03 21:28 18516
    }
1193b4be Poul Sander 2025-02-03 21:28 18517
1193b4be Poul Sander 2025-02-03 21:28 18518
    // Restore style colors
1193b4be Poul Sander 2025-02-03 21:28 18519
    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
1193b4be Poul Sander 2025-02-03 21:28 18520
        g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];
1193b4be Poul Sander 2025-02-03 21:28 18521
1193b4be Poul Sander 2025-02-03 21:28 18522
    // Notify root of visible window (used to display title in OS task bar)
1193b4be Poul Sander 2025-02-03 21:28 18523
    if (node->VisibleWindow)
1193b4be Poul Sander 2025-02-03 21:28 18524
        if (is_focused || root_node->VisibleWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18525
            root_node->VisibleWindow = node->VisibleWindow;
1193b4be Poul Sander 2025-02-03 21:28 18526
1193b4be Poul Sander 2025-02-03 21:28 18527
    // Close button (after VisibleWindow was updated)
1193b4be Poul Sander 2025-02-03 21:28 18528
    // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId
1193b4be Poul Sander 2025-02-03 21:28 18529
    const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;
1193b4be Poul Sander 2025-02-03 21:28 18530
    const bool close_button_is_visible = node->HasCloseButton;
1193b4be Poul Sander 2025-02-03 21:28 18531
    //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one)
1193b4be Poul Sander 2025-02-03 21:28 18532
    if (close_button_is_visible)
1193b4be Poul Sander 2025-02-03 21:28 18533
    {
1193b4be Poul Sander 2025-02-03 21:28 18534
        if (!close_button_is_enabled)
1193b4be Poul Sander 2025-02-03 21:28 18535
        {
1193b4be Poul Sander 2025-02-03 21:28 18536
            PushItemFlag(ImGuiItemFlags_Disabled, true);
1193b4be Poul Sander 2025-02-03 21:28 18537
            PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f));
1193b4be Poul Sander 2025-02-03 21:28 18538
        }
1193b4be Poul Sander 2025-02-03 21:28 18539
        if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos))
1193b4be Poul Sander 2025-02-03 21:28 18540
        {
1193b4be Poul Sander 2025-02-03 21:28 18541
            node->WantCloseAll = true;
1193b4be Poul Sander 2025-02-03 21:28 18542
            for (int n = 0; n < tab_bar->Tabs.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 18543
                TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]);
1193b4be Poul Sander 2025-02-03 21:28 18544
        }
1193b4be Poul Sander 2025-02-03 21:28 18545
        //if (IsItemActive())
1193b4be Poul Sander 2025-02-03 21:28 18546
        //    focus_tab_id = tab_bar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18547
        if (!close_button_is_enabled)
1193b4be Poul Sander 2025-02-03 21:28 18548
        {
1193b4be Poul Sander 2025-02-03 21:28 18549
            PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 18550
            PopItemFlag();
1193b4be Poul Sander 2025-02-03 21:28 18551
        }
1193b4be Poul Sander 2025-02-03 21:28 18552
    }
1193b4be Poul Sander 2025-02-03 21:28 18553
1193b4be Poul Sander 2025-02-03 21:28 18554
    // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
1193b4be Poul Sander 2025-02-03 21:28 18555
    // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them.
1193b4be Poul Sander 2025-02-03 21:28 18556
    ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
1193b4be Poul Sander 2025-02-03 21:28 18557
    if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
1193b4be Poul Sander 2025-02-03 21:28 18558
    {
1193b4be Poul Sander 2025-02-03 21:28 18559
        // AllowOverlap mode required for appending into dock node tab bar,
1193b4be Poul Sander 2025-02-03 21:28 18560
        // otherwise dragging window will steal HoveredId and amended tabs cannot get them.
1193b4be Poul Sander 2025-02-03 21:28 18561
        bool held;
1193b4be Poul Sander 2025-02-03 21:28 18562
        KeepAliveID(title_bar_id);
1193b4be Poul Sander 2025-02-03 21:28 18563
        ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap);
1193b4be Poul Sander 2025-02-03 21:28 18564
        if (g.HoveredId == title_bar_id)
1193b4be Poul Sander 2025-02-03 21:28 18565
        {
1193b4be Poul Sander 2025-02-03 21:28 18566
            g.LastItemData.ID = title_bar_id;
1193b4be Poul Sander 2025-02-03 21:28 18567
        }
1193b4be Poul Sander 2025-02-03 21:28 18568
        if (held)
1193b4be Poul Sander 2025-02-03 21:28 18569
        {
1193b4be Poul Sander 2025-02-03 21:28 18570
            if (IsMouseClicked(0))
1193b4be Poul Sander 2025-02-03 21:28 18571
                focus_tab_id = tab_bar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18572
1193b4be Poul Sander 2025-02-03 21:28 18573
            // Forward moving request to selected window
1193b4be Poul Sander 2025-02-03 21:28 18574
            if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
1193b4be Poul Sander 2025-02-03 21:28 18575
                StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space
1193b4be Poul Sander 2025-02-03 21:28 18576
        }
1193b4be Poul Sander 2025-02-03 21:28 18577
    }
1193b4be Poul Sander 2025-02-03 21:28 18578
1193b4be Poul Sander 2025-02-03 21:28 18579
    // Forward focus from host node to selected window
1193b4be Poul Sander 2025-02-03 21:28 18580
    //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
1193b4be Poul Sander 2025-02-03 21:28 18581
    //    focus_tab_id = tab_bar->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18582
1193b4be Poul Sander 2025-02-03 21:28 18583
    // When clicked on a tab we requested focus to the docked child
1193b4be Poul Sander 2025-02-03 21:28 18584
    // This overrides the value set by "forward focus from host node to selected window".
1193b4be Poul Sander 2025-02-03 21:28 18585
    if (tab_bar->NextSelectedTabId)
1193b4be Poul Sander 2025-02-03 21:28 18586
        focus_tab_id = tab_bar->NextSelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 18587
1193b4be Poul Sander 2025-02-03 21:28 18588
    // Apply navigation focus
1193b4be Poul Sander 2025-02-03 21:28 18589
    if (focus_tab_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 18590
        if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
1193b4be Poul Sander 2025-02-03 21:28 18591
            if (tab->Window)
1193b4be Poul Sander 2025-02-03 21:28 18592
            {
1193b4be Poul Sander 2025-02-03 21:28 18593
                FocusWindow(tab->Window);
1193b4be Poul Sander 2025-02-03 21:28 18594
                NavInitWindow(tab->Window, false);
1193b4be Poul Sander 2025-02-03 21:28 18595
            }
1193b4be Poul Sander 2025-02-03 21:28 18596
1193b4be Poul Sander 2025-02-03 21:28 18597
    EndTabBar();
1193b4be Poul Sander 2025-02-03 21:28 18598
    PopID();
1193b4be Poul Sander 2025-02-03 21:28 18599
1193b4be Poul Sander 2025-02-03 21:28 18600
    // Restore SkipItems flag
1193b4be Poul Sander 2025-02-03 21:28 18601
    if (!node->IsDockSpace())
1193b4be Poul Sander 2025-02-03 21:28 18602
    {
1193b4be Poul Sander 2025-02-03 21:28 18603
        host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
1193b4be Poul Sander 2025-02-03 21:28 18604
        host_window->SkipItems = backup_skip_item;
1193b4be Poul Sander 2025-02-03 21:28 18605
    }
1193b4be Poul Sander 2025-02-03 21:28 18606
}
1193b4be Poul Sander 2025-02-03 21:28 18607
1193b4be Poul Sander 2025-02-03 21:28 18608
static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 18609
{
1193b4be Poul Sander 2025-02-03 21:28 18610
    IM_ASSERT(node->TabBar == NULL);
1193b4be Poul Sander 2025-02-03 21:28 18611
    node->TabBar = IM_NEW(ImGuiTabBar);
1193b4be Poul Sander 2025-02-03 21:28 18612
}
1193b4be Poul Sander 2025-02-03 21:28 18613
1193b4be Poul Sander 2025-02-03 21:28 18614
static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 18615
{
1193b4be Poul Sander 2025-02-03 21:28 18616
    if (node->TabBar == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18617
        return;
1193b4be Poul Sander 2025-02-03 21:28 18618
    IM_DELETE(node->TabBar);
1193b4be Poul Sander 2025-02-03 21:28 18619
    node->TabBar = NULL;
1193b4be Poul Sander 2025-02-03 21:28 18620
}
1193b4be Poul Sander 2025-02-03 21:28 18621
1193b4be Poul Sander 2025-02-03 21:28 18622
static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
1193b4be Poul Sander 2025-02-03 21:28 18623
{
1193b4be Poul Sander 2025-02-03 21:28 18624
    if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
1193b4be Poul Sander 2025-02-03 21:28 18625
        return false;
1193b4be Poul Sander 2025-02-03 21:28 18626
1193b4be Poul Sander 2025-02-03 21:28 18627
    ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
1193b4be Poul Sander 2025-02-03 21:28 18628
    ImGuiWindowClass* payload_class = &payload->WindowClass;
1193b4be Poul Sander 2025-02-03 21:28 18629
    if (host_class->ClassId != payload_class->ClassId)
1193b4be Poul Sander 2025-02-03 21:28 18630
    {
1193b4be Poul Sander 2025-02-03 21:28 18631
        bool pass = false;
1193b4be Poul Sander 2025-02-03 21:28 18632
        if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
1193b4be Poul Sander 2025-02-03 21:28 18633
            pass = true;
1193b4be Poul Sander 2025-02-03 21:28 18634
        if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
1193b4be Poul Sander 2025-02-03 21:28 18635
            pass = true;
1193b4be Poul Sander 2025-02-03 21:28 18636
        if (!pass)
1193b4be Poul Sander 2025-02-03 21:28 18637
            return false;
1193b4be Poul Sander 2025-02-03 21:28 18638
    }
1193b4be Poul Sander 2025-02-03 21:28 18639
1193b4be Poul Sander 2025-02-03 21:28 18640
    // Prevent docking any window created above a popup
1193b4be Poul Sander 2025-02-03 21:28 18641
    // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features),
1193b4be Poul Sander 2025-02-03 21:28 18642
    // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test.
1193b4be Poul Sander 2025-02-03 21:28 18643
    // But it would requires more work on our end because the dock host windows is technically created in NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 18644
    // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking.
1193b4be Poul Sander 2025-02-03 21:28 18645
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18646
    for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
1193b4be Poul Sander 2025-02-03 21:28 18647
        if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window)
1193b4be Poul Sander 2025-02-03 21:28 18648
            if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window))   // Payload is created from within a popup begin stack.
1193b4be Poul Sander 2025-02-03 21:28 18649
                return false;
1193b4be Poul Sander 2025-02-03 21:28 18650
1193b4be Poul Sander 2025-02-03 21:28 18651
    return true;
1193b4be Poul Sander 2025-02-03 21:28 18652
}
1193b4be Poul Sander 2025-02-03 21:28 18653
1193b4be Poul Sander 2025-02-03 21:28 18654
static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
1193b4be Poul Sander 2025-02-03 21:28 18655
{
1193b4be Poul Sander 2025-02-03 21:28 18656
    if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering
1193b4be Poul Sander 2025-02-03 21:28 18657
        return true;
1193b4be Poul Sander 2025-02-03 21:28 18658
1193b4be Poul Sander 2025-02-03 21:28 18659
    const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
1193b4be Poul Sander 2025-02-03 21:28 18660
    for (int payload_n = 0; payload_n < payload_count; payload_n++)
1193b4be Poul Sander 2025-02-03 21:28 18661
    {
1193b4be Poul Sander 2025-02-03 21:28 18662
        ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
1193b4be Poul Sander 2025-02-03 21:28 18663
        if (DockNodeIsDropAllowedOne(payload, host_window))
1193b4be Poul Sander 2025-02-03 21:28 18664
            return true;
1193b4be Poul Sander 2025-02-03 21:28 18665
    }
1193b4be Poul Sander 2025-02-03 21:28 18666
    return false;
1193b4be Poul Sander 2025-02-03 21:28 18667
}
1193b4be Poul Sander 2025-02-03 21:28 18668
1193b4be Poul Sander 2025-02-03 21:28 18669
// window menu button == collapse button when not in a dock node.
1193b4be Poul Sander 2025-02-03 21:28 18670
// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code.
1193b4be Poul Sander 2025-02-03 21:28 18671
static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos)
1193b4be Poul Sander 2025-02-03 21:28 18672
{
1193b4be Poul Sander 2025-02-03 21:28 18673
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18674
    ImGuiStyle& style = g.Style;
1193b4be Poul Sander 2025-02-03 21:28 18675
1193b4be Poul Sander 2025-02-03 21:28 18676
    ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 18677
    if (out_title_rect) { *out_title_rect = r; }
1193b4be Poul Sander 2025-02-03 21:28 18678
1193b4be Poul Sander 2025-02-03 21:28 18679
    r.Min.x += style.WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 18680
    r.Max.x -= style.WindowBorderSize;
1193b4be Poul Sander 2025-02-03 21:28 18681
1193b4be Poul Sander 2025-02-03 21:28 18682
    float button_sz = g.FontSize;
1193b4be Poul Sander 2025-02-03 21:28 18683
    r.Min.x += style.FramePadding.x;
1193b4be Poul Sander 2025-02-03 21:28 18684
    r.Max.x -= style.FramePadding.x;
1193b4be Poul Sander 2025-02-03 21:28 18685
    ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 18686
    if (node->HasCloseButton)
1193b4be Poul Sander 2025-02-03 21:28 18687
    {
1193b4be Poul Sander 2025-02-03 21:28 18688
        if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 18689
        r.Max.x -= button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 18690
    }
1193b4be Poul Sander 2025-02-03 21:28 18691
    if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left)
1193b4be Poul Sander 2025-02-03 21:28 18692
    {
1193b4be Poul Sander 2025-02-03 21:28 18693
        r.Min.x += button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 18694
    }
1193b4be Poul Sander 2025-02-03 21:28 18695
    else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)
1193b4be Poul Sander 2025-02-03 21:28 18696
    {
1193b4be Poul Sander 2025-02-03 21:28 18697
        window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
1193b4be Poul Sander 2025-02-03 21:28 18698
        r.Max.x -= button_sz + style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 18699
    }
1193b4be Poul Sander 2025-02-03 21:28 18700
    if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
1193b4be Poul Sander 2025-02-03 21:28 18701
    if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
1193b4be Poul Sander 2025-02-03 21:28 18702
}
1193b4be Poul Sander 2025-02-03 21:28 18703
1193b4be Poul Sander 2025-02-03 21:28 18704
void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
1193b4be Poul Sander 2025-02-03 21:28 18705
{
1193b4be Poul Sander 2025-02-03 21:28 18706
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18707
    const float dock_spacing = g.Style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 18708
    const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
1193b4be Poul Sander 2025-02-03 21:28 18709
    pos_new[axis ^ 1] = pos_old[axis ^ 1];
1193b4be Poul Sander 2025-02-03 21:28 18710
    size_new[axis ^ 1] = size_old[axis ^ 1];
1193b4be Poul Sander 2025-02-03 21:28 18711
1193b4be Poul Sander 2025-02-03 21:28 18712
    // Distribute size on given axis (with a desired size or equally)
1193b4be Poul Sander 2025-02-03 21:28 18713
    const float w_avail = size_old[axis] - dock_spacing;
1193b4be Poul Sander 2025-02-03 21:28 18714
    if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
1193b4be Poul Sander 2025-02-03 21:28 18715
    {
1193b4be Poul Sander 2025-02-03 21:28 18716
        size_new[axis] = size_new_desired[axis];
1193b4be Poul Sander 2025-02-03 21:28 18717
        size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
1193b4be Poul Sander 2025-02-03 21:28 18718
    }
1193b4be Poul Sander 2025-02-03 21:28 18719
    else
1193b4be Poul Sander 2025-02-03 21:28 18720
    {
1193b4be Poul Sander 2025-02-03 21:28 18721
        size_new[axis] = IM_TRUNC(w_avail * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 18722
        size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
1193b4be Poul Sander 2025-02-03 21:28 18723
    }
1193b4be Poul Sander 2025-02-03 21:28 18724
1193b4be Poul Sander 2025-02-03 21:28 18725
    // Position each node
1193b4be Poul Sander 2025-02-03 21:28 18726
    if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
1193b4be Poul Sander 2025-02-03 21:28 18727
    {
1193b4be Poul Sander 2025-02-03 21:28 18728
        pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
1193b4be Poul Sander 2025-02-03 21:28 18729
    }
1193b4be Poul Sander 2025-02-03 21:28 18730
    else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
1193b4be Poul Sander 2025-02-03 21:28 18731
    {
1193b4be Poul Sander 2025-02-03 21:28 18732
        pos_new[axis] = pos_old[axis];
1193b4be Poul Sander 2025-02-03 21:28 18733
        pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
1193b4be Poul Sander 2025-02-03 21:28 18734
    }
1193b4be Poul Sander 2025-02-03 21:28 18735
}
1193b4be Poul Sander 2025-02-03 21:28 18736
1193b4be Poul Sander 2025-02-03 21:28 18737
// Retrieve the drop rectangles for a given direction or for the center + perform hit testing.
1193b4be Poul Sander 2025-02-03 21:28 18738
bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
1193b4be Poul Sander 2025-02-03 21:28 18739
{
1193b4be Poul Sander 2025-02-03 21:28 18740
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18741
1193b4be Poul Sander 2025-02-03 21:28 18742
    const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
1193b4be Poul Sander 2025-02-03 21:28 18743
    const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
1193b4be Poul Sander 2025-02-03 21:28 18744
    float hs_w; // Half-size, longer axis
1193b4be Poul Sander 2025-02-03 21:28 18745
    float hs_h; // Half-size, smaller axis
1193b4be Poul Sander 2025-02-03 21:28 18746
    ImVec2 off; // Distance from edge or center
1193b4be Poul Sander 2025-02-03 21:28 18747
    if (outer_docking)
1193b4be Poul Sander 2025-02-03 21:28 18748
    {
1193b4be Poul Sander 2025-02-03 21:28 18749
        //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
1193b4be Poul Sander 2025-02-03 21:28 18750
        //hs_h = ImTrunc(hs_w * 0.15f);
1193b4be Poul Sander 2025-02-03 21:28 18751
        //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
1193b4be Poul Sander 2025-02-03 21:28 18752
        hs_w = ImTrunc(hs_for_central_nodes * 1.50f);
1193b4be Poul Sander 2025-02-03 21:28 18753
        hs_h = ImTrunc(hs_for_central_nodes * 0.80f);
1193b4be Poul Sander 2025-02-03 21:28 18754
        off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h));
1193b4be Poul Sander 2025-02-03 21:28 18755
    }
1193b4be Poul Sander 2025-02-03 21:28 18756
    else
1193b4be Poul Sander 2025-02-03 21:28 18757
    {
1193b4be Poul Sander 2025-02-03 21:28 18758
        hs_w = ImTrunc(hs_for_central_nodes);
1193b4be Poul Sander 2025-02-03 21:28 18759
        hs_h = ImTrunc(hs_for_central_nodes * 0.90f);
1193b4be Poul Sander 2025-02-03 21:28 18760
        off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f));
1193b4be Poul Sander 2025-02-03 21:28 18761
    }
1193b4be Poul Sander 2025-02-03 21:28 18762
1193b4be Poul Sander 2025-02-03 21:28 18763
    ImVec2 c = ImTrunc(parent.GetCenter());
1193b4be Poul Sander 2025-02-03 21:28 18764
    if      (dir == ImGuiDir_None)  { out_r = ImRect(c.x - hs_w, c.y - hs_w,         c.x + hs_w, c.y + hs_w);         }
1193b4be Poul Sander 2025-02-03 21:28 18765
    else if (dir == ImGuiDir_Up)    { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); }
1193b4be Poul Sander 2025-02-03 21:28 18766
    else if (dir == ImGuiDir_Down)  { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); }
1193b4be Poul Sander 2025-02-03 21:28 18767
    else if (dir == ImGuiDir_Left)  { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); }
1193b4be Poul Sander 2025-02-03 21:28 18768
    else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); }
1193b4be Poul Sander 2025-02-03 21:28 18769
1193b4be Poul Sander 2025-02-03 21:28 18770
    if (test_mouse_pos == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18771
        return false;
1193b4be Poul Sander 2025-02-03 21:28 18772
1193b4be Poul Sander 2025-02-03 21:28 18773
    ImRect hit_r = out_r;
1193b4be Poul Sander 2025-02-03 21:28 18774
    if (!outer_docking)
1193b4be Poul Sander 2025-02-03 21:28 18775
    {
1193b4be Poul Sander 2025-02-03 21:28 18776
        // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
1193b4be Poul Sander 2025-02-03 21:28 18777
        hit_r.Expand(ImTrunc(hs_w * 0.30f));
1193b4be Poul Sander 2025-02-03 21:28 18778
        ImVec2 mouse_delta = (*test_mouse_pos - c);
1193b4be Poul Sander 2025-02-03 21:28 18779
        float mouse_delta_len2 = ImLengthSqr(mouse_delta);
1193b4be Poul Sander 2025-02-03 21:28 18780
        float r_threshold_center = hs_w * 1.4f;
1193b4be Poul Sander 2025-02-03 21:28 18781
        float r_threshold_sides = hs_w * (1.4f + 1.2f);
1193b4be Poul Sander 2025-02-03 21:28 18782
        if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
1193b4be Poul Sander 2025-02-03 21:28 18783
            return (dir == ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 18784
        if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
1193b4be Poul Sander 2025-02-03 21:28 18785
            return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
1193b4be Poul Sander 2025-02-03 21:28 18786
    }
1193b4be Poul Sander 2025-02-03 21:28 18787
    return hit_r.Contains(*test_mouse_pos);
1193b4be Poul Sander 2025-02-03 21:28 18788
}
1193b4be Poul Sander 2025-02-03 21:28 18789
1193b4be Poul Sander 2025-02-03 21:28 18790
// host_node may be NULL if the window doesn't have a DockNode already.
1193b4be Poul Sander 2025-02-03 21:28 18791
// FIXME-DOCK: This is misnamed since it's also doing the filtering.
1193b4be Poul Sander 2025-02-03 21:28 18792
static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking)
1193b4be Poul Sander 2025-02-03 21:28 18793
{
1193b4be Poul Sander 2025-02-03 21:28 18794
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18795
1193b4be Poul Sander 2025-02-03 21:28 18796
    // There is an edge case when docking into a dockspace which only has inactive nodes.
1193b4be Poul Sander 2025-02-03 21:28 18797
    // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
1193b4be Poul Sander 2025-02-03 21:28 18798
    // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
1193b4be Poul Sander 2025-02-03 21:28 18799
    if (payload_node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18800
        payload_node = payload_window->DockNodeAsHost;
1193b4be Poul Sander 2025-02-03 21:28 18801
    ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
1193b4be Poul Sander 2025-02-03 21:28 18802
    if (ref_node_for_rect)
1193b4be Poul Sander 2025-02-03 21:28 18803
        IM_ASSERT(ref_node_for_rect->IsVisible == true);
1193b4be Poul Sander 2025-02-03 21:28 18804
1193b4be Poul Sander 2025-02-03 21:28 18805
    // Filter, figure out where we are allowed to dock
1193b4be Poul Sander 2025-02-03 21:28 18806
    ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet;
1193b4be Poul Sander 2025-02-03 21:28 18807
    ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;
1193b4be Poul Sander 2025-02-03 21:28 18808
    data->IsCenterAvailable = true;
1193b4be Poul Sander 2025-02-03 21:28 18809
    if (is_outer_docking)
1193b4be Poul Sander 2025-02-03 21:28 18810
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18811
    else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe)
1193b4be Poul Sander 2025-02-03 21:28 18812
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18813
    else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 18814
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18815
    else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?
1193b4be Poul Sander 2025-02-03 21:28 18816
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18817
    else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty()))
1193b4be Poul Sander 2025-02-03 21:28 18818
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18819
    else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty())
1193b4be Poul Sander 2025-02-03 21:28 18820
        data->IsCenterAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18821
1193b4be Poul Sander 2025-02-03 21:28 18822
    data->IsSidesAvailable = true;
1193b4be Poul Sander 2025-02-03 21:28 18823
    if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit)
1193b4be Poul Sander 2025-02-03 21:28 18824
        data->IsSidesAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18825
    else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 18826
        data->IsSidesAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18827
    else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)
1193b4be Poul Sander 2025-02-03 21:28 18828
        data->IsSidesAvailable = false;
1193b4be Poul Sander 2025-02-03 21:28 18829
1193b4be Poul Sander 2025-02-03 21:28 18830
    // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
1193b4be Poul Sander 2025-02-03 21:28 18831
    data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton);
1193b4be Poul Sander 2025-02-03 21:28 18832
    data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
1193b4be Poul Sander 2025-02-03 21:28 18833
    data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 18834
    data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
1193b4be Poul Sander 2025-02-03 21:28 18835
1193b4be Poul Sander 2025-02-03 21:28 18836
    // Calculate drop shapes geometry for allowed splitting directions
1193b4be Poul Sander 2025-02-03 21:28 18837
    IM_ASSERT(ImGuiDir_None == -1);
1193b4be Poul Sander 2025-02-03 21:28 18838
    data->SplitNode = host_node;
1193b4be Poul Sander 2025-02-03 21:28 18839
    data->SplitDir = ImGuiDir_None;
1193b4be Poul Sander 2025-02-03 21:28 18840
    data->IsSplitDirExplicit = false;
1193b4be Poul Sander 2025-02-03 21:28 18841
    if (!host_window->Collapsed)
1193b4be Poul Sander 2025-02-03 21:28 18842
        for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
1193b4be Poul Sander 2025-02-03 21:28 18843
        {
1193b4be Poul Sander 2025-02-03 21:28 18844
            if (dir == ImGuiDir_None && !data->IsCenterAvailable)
1193b4be Poul Sander 2025-02-03 21:28 18845
                continue;
1193b4be Poul Sander 2025-02-03 21:28 18846
            if (dir != ImGuiDir_None && !data->IsSidesAvailable)
1193b4be Poul Sander 2025-02-03 21:28 18847
                continue;
1193b4be Poul Sander 2025-02-03 21:28 18848
            if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
1193b4be Poul Sander 2025-02-03 21:28 18849
            {
1193b4be Poul Sander 2025-02-03 21:28 18850
                data->SplitDir = (ImGuiDir)dir;
1193b4be Poul Sander 2025-02-03 21:28 18851
                data->IsSplitDirExplicit = true;
1193b4be Poul Sander 2025-02-03 21:28 18852
            }
1193b4be Poul Sander 2025-02-03 21:28 18853
        }
1193b4be Poul Sander 2025-02-03 21:28 18854
1193b4be Poul Sander 2025-02-03 21:28 18855
    // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
1193b4be Poul Sander 2025-02-03 21:28 18856
    data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
1193b4be Poul Sander 2025-02-03 21:28 18857
    if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)
1193b4be Poul Sander 2025-02-03 21:28 18858
        data->IsDropAllowed = false;
1193b4be Poul Sander 2025-02-03 21:28 18859
1193b4be Poul Sander 2025-02-03 21:28 18860
    // Calculate split area
1193b4be Poul Sander 2025-02-03 21:28 18861
    data->SplitRatio = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 18862
    if (data->SplitDir != ImGuiDir_None)
1193b4be Poul Sander 2025-02-03 21:28 18863
    {
1193b4be Poul Sander 2025-02-03 21:28 18864
        ImGuiDir split_dir = data->SplitDir;
1193b4be Poul Sander 2025-02-03 21:28 18865
        ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
1193b4be Poul Sander 2025-02-03 21:28 18866
        ImVec2 pos_new, pos_old = data->FutureNode.Pos;
1193b4be Poul Sander 2025-02-03 21:28 18867
        ImVec2 size_new, size_old = data->FutureNode.Size;
1193b4be Poul Sander 2025-02-03 21:28 18868
        DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size);
1193b4be Poul Sander 2025-02-03 21:28 18869
1193b4be Poul Sander 2025-02-03 21:28 18870
        // Calculate split ratio so we can pass it down the docking request
1193b4be Poul Sander 2025-02-03 21:28 18871
        float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
1193b4be Poul Sander 2025-02-03 21:28 18872
        data->FutureNode.Pos = pos_new;
1193b4be Poul Sander 2025-02-03 21:28 18873
        data->FutureNode.Size = size_new;
1193b4be Poul Sander 2025-02-03 21:28 18874
        data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
1193b4be Poul Sander 2025-02-03 21:28 18875
    }
1193b4be Poul Sander 2025-02-03 21:28 18876
}
1193b4be Poul Sander 2025-02-03 21:28 18877
1193b4be Poul Sander 2025-02-03 21:28 18878
static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
1193b4be Poul Sander 2025-02-03 21:28 18879
{
1193b4be Poul Sander 2025-02-03 21:28 18880
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 18881
    IM_ASSERT(g.CurrentWindow == host_window);   // Because we rely on font size to calculate tab sizes
1193b4be Poul Sander 2025-02-03 21:28 18882
1193b4be Poul Sander 2025-02-03 21:28 18883
    // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
1193b4be Poul Sander 2025-02-03 21:28 18884
    // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
1193b4be Poul Sander 2025-02-03 21:28 18885
    const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
1193b4be Poul Sander 2025-02-03 21:28 18886
1193b4be Poul Sander 2025-02-03 21:28 18887
    // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
1193b4be Poul Sander 2025-02-03 21:28 18888
    int overlay_draw_lists_count = 0;
1193b4be Poul Sander 2025-02-03 21:28 18889
    ImDrawList* overlay_draw_lists[2];
1193b4be Poul Sander 2025-02-03 21:28 18890
    overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 18891
    if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
1193b4be Poul Sander 2025-02-03 21:28 18892
        overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
1193b4be Poul Sander 2025-02-03 21:28 18893
1193b4be Poul Sander 2025-02-03 21:28 18894
    // Draw main preview rectangle
1193b4be Poul Sander 2025-02-03 21:28 18895
    const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
1193b4be Poul Sander 2025-02-03 21:28 18896
    const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
1193b4be Poul Sander 2025-02-03 21:28 18897
    const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
1193b4be Poul Sander 2025-02-03 21:28 18898
    const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
1193b4be Poul Sander 2025-02-03 21:28 18899
1193b4be Poul Sander 2025-02-03 21:28 18900
    // Display area preview
1193b4be Poul Sander 2025-02-03 21:28 18901
    const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
1193b4be Poul Sander 2025-02-03 21:28 18902
    if (data->IsDropAllowed)
1193b4be Poul Sander 2025-02-03 21:28 18903
    {
1193b4be Poul Sander 2025-02-03 21:28 18904
        ImRect overlay_rect = data->FutureNode.Rect();
1193b4be Poul Sander 2025-02-03 21:28 18905
        if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
1193b4be Poul Sander 2025-02-03 21:28 18906
            overlay_rect.Min.y += GetFrameHeight();
1193b4be Poul Sander 2025-02-03 21:28 18907
        if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
1193b4be Poul Sander 2025-02-03 21:28 18908
            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
1193b4be Poul Sander 2025-02-03 21:28 18909
                overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize));
1193b4be Poul Sander 2025-02-03 21:28 18910
    }
1193b4be Poul Sander 2025-02-03 21:28 18911
1193b4be Poul Sander 2025-02-03 21:28 18912
    // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
1193b4be Poul Sander 2025-02-03 21:28 18913
    if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
1193b4be Poul Sander 2025-02-03 21:28 18914
    {
1193b4be Poul Sander 2025-02-03 21:28 18915
        // Compute target tab bar geometry so we can locate our preview tabs
1193b4be Poul Sander 2025-02-03 21:28 18916
        ImRect tab_bar_rect;
1193b4be Poul Sander 2025-02-03 21:28 18917
        DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 18918
        ImVec2 tab_pos = tab_bar_rect.Min;
1193b4be Poul Sander 2025-02-03 21:28 18919
        if (host_node && host_node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 18920
        {
1193b4be Poul Sander 2025-02-03 21:28 18921
            if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
1193b4be Poul Sander 2025-02-03 21:28 18922
                tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission.
1193b4be Poul Sander 2025-02-03 21:28 18923
            else
1193b4be Poul Sander 2025-02-03 21:28 18924
                tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x;
1193b4be Poul Sander 2025-02-03 21:28 18925
        }
1193b4be Poul Sander 2025-02-03 21:28 18926
        else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
1193b4be Poul Sander 2025-02-03 21:28 18927
        {
1193b4be Poul Sander 2025-02-03 21:28 18928
            tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar
1193b4be Poul Sander 2025-02-03 21:28 18929
        }
1193b4be Poul Sander 2025-02-03 21:28 18930
1193b4be Poul Sander 2025-02-03 21:28 18931
        // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
1193b4be Poul Sander 2025-02-03 21:28 18932
        if (root_payload->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 18933
            IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);
1193b4be Poul Sander 2025-02-03 21:28 18934
        ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;
1193b4be Poul Sander 2025-02-03 21:28 18935
        const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;
1193b4be Poul Sander 2025-02-03 21:28 18936
        for (int payload_n = 0; payload_n < payload_count; payload_n++)
1193b4be Poul Sander 2025-02-03 21:28 18937
        {
1193b4be Poul Sander 2025-02-03 21:28 18938
            // DockNode's TabBar may have non-window Tabs manually appended by user
1193b4be Poul Sander 2025-02-03 21:28 18939
            ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;
1193b4be Poul Sander 2025-02-03 21:28 18940
            if (tab_bar_with_payload && payload_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 18941
                continue;
1193b4be Poul Sander 2025-02-03 21:28 18942
            if (!DockNodeIsDropAllowedOne(payload_window, host_window))
1193b4be Poul Sander 2025-02-03 21:28 18943
                continue;
1193b4be Poul Sander 2025-02-03 21:28 18944
1193b4be Poul Sander 2025-02-03 21:28 18945
            // Calculate the tab bounding box for each payload window
1193b4be Poul Sander 2025-02-03 21:28 18946
            ImVec2 tab_size = TabItemCalcSize(payload_window);
1193b4be Poul Sander 2025-02-03 21:28 18947
            ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
1193b4be Poul Sander 2025-02-03 21:28 18948
            tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
1193b4be Poul Sander 2025-02-03 21:28 18949
            const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
1193b4be Poul Sander 2025-02-03 21:28 18950
            const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabSelected]);
1193b4be Poul Sander 2025-02-03 21:28 18951
            PushStyleColor(ImGuiCol_Text, overlay_col_text);
1193b4be Poul Sander 2025-02-03 21:28 18952
            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
1193b4be Poul Sander 2025-02-03 21:28 18953
            {
1193b4be Poul Sander 2025-02-03 21:28 18954
                ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0;
1193b4be Poul Sander 2025-02-03 21:28 18955
                if (!tab_bar_rect.Contains(tab_bb))
1193b4be Poul Sander 2025-02-03 21:28 18956
                    overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
1193b4be Poul Sander 2025-02-03 21:28 18957
                TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
1193b4be Poul Sander 2025-02-03 21:28 18958
                TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 18959
                if (!tab_bar_rect.Contains(tab_bb))
1193b4be Poul Sander 2025-02-03 21:28 18960
                    overlay_draw_lists[overlay_n]->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 18961
            }
1193b4be Poul Sander 2025-02-03 21:28 18962
            PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 18963
        }
1193b4be Poul Sander 2025-02-03 21:28 18964
    }
1193b4be Poul Sander 2025-02-03 21:28 18965
1193b4be Poul Sander 2025-02-03 21:28 18966
    // Display drop boxes
1193b4be Poul Sander 2025-02-03 21:28 18967
    const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
1193b4be Poul Sander 2025-02-03 21:28 18968
    for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
1193b4be Poul Sander 2025-02-03 21:28 18969
    {
1193b4be Poul Sander 2025-02-03 21:28 18970
        if (!data->DropRectsDraw[dir + 1].IsInverted())
1193b4be Poul Sander 2025-02-03 21:28 18971
        {
1193b4be Poul Sander 2025-02-03 21:28 18972
            ImRect draw_r = data->DropRectsDraw[dir + 1];
1193b4be Poul Sander 2025-02-03 21:28 18973
            ImRect draw_r_in = draw_r;
1193b4be Poul Sander 2025-02-03 21:28 18974
            draw_r_in.Expand(-2.0f);
1193b4be Poul Sander 2025-02-03 21:28 18975
            ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
1193b4be Poul Sander 2025-02-03 21:28 18976
            for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
1193b4be Poul Sander 2025-02-03 21:28 18977
            {
1193b4be Poul Sander 2025-02-03 21:28 18978
                ImVec2 center = ImFloor(draw_r_in.GetCenter());
1193b4be Poul Sander 2025-02-03 21:28 18979
                overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
1193b4be Poul Sander 2025-02-03 21:28 18980
                overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
1193b4be Poul Sander 2025-02-03 21:28 18981
                if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
1193b4be Poul Sander 2025-02-03 21:28 18982
                    overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines);
1193b4be Poul Sander 2025-02-03 21:28 18983
                if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
1193b4be Poul Sander 2025-02-03 21:28 18984
                    overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines);
1193b4be Poul Sander 2025-02-03 21:28 18985
            }
1193b4be Poul Sander 2025-02-03 21:28 18986
        }
1193b4be Poul Sander 2025-02-03 21:28 18987
1193b4be Poul Sander 2025-02-03 21:28 18988
        // Stop after ImGuiDir_None
1193b4be Poul Sander 2025-02-03 21:28 18989
        if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit)
1193b4be Poul Sander 2025-02-03 21:28 18990
            return;
1193b4be Poul Sander 2025-02-03 21:28 18991
    }
1193b4be Poul Sander 2025-02-03 21:28 18992
}
1193b4be Poul Sander 2025-02-03 21:28 18993
1193b4be Poul Sander 2025-02-03 21:28 18994
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 18995
// Docking: ImGuiDockNode Tree manipulation functions
1193b4be Poul Sander 2025-02-03 21:28 18996
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 18997
// - DockNodeTreeSplit()
1193b4be Poul Sander 2025-02-03 21:28 18998
// - DockNodeTreeMerge()
1193b4be Poul Sander 2025-02-03 21:28 18999
// - DockNodeTreeUpdatePosSize()
1193b4be Poul Sander 2025-02-03 21:28 19000
// - DockNodeTreeUpdateSplitterFindTouchingNode()
1193b4be Poul Sander 2025-02-03 21:28 19001
// - DockNodeTreeUpdateSplitter()
1193b4be Poul Sander 2025-02-03 21:28 19002
// - DockNodeTreeFindFallbackLeafNode()
1193b4be Poul Sander 2025-02-03 21:28 19003
// - DockNodeTreeFindNodeByPos()
1193b4be Poul Sander 2025-02-03 21:28 19004
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19005
1193b4be Poul Sander 2025-02-03 21:28 19006
void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
1193b4be Poul Sander 2025-02-03 21:28 19007
{
1193b4be Poul Sander 2025-02-03 21:28 19008
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19009
    IM_ASSERT(split_axis != ImGuiAxis_None);
1193b4be Poul Sander 2025-02-03 21:28 19010
1193b4be Poul Sander 2025-02-03 21:28 19011
    ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
1193b4be Poul Sander 2025-02-03 21:28 19012
    child_0->ParentNode = parent_node;
1193b4be Poul Sander 2025-02-03 21:28 19013
1193b4be Poul Sander 2025-02-03 21:28 19014
    ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
1193b4be Poul Sander 2025-02-03 21:28 19015
    child_1->ParentNode = parent_node;
1193b4be Poul Sander 2025-02-03 21:28 19016
1193b4be Poul Sander 2025-02-03 21:28 19017
    ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
1193b4be Poul Sander 2025-02-03 21:28 19018
    DockNodeMoveChildNodes(child_inheritor, parent_node);
1193b4be Poul Sander 2025-02-03 21:28 19019
    parent_node->ChildNodes[0] = child_0;
1193b4be Poul Sander 2025-02-03 21:28 19020
    parent_node->ChildNodes[1] = child_1;
1193b4be Poul Sander 2025-02-03 21:28 19021
    parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
1193b4be Poul Sander 2025-02-03 21:28 19022
    parent_node->SplitAxis = split_axis;
1193b4be Poul Sander 2025-02-03 21:28 19023
    parent_node->VisibleWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 19024
    parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 19025
1193b4be Poul Sander 2025-02-03 21:28 19026
    float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize);
1193b4be Poul Sander 2025-02-03 21:28 19027
    size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 19028
    IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
1193b4be Poul Sander 2025-02-03 21:28 19029
    child_0->SizeRef = child_1->SizeRef = parent_node->Size;
1193b4be Poul Sander 2025-02-03 21:28 19030
    child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio);
1193b4be Poul Sander 2025-02-03 21:28 19031
    child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]);
1193b4be Poul Sander 2025-02-03 21:28 19032
1193b4be Poul Sander 2025-02-03 21:28 19033
    DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
1193b4be Poul Sander 2025-02-03 21:28 19034
    DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);
1193b4be Poul Sander 2025-02-03 21:28 19035
    DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node));
1193b4be Poul Sander 2025-02-03 21:28 19036
    DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
1193b4be Poul Sander 2025-02-03 21:28 19037
1193b4be Poul Sander 2025-02-03 21:28 19038
    // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
1193b4be Poul Sander 2025-02-03 21:28 19039
    child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
1193b4be Poul Sander 2025-02-03 21:28 19040
    child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
1193b4be Poul Sander 2025-02-03 21:28 19041
    child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
1193b4be Poul Sander 2025-02-03 21:28 19042
    parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
1193b4be Poul Sander 2025-02-03 21:28 19043
    child_0->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 19044
    child_1->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 19045
    parent_node->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 19046
    if (child_inheritor->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 19047
        DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
1193b4be Poul Sander 2025-02-03 21:28 19048
}
1193b4be Poul Sander 2025-02-03 21:28 19049
1193b4be Poul Sander 2025-02-03 21:28 19050
void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
1193b4be Poul Sander 2025-02-03 21:28 19051
{
1193b4be Poul Sander 2025-02-03 21:28 19052
    // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
1193b4be Poul Sander 2025-02-03 21:28 19053
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19054
    ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
1193b4be Poul Sander 2025-02-03 21:28 19055
    ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
1193b4be Poul Sander 2025-02-03 21:28 19056
    IM_ASSERT(child_0 || child_1);
1193b4be Poul Sander 2025-02-03 21:28 19057
    IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
1193b4be Poul Sander 2025-02-03 21:28 19058
    if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
1193b4be Poul Sander 2025-02-03 21:28 19059
    {
1193b4be Poul Sander 2025-02-03 21:28 19060
        IM_ASSERT(parent_node->TabBar == NULL);
1193b4be Poul Sander 2025-02-03 21:28 19061
        IM_ASSERT(parent_node->Windows.Size == 0);
1193b4be Poul Sander 2025-02-03 21:28 19062
    }
1193b4be Poul Sander 2025-02-03 21:28 19063
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeTreeMerge: 0x%08X + 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19064
1193b4be Poul Sander 2025-02-03 21:28 19065
    ImVec2 backup_last_explicit_size = parent_node->SizeRef;
1193b4be Poul Sander 2025-02-03 21:28 19066
    DockNodeMoveChildNodes(parent_node, merge_lead_child);
1193b4be Poul Sander 2025-02-03 21:28 19067
    if (child_0)
1193b4be Poul Sander 2025-02-03 21:28 19068
    {
1193b4be Poul Sander 2025-02-03 21:28 19069
        DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
1193b4be Poul Sander 2025-02-03 21:28 19070
        DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19071
    }
1193b4be Poul Sander 2025-02-03 21:28 19072
    if (child_1)
1193b4be Poul Sander 2025-02-03 21:28 19073
    {
1193b4be Poul Sander 2025-02-03 21:28 19074
        DockNodeMoveWindows(parent_node, child_1);
1193b4be Poul Sander 2025-02-03 21:28 19075
        DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19076
    }
1193b4be Poul Sander 2025-02-03 21:28 19077
    DockNodeApplyPosSizeToWindows(parent_node);
1193b4be Poul Sander 2025-02-03 21:28 19078
    parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 19079
    parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
1193b4be Poul Sander 2025-02-03 21:28 19080
    parent_node->SizeRef = backup_last_explicit_size;
1193b4be Poul Sander 2025-02-03 21:28 19081
1193b4be Poul Sander 2025-02-03 21:28 19082
    // Flags transfer
1193b4be Poul Sander 2025-02-03 21:28 19083
    parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
1193b4be Poul Sander 2025-02-03 21:28 19084
    parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
1193b4be Poul Sander 2025-02-03 21:28 19085
    parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
1193b4be Poul Sander 2025-02-03 21:28 19086
    parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows
1193b4be Poul Sander 2025-02-03 21:28 19087
    parent_node->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 19088
1193b4be Poul Sander 2025-02-03 21:28 19089
    if (child_0)
1193b4be Poul Sander 2025-02-03 21:28 19090
    {
1193b4be Poul Sander 2025-02-03 21:28 19091
        ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
1193b4be Poul Sander 2025-02-03 21:28 19092
        IM_DELETE(child_0);
1193b4be Poul Sander 2025-02-03 21:28 19093
    }
1193b4be Poul Sander 2025-02-03 21:28 19094
    if (child_1)
1193b4be Poul Sander 2025-02-03 21:28 19095
    {
1193b4be Poul Sander 2025-02-03 21:28 19096
        ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
1193b4be Poul Sander 2025-02-03 21:28 19097
        IM_DELETE(child_1);
1193b4be Poul Sander 2025-02-03 21:28 19098
    }
1193b4be Poul Sander 2025-02-03 21:28 19099
}
1193b4be Poul Sander 2025-02-03 21:28 19100
1193b4be Poul Sander 2025-02-03 21:28 19101
// Update Pos/Size for a node hierarchy (don't affect child Windows yet)
1193b4be Poul Sander 2025-02-03 21:28 19102
// (Depth-first, Pre-Order)
1193b4be Poul Sander 2025-02-03 21:28 19103
void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node)
1193b4be Poul Sander 2025-02-03 21:28 19104
{
1193b4be Poul Sander 2025-02-03 21:28 19105
    // During the regular dock node update we write to all nodes.
1193b4be Poul Sander 2025-02-03 21:28 19106
    // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away.
1193b4be Poul Sander 2025-02-03 21:28 19107
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19108
    const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node;
1193b4be Poul Sander 2025-02-03 21:28 19109
    if (write_to_node)
1193b4be Poul Sander 2025-02-03 21:28 19110
    {
1193b4be Poul Sander 2025-02-03 21:28 19111
        node->Pos = pos;
1193b4be Poul Sander 2025-02-03 21:28 19112
        node->Size = size;
1193b4be Poul Sander 2025-02-03 21:28 19113
    }
1193b4be Poul Sander 2025-02-03 21:28 19114
1193b4be Poul Sander 2025-02-03 21:28 19115
    if (node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 19116
        return;
1193b4be Poul Sander 2025-02-03 21:28 19117
1193b4be Poul Sander 2025-02-03 21:28 19118
    ImGuiDockNode* child_0 = node->ChildNodes[0];
1193b4be Poul Sander 2025-02-03 21:28 19119
    ImGuiDockNode* child_1 = node->ChildNodes[1];
1193b4be Poul Sander 2025-02-03 21:28 19120
    ImVec2 child_0_pos = pos, child_1_pos = pos;
1193b4be Poul Sander 2025-02-03 21:28 19121
    ImVec2 child_0_size = size, child_1_size = size;
1193b4be Poul Sander 2025-02-03 21:28 19122
1193b4be Poul Sander 2025-02-03 21:28 19123
    const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0));
1193b4be Poul Sander 2025-02-03 21:28 19124
    const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1));
1193b4be Poul Sander 2025-02-03 21:28 19125
    const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node;
1193b4be Poul Sander 2025-02-03 21:28 19126
    const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node;
1193b4be Poul Sander 2025-02-03 21:28 19127
1193b4be Poul Sander 2025-02-03 21:28 19128
    if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible)
1193b4be Poul Sander 2025-02-03 21:28 19129
    {
1193b4be Poul Sander 2025-02-03 21:28 19130
        const float spacing = g.Style.DockingSeparatorSize;
1193b4be Poul Sander 2025-02-03 21:28 19131
        const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 19132
        const float size_avail = ImMax(size[axis] - spacing, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19133
1193b4be Poul Sander 2025-02-03 21:28 19134
        // Size allocation policy
1193b4be Poul Sander 2025-02-03 21:28 19135
        // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
1193b4be Poul Sander 2025-02-03 21:28 19136
        const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 19137
1193b4be Poul Sander 2025-02-03 21:28 19138
        // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.
1193b4be Poul Sander 2025-02-03 21:28 19139
        // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc()
1193b4be Poul Sander 2025-02-03 21:28 19140
        // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce
1193b4be Poul Sander 2025-02-03 21:28 19141
1193b4be Poul Sander 2025-02-03 21:28 19142
        // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
1193b4be Poul Sander 2025-02-03 21:28 19143
        if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
1193b4be Poul Sander 2025-02-03 21:28 19144
        {
1193b4be Poul Sander 2025-02-03 21:28 19145
            child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19146
            child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19147
            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19148
        }
1193b4be Poul Sander 2025-02-03 21:28 19149
        else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
1193b4be Poul Sander 2025-02-03 21:28 19150
        {
1193b4be Poul Sander 2025-02-03 21:28 19151
            child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19152
            child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19153
            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19154
        }
1193b4be Poul Sander 2025-02-03 21:28 19155
        else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
1193b4be Poul Sander 2025-02-03 21:28 19156
        {
1193b4be Poul Sander 2025-02-03 21:28 19157
            // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
1193b4be Poul Sander 2025-02-03 21:28 19158
            // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
1193b4be Poul Sander 2025-02-03 21:28 19159
            float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19160
            child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio);
1193b4be Poul Sander 2025-02-03 21:28 19161
            child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19162
            IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19163
        }
1193b4be Poul Sander 2025-02-03 21:28 19164
1193b4be Poul Sander 2025-02-03 21:28 19165
        // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node
1193b4be Poul Sander 2025-02-03 21:28 19166
        else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild)
1193b4be Poul Sander 2025-02-03 21:28 19167
        {
1193b4be Poul Sander 2025-02-03 21:28 19168
            child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19169
            child_1_size[axis] = (size_avail - child_0_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19170
        }
1193b4be Poul Sander 2025-02-03 21:28 19171
        else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild)
1193b4be Poul Sander 2025-02-03 21:28 19172
        {
1193b4be Poul Sander 2025-02-03 21:28 19173
            child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19174
            child_0_size[axis] = (size_avail - child_1_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19175
        }
1193b4be Poul Sander 2025-02-03 21:28 19176
        else
1193b4be Poul Sander 2025-02-03 21:28 19177
        {
1193b4be Poul Sander 2025-02-03 21:28 19178
            // 4) Otherwise distribute according to the relative ratio of each SizeRef value
1193b4be Poul Sander 2025-02-03 21:28 19179
            float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19180
            child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f));
1193b4be Poul Sander 2025-02-03 21:28 19181
            child_1_size[axis] = (size_avail - child_0_size[axis]);
1193b4be Poul Sander 2025-02-03 21:28 19182
        }
1193b4be Poul Sander 2025-02-03 21:28 19183
1193b4be Poul Sander 2025-02-03 21:28 19184
        child_1_pos[axis] += spacing + child_0_size[axis];
1193b4be Poul Sander 2025-02-03 21:28 19185
    }
1193b4be Poul Sander 2025-02-03 21:28 19186
1193b4be Poul Sander 2025-02-03 21:28 19187
    if (only_write_to_single_node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19188
        child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
1193b4be Poul Sander 2025-02-03 21:28 19189
1193b4be Poul Sander 2025-02-03 21:28 19190
    const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible;
1193b4be Poul Sander 2025-02-03 21:28 19191
    const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible;
1193b4be Poul Sander 2025-02-03 21:28 19192
    if (child_0_recurse)
1193b4be Poul Sander 2025-02-03 21:28 19193
        DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
1193b4be Poul Sander 2025-02-03 21:28 19194
    if (child_1_recurse)
1193b4be Poul Sander 2025-02-03 21:28 19195
        DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
1193b4be Poul Sander 2025-02-03 21:28 19196
}
1193b4be Poul Sander 2025-02-03 21:28 19197
1193b4be Poul Sander 2025-02-03 21:28 19198
static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
1193b4be Poul Sander 2025-02-03 21:28 19199
{
1193b4be Poul Sander 2025-02-03 21:28 19200
    if (node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 19201
    {
1193b4be Poul Sander 2025-02-03 21:28 19202
        touching_nodes->push_back(node);
1193b4be Poul Sander 2025-02-03 21:28 19203
        return;
1193b4be Poul Sander 2025-02-03 21:28 19204
    }
1193b4be Poul Sander 2025-02-03 21:28 19205
    if (node->ChildNodes[0]->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19206
        if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19207
            DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
1193b4be Poul Sander 2025-02-03 21:28 19208
    if (node->ChildNodes[1]->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19209
        if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19210
            DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
1193b4be Poul Sander 2025-02-03 21:28 19211
}
1193b4be Poul Sander 2025-02-03 21:28 19212
1193b4be Poul Sander 2025-02-03 21:28 19213
// (Depth-First, Pre-Order)
1193b4be Poul Sander 2025-02-03 21:28 19214
void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 19215
{
1193b4be Poul Sander 2025-02-03 21:28 19216
    if (node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 19217
        return;
1193b4be Poul Sander 2025-02-03 21:28 19218
1193b4be Poul Sander 2025-02-03 21:28 19219
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19220
1193b4be Poul Sander 2025-02-03 21:28 19221
    ImGuiDockNode* child_0 = node->ChildNodes[0];
1193b4be Poul Sander 2025-02-03 21:28 19222
    ImGuiDockNode* child_1 = node->ChildNodes[1];
1193b4be Poul Sander 2025-02-03 21:28 19223
    if (child_0->IsVisible && child_1->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19224
    {
1193b4be Poul Sander 2025-02-03 21:28 19225
        // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
1193b4be Poul Sander 2025-02-03 21:28 19226
        const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 19227
        IM_ASSERT(axis != ImGuiAxis_None);
1193b4be Poul Sander 2025-02-03 21:28 19228
        ImRect bb;
1193b4be Poul Sander 2025-02-03 21:28 19229
        bb.Min = child_0->Pos;
1193b4be Poul Sander 2025-02-03 21:28 19230
        bb.Max = child_1->Pos;
1193b4be Poul Sander 2025-02-03 21:28 19231
        bb.Min[axis] += child_0->Size[axis];
1193b4be Poul Sander 2025-02-03 21:28 19232
        bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
1193b4be Poul Sander 2025-02-03 21:28 19233
        //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
1193b4be Poul Sander 2025-02-03 21:28 19234
1193b4be Poul Sander 2025-02-03 21:28 19235
        const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs
1193b4be Poul Sander 2025-02-03 21:28 19236
        const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
1193b4be Poul Sander 2025-02-03 21:28 19237
        if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
1193b4be Poul Sander 2025-02-03 21:28 19238
        {
1193b4be Poul Sander 2025-02-03 21:28 19239
            ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 19240
            window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
1193b4be Poul Sander 2025-02-03 21:28 19241
        }
1193b4be Poul Sander 2025-02-03 21:28 19242
        else
1193b4be Poul Sander 2025-02-03 21:28 19243
        {
1193b4be Poul Sander 2025-02-03 21:28 19244
            //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
1193b4be Poul Sander 2025-02-03 21:28 19245
            //bb.Max[axis] -= 1;
1193b4be Poul Sander 2025-02-03 21:28 19246
            PushID(node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19247
1193b4be Poul Sander 2025-02-03 21:28 19248
            // Find resizing limits by gathering list of nodes that are touching the splitter line.
1193b4be Poul Sander 2025-02-03 21:28 19249
            ImVector<ImGuiDockNode*> touching_nodes[2];
1193b4be Poul Sander 2025-02-03 21:28 19250
            float min_size = g.Style.WindowMinSize[axis];
1193b4be Poul Sander 2025-02-03 21:28 19251
            float resize_limits[2];
1193b4be Poul Sander 2025-02-03 21:28 19252
            resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
1193b4be Poul Sander 2025-02-03 21:28 19253
            resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
1193b4be Poul Sander 2025-02-03 21:28 19254
1193b4be Poul Sander 2025-02-03 21:28 19255
            ImGuiID splitter_id = GetID("##Splitter");
1193b4be Poul Sander 2025-02-03 21:28 19256
            if (g.ActiveId == splitter_id) // Only process when splitter is active
1193b4be Poul Sander 2025-02-03 21:28 19257
            {
1193b4be Poul Sander 2025-02-03 21:28 19258
                DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
1193b4be Poul Sander 2025-02-03 21:28 19259
                DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
1193b4be Poul Sander 2025-02-03 21:28 19260
                for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
1193b4be Poul Sander 2025-02-03 21:28 19261
                    resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
1193b4be Poul Sander 2025-02-03 21:28 19262
                for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
1193b4be Poul Sander 2025-02-03 21:28 19263
                    resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
1193b4be Poul Sander 2025-02-03 21:28 19264
1193b4be Poul Sander 2025-02-03 21:28 19265
                // [DEBUG] Render touching nodes & limits
1193b4be Poul Sander 2025-02-03 21:28 19266
                /*
1193b4be Poul Sander 2025-02-03 21:28 19267
                ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
1193b4be Poul Sander 2025-02-03 21:28 19268
                for (int n = 0; n < 2; n++)
1193b4be Poul Sander 2025-02-03 21:28 19269
                {
1193b4be Poul Sander 2025-02-03 21:28 19270
                    for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++)
1193b4be Poul Sander 2025-02-03 21:28 19271
                        draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 19272
                    if (axis == ImGuiAxis_X)
1193b4be Poul Sander 2025-02-03 21:28 19273
                        draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f);
1193b4be Poul Sander 2025-02-03 21:28 19274
                    else
1193b4be Poul Sander 2025-02-03 21:28 19275
                        draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f);
1193b4be Poul Sander 2025-02-03 21:28 19276
                }
1193b4be Poul Sander 2025-02-03 21:28 19277
                */
1193b4be Poul Sander 2025-02-03 21:28 19278
            }
1193b4be Poul Sander 2025-02-03 21:28 19279
1193b4be Poul Sander 2025-02-03 21:28 19280
            // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
1193b4be Poul Sander 2025-02-03 21:28 19281
            float cur_size_0 = child_0->Size[axis];
1193b4be Poul Sander 2025-02-03 21:28 19282
            float cur_size_1 = child_1->Size[axis];
1193b4be Poul Sander 2025-02-03 21:28 19283
            float min_size_0 = resize_limits[0] - child_0->Pos[axis];
1193b4be Poul Sander 2025-02-03 21:28 19284
            float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
1193b4be Poul Sander 2025-02-03 21:28 19285
            ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg);
1193b4be Poul Sander 2025-02-03 21:28 19286
            if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col))
1193b4be Poul Sander 2025-02-03 21:28 19287
            {
1193b4be Poul Sander 2025-02-03 21:28 19288
                if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 19289
                {
1193b4be Poul Sander 2025-02-03 21:28 19290
                    child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
1193b4be Poul Sander 2025-02-03 21:28 19291
                    child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
1193b4be Poul Sander 2025-02-03 21:28 19292
                    child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
1193b4be Poul Sander 2025-02-03 21:28 19293
1193b4be Poul Sander 2025-02-03 21:28 19294
                    // Lock the size of every node that is a sibling of the node we are touching
1193b4be Poul Sander 2025-02-03 21:28 19295
                    // This might be less desirable if we can merge sibling of a same axis into the same parental level.
1193b4be Poul Sander 2025-02-03 21:28 19296
                    for (int side_n = 0; side_n < 2; side_n++)
1193b4be Poul Sander 2025-02-03 21:28 19297
                        for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
1193b4be Poul Sander 2025-02-03 21:28 19298
                        {
1193b4be Poul Sander 2025-02-03 21:28 19299
                            ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
1193b4be Poul Sander 2025-02-03 21:28 19300
                            //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
1193b4be Poul Sander 2025-02-03 21:28 19301
                            //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 19302
                            while (touching_node->ParentNode != node)
1193b4be Poul Sander 2025-02-03 21:28 19303
                            {
1193b4be Poul Sander 2025-02-03 21:28 19304
                                if (touching_node->ParentNode->SplitAxis == axis)
1193b4be Poul Sander 2025-02-03 21:28 19305
                                {
1193b4be Poul Sander 2025-02-03 21:28 19306
                                    // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
1193b4be Poul Sander 2025-02-03 21:28 19307
                                    ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
1193b4be Poul Sander 2025-02-03 21:28 19308
                                    node_to_preserve->WantLockSizeOnce = true;
1193b4be Poul Sander 2025-02-03 21:28 19309
                                    //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 19310
                                    //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
1193b4be Poul Sander 2025-02-03 21:28 19311
                                }
1193b4be Poul Sander 2025-02-03 21:28 19312
                                touching_node = touching_node->ParentNode;
1193b4be Poul Sander 2025-02-03 21:28 19313
                            }
1193b4be Poul Sander 2025-02-03 21:28 19314
                        }
1193b4be Poul Sander 2025-02-03 21:28 19315
1193b4be Poul Sander 2025-02-03 21:28 19316
                    DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
1193b4be Poul Sander 2025-02-03 21:28 19317
                    DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
1193b4be Poul Sander 2025-02-03 21:28 19318
                    MarkIniSettingsDirty();
1193b4be Poul Sander 2025-02-03 21:28 19319
                }
1193b4be Poul Sander 2025-02-03 21:28 19320
            }
1193b4be Poul Sander 2025-02-03 21:28 19321
            PopID();
1193b4be Poul Sander 2025-02-03 21:28 19322
        }
1193b4be Poul Sander 2025-02-03 21:28 19323
    }
1193b4be Poul Sander 2025-02-03 21:28 19324
1193b4be Poul Sander 2025-02-03 21:28 19325
    if (child_0->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19326
        DockNodeTreeUpdateSplitter(child_0);
1193b4be Poul Sander 2025-02-03 21:28 19327
    if (child_1->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19328
        DockNodeTreeUpdateSplitter(child_1);
1193b4be Poul Sander 2025-02-03 21:28 19329
}
1193b4be Poul Sander 2025-02-03 21:28 19330
1193b4be Poul Sander 2025-02-03 21:28 19331
ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
1193b4be Poul Sander 2025-02-03 21:28 19332
{
1193b4be Poul Sander 2025-02-03 21:28 19333
    if (node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 19334
        return node;
1193b4be Poul Sander 2025-02-03 21:28 19335
    if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
1193b4be Poul Sander 2025-02-03 21:28 19336
        return leaf_node;
1193b4be Poul Sander 2025-02-03 21:28 19337
    if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
1193b4be Poul Sander 2025-02-03 21:28 19338
        return leaf_node;
1193b4be Poul Sander 2025-02-03 21:28 19339
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 19340
}
1193b4be Poul Sander 2025-02-03 21:28 19341
1193b4be Poul Sander 2025-02-03 21:28 19342
ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
1193b4be Poul Sander 2025-02-03 21:28 19343
{
1193b4be Poul Sander 2025-02-03 21:28 19344
    if (!node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 19345
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 19346
1193b4be Poul Sander 2025-02-03 21:28 19347
    const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
1193b4be Poul Sander 2025-02-03 21:28 19348
    ImRect r(node->Pos, node->Pos + node->Size);
1193b4be Poul Sander 2025-02-03 21:28 19349
    r.Expand(dock_spacing * 0.5f);
1193b4be Poul Sander 2025-02-03 21:28 19350
    bool inside = r.Contains(pos);
1193b4be Poul Sander 2025-02-03 21:28 19351
    if (!inside)
1193b4be Poul Sander 2025-02-03 21:28 19352
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 19353
1193b4be Poul Sander 2025-02-03 21:28 19354
    if (node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 19355
        return node;
1193b4be Poul Sander 2025-02-03 21:28 19356
    if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
1193b4be Poul Sander 2025-02-03 21:28 19357
        return hovered_node;
1193b4be Poul Sander 2025-02-03 21:28 19358
    if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
1193b4be Poul Sander 2025-02-03 21:28 19359
        return hovered_node;
1193b4be Poul Sander 2025-02-03 21:28 19360
1193b4be Poul Sander 2025-02-03 21:28 19361
    // This means we are hovering over the splitter/spacing of a parent node
1193b4be Poul Sander 2025-02-03 21:28 19362
    return node;
1193b4be Poul Sander 2025-02-03 21:28 19363
}
1193b4be Poul Sander 2025-02-03 21:28 19364
1193b4be Poul Sander 2025-02-03 21:28 19365
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19366
// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
1193b4be Poul Sander 2025-02-03 21:28 19367
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19368
// - SetWindowDock() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 19369
// - DockSpace()
1193b4be Poul Sander 2025-02-03 21:28 19370
// - DockSpaceOverViewport()
1193b4be Poul Sander 2025-02-03 21:28 19371
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19372
1193b4be Poul Sander 2025-02-03 21:28 19373
// [Internal] Called via SetNextWindowDockID()
1193b4be Poul Sander 2025-02-03 21:28 19374
void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
1193b4be Poul Sander 2025-02-03 21:28 19375
{
1193b4be Poul Sander 2025-02-03 21:28 19376
    // Test condition (NB: bit 0 is always true) and clear flags for next time
1193b4be Poul Sander 2025-02-03 21:28 19377
    if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
1193b4be Poul Sander 2025-02-03 21:28 19378
        return;
1193b4be Poul Sander 2025-02-03 21:28 19379
    window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
1193b4be Poul Sander 2025-02-03 21:28 19380
1193b4be Poul Sander 2025-02-03 21:28 19381
    if (window->DockId == dock_id)
1193b4be Poul Sander 2025-02-03 21:28 19382
        return;
1193b4be Poul Sander 2025-02-03 21:28 19383
1193b4be Poul Sander 2025-02-03 21:28 19384
    // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
1193b4be Poul Sander 2025-02-03 21:28 19385
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19386
    if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id))
1193b4be Poul Sander 2025-02-03 21:28 19387
        if (new_node->IsSplitNode())
1193b4be Poul Sander 2025-02-03 21:28 19388
        {
1193b4be Poul Sander 2025-02-03 21:28 19389
            // Policy: Find central node or latest focused node. We first move back to our root node.
1193b4be Poul Sander 2025-02-03 21:28 19390
            new_node = DockNodeGetRootNode(new_node);
1193b4be Poul Sander 2025-02-03 21:28 19391
            if (new_node->CentralNode)
1193b4be Poul Sander 2025-02-03 21:28 19392
            {
1193b4be Poul Sander 2025-02-03 21:28 19393
                IM_ASSERT(new_node->CentralNode->IsCentralNode());
1193b4be Poul Sander 2025-02-03 21:28 19394
                dock_id = new_node->CentralNode->ID;
1193b4be Poul Sander 2025-02-03 21:28 19395
            }
1193b4be Poul Sander 2025-02-03 21:28 19396
            else
1193b4be Poul Sander 2025-02-03 21:28 19397
            {
1193b4be Poul Sander 2025-02-03 21:28 19398
                dock_id = new_node->LastFocusedNodeId;
1193b4be Poul Sander 2025-02-03 21:28 19399
            }
1193b4be Poul Sander 2025-02-03 21:28 19400
        }
1193b4be Poul Sander 2025-02-03 21:28 19401
1193b4be Poul Sander 2025-02-03 21:28 19402
    if (window->DockId == dock_id)
1193b4be Poul Sander 2025-02-03 21:28 19403
        return;
1193b4be Poul Sander 2025-02-03 21:28 19404
1193b4be Poul Sander 2025-02-03 21:28 19405
    if (window->DockNode)
1193b4be Poul Sander 2025-02-03 21:28 19406
        DockNodeRemoveWindow(window->DockNode, window, 0);
1193b4be Poul Sander 2025-02-03 21:28 19407
    window->DockId = dock_id;
1193b4be Poul Sander 2025-02-03 21:28 19408
}
1193b4be Poul Sander 2025-02-03 21:28 19409
1193b4be Poul Sander 2025-02-03 21:28 19410
// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
1193b4be Poul Sander 2025-02-03 21:28 19411
// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
1193b4be Poul Sander 2025-02-03 21:28 19412
// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
1193b4be Poul Sander 2025-02-03 21:28 19413
// When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location).
1193b4be Poul Sander 2025-02-03 21:28 19414
ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
1193b4be Poul Sander 2025-02-03 21:28 19415
{
1193b4be Poul Sander 2025-02-03 21:28 19416
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19417
    ImGuiWindow* window = GetCurrentWindowRead();
1193b4be Poul Sander 2025-02-03 21:28 19418
    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
1193b4be Poul Sander 2025-02-03 21:28 19419
        return 0;
1193b4be Poul Sander 2025-02-03 21:28 19420
1193b4be Poul Sander 2025-02-03 21:28 19421
    // Early out if parent window is hidden/collapsed
1193b4be Poul Sander 2025-02-03 21:28 19422
    // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
1193b4be Poul Sander 2025-02-03 21:28 19423
    // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
1193b4be Poul Sander 2025-02-03 21:28 19424
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 19425
        flags |= ImGuiDockNodeFlags_KeepAliveOnly;
1193b4be Poul Sander 2025-02-03 21:28 19426
    if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0)
1193b4be Poul Sander 2025-02-03 21:28 19427
        window = GetCurrentWindow(); // call to set window->WriteAccessed = true;
1193b4be Poul Sander 2025-02-03 21:28 19428
1193b4be Poul Sander 2025-02-03 21:28 19429
    IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
1193b4be Poul Sander 2025-02-03 21:28 19430
    IM_ASSERT(dockspace_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 19431
    ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id);
1193b4be Poul Sander 2025-02-03 21:28 19432
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19433
    {
1193b4be Poul Sander 2025-02-03 21:28 19434
        IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X created\n", dockspace_id);
1193b4be Poul Sander 2025-02-03 21:28 19435
        node = DockContextAddNode(&g, dockspace_id);
1193b4be Poul Sander 2025-02-03 21:28 19436
        node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 19437
    }
1193b4be Poul Sander 2025-02-03 21:28 19438
    if (window_class && window_class->ClassId != node->WindowClass.ClassId)
1193b4be Poul Sander 2025-02-03 21:28 19439
        IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", dockspace_id, node->WindowClass.ClassId, window_class->ClassId);
1193b4be Poul Sander 2025-02-03 21:28 19440
    node->SharedFlags = flags;
1193b4be Poul Sander 2025-02-03 21:28 19441
    node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
1193b4be Poul Sander 2025-02-03 21:28 19442
1193b4be Poul Sander 2025-02-03 21:28 19443
    // When a DockSpace transitioned form implicit to explicit this may be called a second time
1193b4be Poul Sander 2025-02-03 21:28 19444
    // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again.
1193b4be Poul Sander 2025-02-03 21:28 19445
    if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
1193b4be Poul Sander 2025-02-03 21:28 19446
    {
1193b4be Poul Sander 2025-02-03 21:28 19447
        IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
1193b4be Poul Sander 2025-02-03 21:28 19448
        node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
1193b4be Poul Sander 2025-02-03 21:28 19449
        return dockspace_id;
1193b4be Poul Sander 2025-02-03 21:28 19450
    }
1193b4be Poul Sander 2025-02-03 21:28 19451
    node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
1193b4be Poul Sander 2025-02-03 21:28 19452
1193b4be Poul Sander 2025-02-03 21:28 19453
    // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
1193b4be Poul Sander 2025-02-03 21:28 19454
    if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
1193b4be Poul Sander 2025-02-03 21:28 19455
    {
1193b4be Poul Sander 2025-02-03 21:28 19456
        node->LastFrameAlive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 19457
        return dockspace_id;
1193b4be Poul Sander 2025-02-03 21:28 19458
    }
1193b4be Poul Sander 2025-02-03 21:28 19459
1193b4be Poul Sander 2025-02-03 21:28 19460
    const ImVec2 content_avail = GetContentRegionAvail();
1193b4be Poul Sander 2025-02-03 21:28 19461
    ImVec2 size = ImTrunc(size_arg);
1193b4be Poul Sander 2025-02-03 21:28 19462
    if (size.x <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 19463
        size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
1193b4be Poul Sander 2025-02-03 21:28 19464
    if (size.y <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 19465
        size.y = ImMax(content_avail.y + size.y, 4.0f);
1193b4be Poul Sander 2025-02-03 21:28 19466
    IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19467
1193b4be Poul Sander 2025-02-03 21:28 19468
    node->Pos = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 19469
    node->Size = node->SizeRef = size;
1193b4be Poul Sander 2025-02-03 21:28 19470
    SetNextWindowPos(node->Pos);
1193b4be Poul Sander 2025-02-03 21:28 19471
    SetNextWindowSize(node->Size);
1193b4be Poul Sander 2025-02-03 21:28 19472
    g.NextWindowData.PosUndock = false;
1193b4be Poul Sander 2025-02-03 21:28 19473
1193b4be Poul Sander 2025-02-03 21:28 19474
    // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?
1193b4be Poul Sander 2025-02-03 21:28 19475
    // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)
1193b4be Poul Sander 2025-02-03 21:28 19476
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
1193b4be Poul Sander 2025-02-03 21:28 19477
    window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
1193b4be Poul Sander 2025-02-03 21:28 19478
    window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
1193b4be Poul Sander 2025-02-03 21:28 19479
    window_flags |= ImGuiWindowFlags_NoBackground;
1193b4be Poul Sander 2025-02-03 21:28 19480
1193b4be Poul Sander 2025-02-03 21:28 19481
    char title[256];
1193b4be Poul Sander 2025-02-03 21:28 19482
    ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, dockspace_id);
1193b4be Poul Sander 2025-02-03 21:28 19483
1193b4be Poul Sander 2025-02-03 21:28 19484
    PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19485
    Begin(title, NULL, window_flags);
1193b4be Poul Sander 2025-02-03 21:28 19486
    PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 19487
1193b4be Poul Sander 2025-02-03 21:28 19488
    ImGuiWindow* host_window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 19489
    DockNodeSetupHostWindow(node, host_window);
1193b4be Poul Sander 2025-02-03 21:28 19490
    host_window->ChildId = window->GetID(title);
1193b4be Poul Sander 2025-02-03 21:28 19491
    node->OnlyNodeWithWindows = NULL;
1193b4be Poul Sander 2025-02-03 21:28 19492
1193b4be Poul Sander 2025-02-03 21:28 19493
    IM_ASSERT(node->IsRootNode());
1193b4be Poul Sander 2025-02-03 21:28 19494
1193b4be Poul Sander 2025-02-03 21:28 19495
    // We need to handle the rare case were a central node is missing.
1193b4be Poul Sander 2025-02-03 21:28 19496
    // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
1193b4be Poul Sander 2025-02-03 21:28 19497
    // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
1193b4be Poul Sander 2025-02-03 21:28 19498
    // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining.
1193b4be Poul Sander 2025-02-03 21:28 19499
    // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
1193b4be Poul Sander 2025-02-03 21:28 19500
    // as it doesn't make sense for an empty dockspace to not have this property.
1193b4be Poul Sander 2025-02-03 21:28 19501
    if (node->IsLeafNode() && !node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 19502
        node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 19503
1193b4be Poul Sander 2025-02-03 21:28 19504
    // Update the node
1193b4be Poul Sander 2025-02-03 21:28 19505
    DockNodeUpdate(node);
1193b4be Poul Sander 2025-02-03 21:28 19506
1193b4be Poul Sander 2025-02-03 21:28 19507
    End();
1193b4be Poul Sander 2025-02-03 21:28 19508
1193b4be Poul Sander 2025-02-03 21:28 19509
    ImRect bb(node->Pos, node->Pos + size);
1193b4be Poul Sander 2025-02-03 21:28 19510
    ItemSize(size);
1193b4be Poul Sander 2025-02-03 21:28 19511
    ItemAdd(bb, dockspace_id, NULL, ImGuiItemFlags_NoNav); // Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?)
1193b4be Poul Sander 2025-02-03 21:28 19512
    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild()
1193b4be Poul Sander 2025-02-03 21:28 19513
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
1193b4be Poul Sander 2025-02-03 21:28 19514
1193b4be Poul Sander 2025-02-03 21:28 19515
    return dockspace_id;
1193b4be Poul Sander 2025-02-03 21:28 19516
}
1193b4be Poul Sander 2025-02-03 21:28 19517
1193b4be Poul Sander 2025-02-03 21:28 19518
// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
1193b4be Poul Sander 2025-02-03 21:28 19519
// The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar().
1193b4be Poul Sander 2025-02-03 21:28 19520
// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function.
1193b4be Poul Sander 2025-02-03 21:28 19521
// If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it.
1193b4be Poul Sander 2025-02-03 21:28 19522
ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
1193b4be Poul Sander 2025-02-03 21:28 19523
{
1193b4be Poul Sander 2025-02-03 21:28 19524
    if (viewport == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19525
        viewport = GetMainViewport();
1193b4be Poul Sander 2025-02-03 21:28 19526
1193b4be Poul Sander 2025-02-03 21:28 19527
    // Submit a window filling the entire viewport
1193b4be Poul Sander 2025-02-03 21:28 19528
    SetNextWindowPos(viewport->WorkPos);
1193b4be Poul Sander 2025-02-03 21:28 19529
    SetNextWindowSize(viewport->WorkSize);
1193b4be Poul Sander 2025-02-03 21:28 19530
    SetNextWindowViewport(viewport->ID);
1193b4be Poul Sander 2025-02-03 21:28 19531
1193b4be Poul Sander 2025-02-03 21:28 19532
    ImGuiWindowFlags host_window_flags = 0;
1193b4be Poul Sander 2025-02-03 21:28 19533
    host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
1193b4be Poul Sander 2025-02-03 21:28 19534
    host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
1193b4be Poul Sander 2025-02-03 21:28 19535
    if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
1193b4be Poul Sander 2025-02-03 21:28 19536
        host_window_flags |= ImGuiWindowFlags_NoBackground;
1193b4be Poul Sander 2025-02-03 21:28 19537
1193b4be Poul Sander 2025-02-03 21:28 19538
    char label[32];
1193b4be Poul Sander 2025-02-03 21:28 19539
    ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", viewport->ID);
1193b4be Poul Sander 2025-02-03 21:28 19540
1193b4be Poul Sander 2025-02-03 21:28 19541
    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19542
    PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19543
    PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 19544
    Begin(label, NULL, host_window_flags);
1193b4be Poul Sander 2025-02-03 21:28 19545
    PopStyleVar(3);
1193b4be Poul Sander 2025-02-03 21:28 19546
1193b4be Poul Sander 2025-02-03 21:28 19547
    // Submit the dockspace
1193b4be Poul Sander 2025-02-03 21:28 19548
    if (dockspace_id == 0)
1193b4be Poul Sander 2025-02-03 21:28 19549
        dockspace_id = GetID("DockSpace");
1193b4be Poul Sander 2025-02-03 21:28 19550
    DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
1193b4be Poul Sander 2025-02-03 21:28 19551
1193b4be Poul Sander 2025-02-03 21:28 19552
    End();
1193b4be Poul Sander 2025-02-03 21:28 19553
1193b4be Poul Sander 2025-02-03 21:28 19554
    return dockspace_id;
1193b4be Poul Sander 2025-02-03 21:28 19555
}
1193b4be Poul Sander 2025-02-03 21:28 19556
1193b4be Poul Sander 2025-02-03 21:28 19557
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19558
// Docking: Builder Functions
1193b4be Poul Sander 2025-02-03 21:28 19559
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19560
// Very early end-user API to manipulate dock nodes.
1193b4be Poul Sander 2025-02-03 21:28 19561
// Only available in imgui_internal.h. Expect this API to change/break!
1193b4be Poul Sander 2025-02-03 21:28 19562
// It is expected that those functions are all called _before_ the dockspace node submission.
1193b4be Poul Sander 2025-02-03 21:28 19563
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19564
// - DockBuilderDockWindow()
1193b4be Poul Sander 2025-02-03 21:28 19565
// - DockBuilderGetNode()
1193b4be Poul Sander 2025-02-03 21:28 19566
// - DockBuilderSetNodePos()
1193b4be Poul Sander 2025-02-03 21:28 19567
// - DockBuilderSetNodeSize()
1193b4be Poul Sander 2025-02-03 21:28 19568
// - DockBuilderAddNode()
1193b4be Poul Sander 2025-02-03 21:28 19569
// - DockBuilderRemoveNode()
1193b4be Poul Sander 2025-02-03 21:28 19570
// - DockBuilderRemoveNodeChildNodes()
1193b4be Poul Sander 2025-02-03 21:28 19571
// - DockBuilderRemoveNodeDockedWindows()
1193b4be Poul Sander 2025-02-03 21:28 19572
// - DockBuilderSplitNode()
1193b4be Poul Sander 2025-02-03 21:28 19573
// - DockBuilderCopyNodeRec()
1193b4be Poul Sander 2025-02-03 21:28 19574
// - DockBuilderCopyNode()
1193b4be Poul Sander 2025-02-03 21:28 19575
// - DockBuilderCopyWindowSettings()
1193b4be Poul Sander 2025-02-03 21:28 19576
// - DockBuilderCopyDockSpace()
1193b4be Poul Sander 2025-02-03 21:28 19577
// - DockBuilderFinish()
1193b4be Poul Sander 2025-02-03 21:28 19578
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19579
1193b4be Poul Sander 2025-02-03 21:28 19580
void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
1193b4be Poul Sander 2025-02-03 21:28 19581
{
1193b4be Poul Sander 2025-02-03 21:28 19582
    // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
1193b4be Poul Sander 2025-02-03 21:28 19583
    ImGuiContext& g = *GImGui; IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 19584
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19585
    ImGuiID window_id = ImHashStr(window_name);
1193b4be Poul Sander 2025-02-03 21:28 19586
    if (ImGuiWindow* window = FindWindowByID(window_id))
1193b4be Poul Sander 2025-02-03 21:28 19587
    {
1193b4be Poul Sander 2025-02-03 21:28 19588
        // Apply to created window
1193b4be Poul Sander 2025-02-03 21:28 19589
        ImGuiID prev_node_id = window->DockId;
1193b4be Poul Sander 2025-02-03 21:28 19590
        SetWindowDock(window, node_id, ImGuiCond_Always);
1193b4be Poul Sander 2025-02-03 21:28 19591
        if (window->DockId != prev_node_id)
1193b4be Poul Sander 2025-02-03 21:28 19592
            window->DockOrder = -1;
1193b4be Poul Sander 2025-02-03 21:28 19593
    }
1193b4be Poul Sander 2025-02-03 21:28 19594
    else
1193b4be Poul Sander 2025-02-03 21:28 19595
    {
1193b4be Poul Sander 2025-02-03 21:28 19596
        // Apply to settings
1193b4be Poul Sander 2025-02-03 21:28 19597
        ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id);
1193b4be Poul Sander 2025-02-03 21:28 19598
        if (settings == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19599
            settings = CreateNewWindowSettings(window_name);
1193b4be Poul Sander 2025-02-03 21:28 19600
        if (settings->DockId != node_id)
1193b4be Poul Sander 2025-02-03 21:28 19601
            settings->DockOrder = -1;
1193b4be Poul Sander 2025-02-03 21:28 19602
        settings->DockId = node_id;
1193b4be Poul Sander 2025-02-03 21:28 19603
    }
1193b4be Poul Sander 2025-02-03 21:28 19604
}
1193b4be Poul Sander 2025-02-03 21:28 19605
1193b4be Poul Sander 2025-02-03 21:28 19606
ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
1193b4be Poul Sander 2025-02-03 21:28 19607
{
1193b4be Poul Sander 2025-02-03 21:28 19608
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19609
    return DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19610
}
1193b4be Poul Sander 2025-02-03 21:28 19611
1193b4be Poul Sander 2025-02-03 21:28 19612
void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
1193b4be Poul Sander 2025-02-03 21:28 19613
{
1193b4be Poul Sander 2025-02-03 21:28 19614
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19615
    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19616
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19617
        return;
1193b4be Poul Sander 2025-02-03 21:28 19618
    node->Pos = pos;
1193b4be Poul Sander 2025-02-03 21:28 19619
    node->AuthorityForPos = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 19620
}
1193b4be Poul Sander 2025-02-03 21:28 19621
1193b4be Poul Sander 2025-02-03 21:28 19622
void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
1193b4be Poul Sander 2025-02-03 21:28 19623
{
1193b4be Poul Sander 2025-02-03 21:28 19624
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19625
    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19626
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19627
        return;
1193b4be Poul Sander 2025-02-03 21:28 19628
    IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 19629
    node->Size = node->SizeRef = size;
1193b4be Poul Sander 2025-02-03 21:28 19630
    node->AuthorityForSize = ImGuiDataAuthority_DockNode;
1193b4be Poul Sander 2025-02-03 21:28 19631
}
1193b4be Poul Sander 2025-02-03 21:28 19632
1193b4be Poul Sander 2025-02-03 21:28 19633
// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
1193b4be Poul Sander 2025-02-03 21:28 19634
// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
1193b4be Poul Sander 2025-02-03 21:28 19635
// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
1193b4be Poul Sander 2025-02-03 21:28 19636
// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
1193b4be Poul Sander 2025-02-03 21:28 19637
//   For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
1193b4be Poul Sander 2025-02-03 21:28 19638
// - Use (id == 0) to let the system allocate a node identifier.
1193b4be Poul Sander 2025-02-03 21:28 19639
// - Existing node with a same id will be removed.
1193b4be Poul Sander 2025-02-03 21:28 19640
ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 19641
{
1193b4be Poul Sander 2025-02-03 21:28 19642
    ImGuiContext& g = *GImGui; IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 19643
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags);
1193b4be Poul Sander 2025-02-03 21:28 19644
1193b4be Poul Sander 2025-02-03 21:28 19645
    if (node_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 19646
        DockBuilderRemoveNode(node_id);
1193b4be Poul Sander 2025-02-03 21:28 19647
1193b4be Poul Sander 2025-02-03 21:28 19648
    ImGuiDockNode* node = NULL;
1193b4be Poul Sander 2025-02-03 21:28 19649
    if (flags & ImGuiDockNodeFlags_DockSpace)
1193b4be Poul Sander 2025-02-03 21:28 19650
    {
1193b4be Poul Sander 2025-02-03 21:28 19651
        DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
1193b4be Poul Sander 2025-02-03 21:28 19652
        node = DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19653
    }
1193b4be Poul Sander 2025-02-03 21:28 19654
    else
1193b4be Poul Sander 2025-02-03 21:28 19655
    {
1193b4be Poul Sander 2025-02-03 21:28 19656
        node = DockContextAddNode(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19657
        node->SetLocalFlags(flags);
1193b4be Poul Sander 2025-02-03 21:28 19658
    }
1193b4be Poul Sander 2025-02-03 21:28 19659
    node->LastFrameAlive = g.FrameCount;   // Set this otherwise BeginDocked will undock during the same frame.
1193b4be Poul Sander 2025-02-03 21:28 19660
    return node->ID;
1193b4be Poul Sander 2025-02-03 21:28 19661
}
1193b4be Poul Sander 2025-02-03 21:28 19662
1193b4be Poul Sander 2025-02-03 21:28 19663
void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
1193b4be Poul Sander 2025-02-03 21:28 19664
{
1193b4be Poul Sander 2025-02-03 21:28 19665
    ImGuiContext& g = *GImGui; IM_UNUSED(g);
1193b4be Poul Sander 2025-02-03 21:28 19666
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id);
1193b4be Poul Sander 2025-02-03 21:28 19667
1193b4be Poul Sander 2025-02-03 21:28 19668
    ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19669
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19670
        return;
1193b4be Poul Sander 2025-02-03 21:28 19671
    DockBuilderRemoveNodeDockedWindows(node_id, true);
1193b4be Poul Sander 2025-02-03 21:28 19672
    DockBuilderRemoveNodeChildNodes(node_id);
1193b4be Poul Sander 2025-02-03 21:28 19673
    // Node may have moved or deleted if e.g. any merge happened
1193b4be Poul Sander 2025-02-03 21:28 19674
    node = DockContextFindNodeByID(&g, node_id);
1193b4be Poul Sander 2025-02-03 21:28 19675
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19676
        return;
1193b4be Poul Sander 2025-02-03 21:28 19677
    if (node->IsCentralNode() && node->ParentNode)
1193b4be Poul Sander 2025-02-03 21:28 19678
        node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 19679
    DockContextRemoveNode(&g, node, true);
1193b4be Poul Sander 2025-02-03 21:28 19680
}
1193b4be Poul Sander 2025-02-03 21:28 19681
1193b4be Poul Sander 2025-02-03 21:28 19682
// root_id = 0 to remove all, root_id != 0 to remove child of given node.
1193b4be Poul Sander 2025-02-03 21:28 19683
void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
1193b4be Poul Sander 2025-02-03 21:28 19684
{
1193b4be Poul Sander 2025-02-03 21:28 19685
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19686
    ImGuiDockContext* dc = &g.DockContext;
1193b4be Poul Sander 2025-02-03 21:28 19687
1193b4be Poul Sander 2025-02-03 21:28 19688
    ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 19689
    if (root_id && root_node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19690
        return;
1193b4be Poul Sander 2025-02-03 21:28 19691
    bool has_central_node = false;
1193b4be Poul Sander 2025-02-03 21:28 19692
1193b4be Poul Sander 2025-02-03 21:28 19693
    ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 19694
    ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
1193b4be Poul Sander 2025-02-03 21:28 19695
1193b4be Poul Sander 2025-02-03 21:28 19696
    // Process active windows
1193b4be Poul Sander 2025-02-03 21:28 19697
    ImVector<ImGuiDockNode*> nodes_to_remove;
1193b4be Poul Sander 2025-02-03 21:28 19698
    for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 19699
        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 19700
        {
1193b4be Poul Sander 2025-02-03 21:28 19701
            bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
1193b4be Poul Sander 2025-02-03 21:28 19702
            if (want_removal)
1193b4be Poul Sander 2025-02-03 21:28 19703
            {
1193b4be Poul Sander 2025-02-03 21:28 19704
                if (node->IsCentralNode())
1193b4be Poul Sander 2025-02-03 21:28 19705
                    has_central_node = true;
1193b4be Poul Sander 2025-02-03 21:28 19706
                if (root_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 19707
                    DockContextQueueNotifyRemovedNode(&g, node);
1193b4be Poul Sander 2025-02-03 21:28 19708
                if (root_node)
1193b4be Poul Sander 2025-02-03 21:28 19709
                {
1193b4be Poul Sander 2025-02-03 21:28 19710
                    DockNodeMoveWindows(root_node, node);
1193b4be Poul Sander 2025-02-03 21:28 19711
                    DockSettingsRenameNodeReferences(node->ID, root_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19712
                }
1193b4be Poul Sander 2025-02-03 21:28 19713
                nodes_to_remove.push_back(node);
1193b4be Poul Sander 2025-02-03 21:28 19714
            }
1193b4be Poul Sander 2025-02-03 21:28 19715
        }
1193b4be Poul Sander 2025-02-03 21:28 19716
1193b4be Poul Sander 2025-02-03 21:28 19717
    // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
1193b4be Poul Sander 2025-02-03 21:28 19718
    // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
1193b4be Poul Sander 2025-02-03 21:28 19719
    if (root_node)
1193b4be Poul Sander 2025-02-03 21:28 19720
    {
1193b4be Poul Sander 2025-02-03 21:28 19721
        root_node->AuthorityForPos = backup_root_node_authority_for_pos;
1193b4be Poul Sander 2025-02-03 21:28 19722
        root_node->AuthorityForSize = backup_root_node_authority_for_size;
1193b4be Poul Sander 2025-02-03 21:28 19723
    }
1193b4be Poul Sander 2025-02-03 21:28 19724
1193b4be Poul Sander 2025-02-03 21:28 19725
    // Apply to settings
1193b4be Poul Sander 2025-02-03 21:28 19726
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 19727
        if (ImGuiID window_settings_dock_id = settings->DockId)
1193b4be Poul Sander 2025-02-03 21:28 19728
            for (int n = 0; n < nodes_to_remove.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 19729
                if (nodes_to_remove[n]->ID == window_settings_dock_id)
1193b4be Poul Sander 2025-02-03 21:28 19730
                {
1193b4be Poul Sander 2025-02-03 21:28 19731
                    settings->DockId = root_id;
1193b4be Poul Sander 2025-02-03 21:28 19732
                    break;
1193b4be Poul Sander 2025-02-03 21:28 19733
                }
1193b4be Poul Sander 2025-02-03 21:28 19734
1193b4be Poul Sander 2025-02-03 21:28 19735
    // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
1193b4be Poul Sander 2025-02-03 21:28 19736
    if (nodes_to_remove.Size > 1)
1193b4be Poul Sander 2025-02-03 21:28 19737
        ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
1193b4be Poul Sander 2025-02-03 21:28 19738
    for (int n = 0; n < nodes_to_remove.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 19739
        DockContextRemoveNode(&g, nodes_to_remove[n], false);
1193b4be Poul Sander 2025-02-03 21:28 19740
1193b4be Poul Sander 2025-02-03 21:28 19741
    if (root_id == 0)
1193b4be Poul Sander 2025-02-03 21:28 19742
    {
1193b4be Poul Sander 2025-02-03 21:28 19743
        dc->Nodes.Clear();
1193b4be Poul Sander 2025-02-03 21:28 19744
        dc->Requests.clear();
1193b4be Poul Sander 2025-02-03 21:28 19745
    }
1193b4be Poul Sander 2025-02-03 21:28 19746
    else if (has_central_node)
1193b4be Poul Sander 2025-02-03 21:28 19747
    {
1193b4be Poul Sander 2025-02-03 21:28 19748
        root_node->CentralNode = root_node;
1193b4be Poul Sander 2025-02-03 21:28 19749
        root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
1193b4be Poul Sander 2025-02-03 21:28 19750
    }
1193b4be Poul Sander 2025-02-03 21:28 19751
}
1193b4be Poul Sander 2025-02-03 21:28 19752
1193b4be Poul Sander 2025-02-03 21:28 19753
void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
1193b4be Poul Sander 2025-02-03 21:28 19754
{
1193b4be Poul Sander 2025-02-03 21:28 19755
    // Clear references in settings
1193b4be Poul Sander 2025-02-03 21:28 19756
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19757
    if (clear_settings_refs)
1193b4be Poul Sander 2025-02-03 21:28 19758
    {
1193b4be Poul Sander 2025-02-03 21:28 19759
        for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 19760
        {
1193b4be Poul Sander 2025-02-03 21:28 19761
            bool want_removal = (root_id == 0) || (settings->DockId == root_id);
1193b4be Poul Sander 2025-02-03 21:28 19762
            if (!want_removal && settings->DockId != 0)
1193b4be Poul Sander 2025-02-03 21:28 19763
                if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId))
1193b4be Poul Sander 2025-02-03 21:28 19764
                    if (DockNodeGetRootNode(node)->ID == root_id)
1193b4be Poul Sander 2025-02-03 21:28 19765
                        want_removal = true;
1193b4be Poul Sander 2025-02-03 21:28 19766
            if (want_removal)
1193b4be Poul Sander 2025-02-03 21:28 19767
                settings->DockId = 0;
1193b4be Poul Sander 2025-02-03 21:28 19768
        }
1193b4be Poul Sander 2025-02-03 21:28 19769
    }
1193b4be Poul Sander 2025-02-03 21:28 19770
1193b4be Poul Sander 2025-02-03 21:28 19771
    // Clear references in windows
1193b4be Poul Sander 2025-02-03 21:28 19772
    for (int n = 0; n < g.Windows.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 19773
    {
1193b4be Poul Sander 2025-02-03 21:28 19774
        ImGuiWindow* window = g.Windows[n];
1193b4be Poul Sander 2025-02-03 21:28 19775
        bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
1193b4be Poul Sander 2025-02-03 21:28 19776
        if (want_removal)
1193b4be Poul Sander 2025-02-03 21:28 19777
        {
1193b4be Poul Sander 2025-02-03 21:28 19778
            const ImGuiID backup_dock_id = window->DockId;
1193b4be Poul Sander 2025-02-03 21:28 19779
            IM_UNUSED(backup_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19780
            DockContextProcessUndockWindow(&g, window, clear_settings_refs);
1193b4be Poul Sander 2025-02-03 21:28 19781
            if (!clear_settings_refs)
1193b4be Poul Sander 2025-02-03 21:28 19782
                IM_ASSERT(window->DockId == backup_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19783
        }
1193b4be Poul Sander 2025-02-03 21:28 19784
    }
1193b4be Poul Sander 2025-02-03 21:28 19785
}
1193b4be Poul Sander 2025-02-03 21:28 19786
1193b4be Poul Sander 2025-02-03 21:28 19787
// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created.
1193b4be Poul Sander 2025-02-03 21:28 19788
// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
1193b4be Poul Sander 2025-02-03 21:28 19789
// FIXME-DOCK: We are not exposing nor using split_outer.
1193b4be Poul Sander 2025-02-03 21:28 19790
ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir)
1193b4be Poul Sander 2025-02-03 21:28 19791
{
1193b4be Poul Sander 2025-02-03 21:28 19792
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19793
    IM_ASSERT(split_dir != ImGuiDir_None);
1193b4be Poul Sander 2025-02-03 21:28 19794
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\n", id, split_dir);
1193b4be Poul Sander 2025-02-03 21:28 19795
1193b4be Poul Sander 2025-02-03 21:28 19796
    ImGuiDockNode* node = DockContextFindNodeByID(&g, id);
1193b4be Poul Sander 2025-02-03 21:28 19797
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19798
    {
1193b4be Poul Sander 2025-02-03 21:28 19799
        IM_ASSERT(node != NULL);
1193b4be Poul Sander 2025-02-03 21:28 19800
        return 0;
1193b4be Poul Sander 2025-02-03 21:28 19801
    }
1193b4be Poul Sander 2025-02-03 21:28 19802
1193b4be Poul Sander 2025-02-03 21:28 19803
    IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
1193b4be Poul Sander 2025-02-03 21:28 19804
1193b4be Poul Sander 2025-02-03 21:28 19805
    ImGuiDockRequest req;
1193b4be Poul Sander 2025-02-03 21:28 19806
    req.Type = ImGuiDockRequestType_Split;
1193b4be Poul Sander 2025-02-03 21:28 19807
    req.DockTargetWindow = NULL;
1193b4be Poul Sander 2025-02-03 21:28 19808
    req.DockTargetNode = node;
1193b4be Poul Sander 2025-02-03 21:28 19809
    req.DockPayload = NULL;
1193b4be Poul Sander 2025-02-03 21:28 19810
    req.DockSplitDir = split_dir;
1193b4be Poul Sander 2025-02-03 21:28 19811
    req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir);
1193b4be Poul Sander 2025-02-03 21:28 19812
    req.DockSplitOuter = false;
1193b4be Poul Sander 2025-02-03 21:28 19813
    DockContextProcessDock(&g, &req);
1193b4be Poul Sander 2025-02-03 21:28 19814
1193b4be Poul Sander 2025-02-03 21:28 19815
    ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
1193b4be Poul Sander 2025-02-03 21:28 19816
    ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
1193b4be Poul Sander 2025-02-03 21:28 19817
    if (out_id_at_dir)
1193b4be Poul Sander 2025-02-03 21:28 19818
        *out_id_at_dir = id_at_dir;
1193b4be Poul Sander 2025-02-03 21:28 19819
    if (out_id_at_opposite_dir)
1193b4be Poul Sander 2025-02-03 21:28 19820
        *out_id_at_opposite_dir = id_at_opposite_dir;
1193b4be Poul Sander 2025-02-03 21:28 19821
    return id_at_dir;
1193b4be Poul Sander 2025-02-03 21:28 19822
}
1193b4be Poul Sander 2025-02-03 21:28 19823
1193b4be Poul Sander 2025-02-03 21:28 19824
static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
1193b4be Poul Sander 2025-02-03 21:28 19825
{
1193b4be Poul Sander 2025-02-03 21:28 19826
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19827
    ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known);
1193b4be Poul Sander 2025-02-03 21:28 19828
    dst_node->SharedFlags = src_node->SharedFlags;
1193b4be Poul Sander 2025-02-03 21:28 19829
    dst_node->LocalFlags = src_node->LocalFlags;
1193b4be Poul Sander 2025-02-03 21:28 19830
    dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 19831
    dst_node->Pos = src_node->Pos;
1193b4be Poul Sander 2025-02-03 21:28 19832
    dst_node->Size = src_node->Size;
1193b4be Poul Sander 2025-02-03 21:28 19833
    dst_node->SizeRef = src_node->SizeRef;
1193b4be Poul Sander 2025-02-03 21:28 19834
    dst_node->SplitAxis = src_node->SplitAxis;
1193b4be Poul Sander 2025-02-03 21:28 19835
    dst_node->UpdateMergedFlags();
1193b4be Poul Sander 2025-02-03 21:28 19836
1193b4be Poul Sander 2025-02-03 21:28 19837
    out_node_remap_pairs->push_back(src_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19838
    out_node_remap_pairs->push_back(dst_node->ID);
1193b4be Poul Sander 2025-02-03 21:28 19839
1193b4be Poul Sander 2025-02-03 21:28 19840
    for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
1193b4be Poul Sander 2025-02-03 21:28 19841
        if (src_node->ChildNodes[child_n])
1193b4be Poul Sander 2025-02-03 21:28 19842
        {
1193b4be Poul Sander 2025-02-03 21:28 19843
            dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
1193b4be Poul Sander 2025-02-03 21:28 19844
            dst_node->ChildNodes[child_n]->ParentNode = dst_node;
1193b4be Poul Sander 2025-02-03 21:28 19845
        }
1193b4be Poul Sander 2025-02-03 21:28 19846
1193b4be Poul Sander 2025-02-03 21:28 19847
    IMGUI_DEBUG_LOG_DOCKING("[docking] Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
1193b4be Poul Sander 2025-02-03 21:28 19848
    return dst_node;
1193b4be Poul Sander 2025-02-03 21:28 19849
}
1193b4be Poul Sander 2025-02-03 21:28 19850
1193b4be Poul Sander 2025-02-03 21:28 19851
void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
1193b4be Poul Sander 2025-02-03 21:28 19852
{
1193b4be Poul Sander 2025-02-03 21:28 19853
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19854
    IM_ASSERT(src_node_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 19855
    IM_ASSERT(dst_node_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 19856
    IM_ASSERT(out_node_remap_pairs != NULL);
1193b4be Poul Sander 2025-02-03 21:28 19857
1193b4be Poul Sander 2025-02-03 21:28 19858
    DockBuilderRemoveNode(dst_node_id);
1193b4be Poul Sander 2025-02-03 21:28 19859
1193b4be Poul Sander 2025-02-03 21:28 19860
    ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id);
1193b4be Poul Sander 2025-02-03 21:28 19861
    IM_ASSERT(src_node != NULL);
1193b4be Poul Sander 2025-02-03 21:28 19862
1193b4be Poul Sander 2025-02-03 21:28 19863
    out_node_remap_pairs->clear();
1193b4be Poul Sander 2025-02-03 21:28 19864
    DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
1193b4be Poul Sander 2025-02-03 21:28 19865
1193b4be Poul Sander 2025-02-03 21:28 19866
    IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
1193b4be Poul Sander 2025-02-03 21:28 19867
}
1193b4be Poul Sander 2025-02-03 21:28 19868
1193b4be Poul Sander 2025-02-03 21:28 19869
void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
1193b4be Poul Sander 2025-02-03 21:28 19870
{
1193b4be Poul Sander 2025-02-03 21:28 19871
    ImGuiWindow* src_window = FindWindowByName(src_name);
1193b4be Poul Sander 2025-02-03 21:28 19872
    if (src_window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 19873
        return;
1193b4be Poul Sander 2025-02-03 21:28 19874
    if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
1193b4be Poul Sander 2025-02-03 21:28 19875
    {
1193b4be Poul Sander 2025-02-03 21:28 19876
        dst_window->Pos = src_window->Pos;
1193b4be Poul Sander 2025-02-03 21:28 19877
        dst_window->Size = src_window->Size;
1193b4be Poul Sander 2025-02-03 21:28 19878
        dst_window->SizeFull = src_window->SizeFull;
1193b4be Poul Sander 2025-02-03 21:28 19879
        dst_window->Collapsed = src_window->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 19880
    }
1193b4be Poul Sander 2025-02-03 21:28 19881
    else
1193b4be Poul Sander 2025-02-03 21:28 19882
    {
1193b4be Poul Sander 2025-02-03 21:28 19883
        ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name));
1193b4be Poul Sander 2025-02-03 21:28 19884
        if (!dst_settings)
1193b4be Poul Sander 2025-02-03 21:28 19885
            dst_settings = CreateNewWindowSettings(dst_name);
1193b4be Poul Sander 2025-02-03 21:28 19886
        ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
1193b4be Poul Sander 2025-02-03 21:28 19887
        if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
1193b4be Poul Sander 2025-02-03 21:28 19888
        {
1193b4be Poul Sander 2025-02-03 21:28 19889
            dst_settings->ViewportPos = window_pos_2ih;
1193b4be Poul Sander 2025-02-03 21:28 19890
            dst_settings->ViewportId = src_window->ViewportId;
1193b4be Poul Sander 2025-02-03 21:28 19891
            dst_settings->Pos = ImVec2ih(0, 0);
1193b4be Poul Sander 2025-02-03 21:28 19892
        }
1193b4be Poul Sander 2025-02-03 21:28 19893
        else
1193b4be Poul Sander 2025-02-03 21:28 19894
        {
1193b4be Poul Sander 2025-02-03 21:28 19895
            dst_settings->Pos = window_pos_2ih;
1193b4be Poul Sander 2025-02-03 21:28 19896
        }
1193b4be Poul Sander 2025-02-03 21:28 19897
        dst_settings->Size = ImVec2ih(src_window->SizeFull);
1193b4be Poul Sander 2025-02-03 21:28 19898
        dst_settings->Collapsed = src_window->Collapsed;
1193b4be Poul Sander 2025-02-03 21:28 19899
    }
1193b4be Poul Sander 2025-02-03 21:28 19900
}
1193b4be Poul Sander 2025-02-03 21:28 19901
1193b4be Poul Sander 2025-02-03 21:28 19902
// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.
1193b4be Poul Sander 2025-02-03 21:28 19903
void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
1193b4be Poul Sander 2025-02-03 21:28 19904
{
1193b4be Poul Sander 2025-02-03 21:28 19905
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19906
    IM_ASSERT(src_dockspace_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 19907
    IM_ASSERT(dst_dockspace_id != 0);
1193b4be Poul Sander 2025-02-03 21:28 19908
    IM_ASSERT(in_window_remap_pairs != NULL);
1193b4be Poul Sander 2025-02-03 21:28 19909
    IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
1193b4be Poul Sander 2025-02-03 21:28 19910
1193b4be Poul Sander 2025-02-03 21:28 19911
    // Duplicate entire dock
1193b4be Poul Sander 2025-02-03 21:28 19912
    // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart,
1193b4be Poul Sander 2025-02-03 21:28 19913
    // whereas we could attempt to at least keep them together in a new, same floating node.
1193b4be Poul Sander 2025-02-03 21:28 19914
    ImVector<ImGuiID> node_remap_pairs;
1193b4be Poul Sander 2025-02-03 21:28 19915
    DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
1193b4be Poul Sander 2025-02-03 21:28 19916
1193b4be Poul Sander 2025-02-03 21:28 19917
    // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
1193b4be Poul Sander 2025-02-03 21:28 19918
    // (The windows associated to src_dockspace_id are staying in place)
1193b4be Poul Sander 2025-02-03 21:28 19919
    ImVector<ImGuiID> src_windows;
1193b4be Poul Sander 2025-02-03 21:28 19920
    for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
1193b4be Poul Sander 2025-02-03 21:28 19921
    {
1193b4be Poul Sander 2025-02-03 21:28 19922
        const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
1193b4be Poul Sander 2025-02-03 21:28 19923
        const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
1193b4be Poul Sander 2025-02-03 21:28 19924
        ImGuiID src_window_id = ImHashStr(src_window_name);
1193b4be Poul Sander 2025-02-03 21:28 19925
        src_windows.push_back(src_window_id);
1193b4be Poul Sander 2025-02-03 21:28 19926
1193b4be Poul Sander 2025-02-03 21:28 19927
        // Search in the remapping tables
1193b4be Poul Sander 2025-02-03 21:28 19928
        ImGuiID src_dock_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 19929
        if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
1193b4be Poul Sander 2025-02-03 21:28 19930
            src_dock_id = src_window->DockId;
1193b4be Poul Sander 2025-02-03 21:28 19931
        else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id))
1193b4be Poul Sander 2025-02-03 21:28 19932
            src_dock_id = src_window_settings->DockId;
1193b4be Poul Sander 2025-02-03 21:28 19933
        ImGuiID dst_dock_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 19934
        for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
1193b4be Poul Sander 2025-02-03 21:28 19935
            if (node_remap_pairs[dock_remap_n] == src_dock_id)
1193b4be Poul Sander 2025-02-03 21:28 19936
            {
1193b4be Poul Sander 2025-02-03 21:28 19937
                dst_dock_id = node_remap_pairs[dock_remap_n + 1];
1193b4be Poul Sander 2025-02-03 21:28 19938
                //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
1193b4be Poul Sander 2025-02-03 21:28 19939
                break;
1193b4be Poul Sander 2025-02-03 21:28 19940
            }
1193b4be Poul Sander 2025-02-03 21:28 19941
1193b4be Poul Sander 2025-02-03 21:28 19942
        if (dst_dock_id != 0)
1193b4be Poul Sander 2025-02-03 21:28 19943
        {
1193b4be Poul Sander 2025-02-03 21:28 19944
            // Docked windows gets redocked into the new node hierarchy.
1193b4be Poul Sander 2025-02-03 21:28 19945
            IMGUI_DEBUG_LOG_DOCKING("[docking] Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19946
            DockBuilderDockWindow(dst_window_name, dst_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19947
        }
1193b4be Poul Sander 2025-02-03 21:28 19948
        else
1193b4be Poul Sander 2025-02-03 21:28 19949
        {
1193b4be Poul Sander 2025-02-03 21:28 19950
            // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
1193b4be Poul Sander 2025-02-03 21:28 19951
            // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
1193b4be Poul Sander 2025-02-03 21:28 19952
            IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
1193b4be Poul Sander 2025-02-03 21:28 19953
            DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
1193b4be Poul Sander 2025-02-03 21:28 19954
        }
1193b4be Poul Sander 2025-02-03 21:28 19955
    }
1193b4be Poul Sander 2025-02-03 21:28 19956
1193b4be Poul Sander 2025-02-03 21:28 19957
    // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list.
1193b4be Poul Sander 2025-02-03 21:28 19958
    // Find those windows and move to them to the cloned dock node. This may be optional?
1193b4be Poul Sander 2025-02-03 21:28 19959
    // Dock those are a second step as undocking would invalidate source dock nodes.
1193b4be Poul Sander 2025-02-03 21:28 19960
    struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } };
1193b4be Poul Sander 2025-02-03 21:28 19961
    ImVector<DockRemainingWindowTask> dock_remaining_windows;
1193b4be Poul Sander 2025-02-03 21:28 19962
    for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
1193b4be Poul Sander 2025-02-03 21:28 19963
        if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
1193b4be Poul Sander 2025-02-03 21:28 19964
        {
1193b4be Poul Sander 2025-02-03 21:28 19965
            ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
1193b4be Poul Sander 2025-02-03 21:28 19966
            ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19967
            for (int window_n = 0; window_n < node->Windows.Size; window_n++)
1193b4be Poul Sander 2025-02-03 21:28 19968
            {
1193b4be Poul Sander 2025-02-03 21:28 19969
                ImGuiWindow* window = node->Windows[window_n];
1193b4be Poul Sander 2025-02-03 21:28 19970
                if (src_windows.contains(window->ID))
1193b4be Poul Sander 2025-02-03 21:28 19971
                    continue;
1193b4be Poul Sander 2025-02-03 21:28 19972
1193b4be Poul Sander 2025-02-03 21:28 19973
                // Docked windows gets redocked into the new node hierarchy.
1193b4be Poul Sander 2025-02-03 21:28 19974
                IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
1193b4be Poul Sander 2025-02-03 21:28 19975
                dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id));
1193b4be Poul Sander 2025-02-03 21:28 19976
            }
1193b4be Poul Sander 2025-02-03 21:28 19977
        }
1193b4be Poul Sander 2025-02-03 21:28 19978
    for (const DockRemainingWindowTask& task : dock_remaining_windows)
1193b4be Poul Sander 2025-02-03 21:28 19979
        DockBuilderDockWindow(task.Window->Name, task.DockId);
1193b4be Poul Sander 2025-02-03 21:28 19980
}
1193b4be Poul Sander 2025-02-03 21:28 19981
1193b4be Poul Sander 2025-02-03 21:28 19982
// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.
1193b4be Poul Sander 2025-02-03 21:28 19983
void ImGui::DockBuilderFinish(ImGuiID root_id)
1193b4be Poul Sander 2025-02-03 21:28 19984
{
1193b4be Poul Sander 2025-02-03 21:28 19985
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 19986
    //DockContextRebuild(&g);
1193b4be Poul Sander 2025-02-03 21:28 19987
    DockContextBuildAddWindowsToNodes(&g, root_id);
1193b4be Poul Sander 2025-02-03 21:28 19988
}
1193b4be Poul Sander 2025-02-03 21:28 19989
1193b4be Poul Sander 2025-02-03 21:28 19990
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19991
// Docking: Begin/End Support Functions (called from Begin/End)
1193b4be Poul Sander 2025-02-03 21:28 19992
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19993
// - GetWindowAlwaysWantOwnTabBar()
1193b4be Poul Sander 2025-02-03 21:28 19994
// - DockContextBindNodeToWindow()
1193b4be Poul Sander 2025-02-03 21:28 19995
// - BeginDocked()
1193b4be Poul Sander 2025-02-03 21:28 19996
// - BeginDockableDragDropSource()
1193b4be Poul Sander 2025-02-03 21:28 19997
// - BeginDockableDragDropTarget()
1193b4be Poul Sander 2025-02-03 21:28 19998
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 19999
1193b4be Poul Sander 2025-02-03 21:28 20000
bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 20001
{
1193b4be Poul Sander 2025-02-03 21:28 20002
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20003
    if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
1193b4be Poul Sander 2025-02-03 21:28 20004
        if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
1193b4be Poul Sander 2025-02-03 21:28 20005
            if (!window->IsFallbackWindow)    // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
1193b4be Poul Sander 2025-02-03 21:28 20006
                return true;
1193b4be Poul Sander 2025-02-03 21:28 20007
    return false;
1193b4be Poul Sander 2025-02-03 21:28 20008
}
1193b4be Poul Sander 2025-02-03 21:28 20009
1193b4be Poul Sander 2025-02-03 21:28 20010
static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 20011
{
1193b4be Poul Sander 2025-02-03 21:28 20012
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20013
    ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
1193b4be Poul Sander 2025-02-03 21:28 20014
    IM_ASSERT(window->DockNode == NULL);
1193b4be Poul Sander 2025-02-03 21:28 20015
1193b4be Poul Sander 2025-02-03 21:28 20016
    // We should not be docking into a split node (SetWindowDock should avoid this)
1193b4be Poul Sander 2025-02-03 21:28 20017
    if (node && node->IsSplitNode())
1193b4be Poul Sander 2025-02-03 21:28 20018
    {
1193b4be Poul Sander 2025-02-03 21:28 20019
        DockContextProcessUndockWindow(ctx, window);
1193b4be Poul Sander 2025-02-03 21:28 20020
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20021
    }
1193b4be Poul Sander 2025-02-03 21:28 20022
1193b4be Poul Sander 2025-02-03 21:28 20023
    // Create node
1193b4be Poul Sander 2025-02-03 21:28 20024
    if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20025
    {
1193b4be Poul Sander 2025-02-03 21:28 20026
        node = DockContextAddNode(ctx, window->DockId);
1193b4be Poul Sander 2025-02-03 21:28 20027
        node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
1193b4be Poul Sander 2025-02-03 21:28 20028
        node->LastFrameAlive = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 20029
    }
1193b4be Poul Sander 2025-02-03 21:28 20030
1193b4be Poul Sander 2025-02-03 21:28 20031
    // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
1193b4be Poul Sander 2025-02-03 21:28 20032
    // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
1193b4be Poul Sander 2025-02-03 21:28 20033
    // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout.
1193b4be Poul Sander 2025-02-03 21:28 20034
    // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
1193b4be Poul Sander 2025-02-03 21:28 20035
    if (!node->IsVisible)
1193b4be Poul Sander 2025-02-03 21:28 20036
    {
1193b4be Poul Sander 2025-02-03 21:28 20037
        ImGuiDockNode* ancestor_node = node;
1193b4be Poul Sander 2025-02-03 21:28 20038
        while (!ancestor_node->IsVisible && ancestor_node->ParentNode)
1193b4be Poul Sander 2025-02-03 21:28 20039
            ancestor_node = ancestor_node->ParentNode;
1193b4be Poul Sander 2025-02-03 21:28 20040
        IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 20041
        DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node));
1193b4be Poul Sander 2025-02-03 21:28 20042
        DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node);
1193b4be Poul Sander 2025-02-03 21:28 20043
    }
1193b4be Poul Sander 2025-02-03 21:28 20044
1193b4be Poul Sander 2025-02-03 21:28 20045
    // Add window to node
1193b4be Poul Sander 2025-02-03 21:28 20046
    bool node_was_visible = node->IsVisible;
1193b4be Poul Sander 2025-02-03 21:28 20047
    DockNodeAddWindow(node, window, true);
1193b4be Poul Sander 2025-02-03 21:28 20048
    node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see)
1193b4be Poul Sander 2025-02-03 21:28 20049
    IM_ASSERT(node == window->DockNode);
1193b4be Poul Sander 2025-02-03 21:28 20050
    return node;
1193b4be Poul Sander 2025-02-03 21:28 20051
}
1193b4be Poul Sander 2025-02-03 21:28 20052
1193b4be Poul Sander 2025-02-03 21:28 20053
static void StoreDockStyleForWindow(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 20054
{
1193b4be Poul Sander 2025-02-03 21:28 20055
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20056
    for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
1193b4be Poul Sander 2025-02-03 21:28 20057
        window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
1193b4be Poul Sander 2025-02-03 21:28 20058
}
1193b4be Poul Sander 2025-02-03 21:28 20059
1193b4be Poul Sander 2025-02-03 21:28 20060
void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
1193b4be Poul Sander 2025-02-03 21:28 20061
{
1193b4be Poul Sander 2025-02-03 21:28 20062
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20063
1193b4be Poul Sander 2025-02-03 21:28 20064
    // Clear fields ahead so most early-out paths don't have to do it
1193b4be Poul Sander 2025-02-03 21:28 20065
    window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 20066
1193b4be Poul Sander 2025-02-03 21:28 20067
    const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
1193b4be Poul Sander 2025-02-03 21:28 20068
    if (auto_dock_node)
1193b4be Poul Sander 2025-02-03 21:28 20069
    {
1193b4be Poul Sander 2025-02-03 21:28 20070
        if (window->DockId == 0)
1193b4be Poul Sander 2025-02-03 21:28 20071
        {
1193b4be Poul Sander 2025-02-03 21:28 20072
            IM_ASSERT(window->DockNode == NULL);
1193b4be Poul Sander 2025-02-03 21:28 20073
            window->DockId = DockContextGenNodeID(&g);
1193b4be Poul Sander 2025-02-03 21:28 20074
        }
1193b4be Poul Sander 2025-02-03 21:28 20075
    }
1193b4be Poul Sander 2025-02-03 21:28 20076
    else
1193b4be Poul Sander 2025-02-03 21:28 20077
    {
1193b4be Poul Sander 2025-02-03 21:28 20078
        // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
1193b4be Poul Sander 2025-02-03 21:28 20079
        bool want_undock = false;
1193b4be Poul Sander 2025-02-03 21:28 20080
        want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
1193b4be Poul Sander 2025-02-03 21:28 20081
        want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
1193b4be Poul Sander 2025-02-03 21:28 20082
        if (want_undock)
1193b4be Poul Sander 2025-02-03 21:28 20083
        {
1193b4be Poul Sander 2025-02-03 21:28 20084
            DockContextProcessUndockWindow(&g, window);
1193b4be Poul Sander 2025-02-03 21:28 20085
            return;
1193b4be Poul Sander 2025-02-03 21:28 20086
        }
1193b4be Poul Sander 2025-02-03 21:28 20087
    }
1193b4be Poul Sander 2025-02-03 21:28 20088
1193b4be Poul Sander 2025-02-03 21:28 20089
    // Bind to our dock node
1193b4be Poul Sander 2025-02-03 21:28 20090
    ImGuiDockNode* node = window->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 20091
    if (node != NULL)
1193b4be Poul Sander 2025-02-03 21:28 20092
        IM_ASSERT(window->DockId == node->ID);
1193b4be Poul Sander 2025-02-03 21:28 20093
    if (window->DockId != 0 && node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20094
    {
1193b4be Poul Sander 2025-02-03 21:28 20095
        node = DockContextBindNodeToWindow(&g, window);
1193b4be Poul Sander 2025-02-03 21:28 20096
        if (node == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20097
            return;
1193b4be Poul Sander 2025-02-03 21:28 20098
    }
1193b4be Poul Sander 2025-02-03 21:28 20099
1193b4be Poul Sander 2025-02-03 21:28 20100
#if 0
1193b4be Poul Sander 2025-02-03 21:28 20101
    // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
1193b4be Poul Sander 2025-02-03 21:28 20102
    if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
1193b4be Poul Sander 2025-02-03 21:28 20103
    {
1193b4be Poul Sander 2025-02-03 21:28 20104
        DockContextProcessUndockWindow(ctx, window);
1193b4be Poul Sander 2025-02-03 21:28 20105
        return;
1193b4be Poul Sander 2025-02-03 21:28 20106
    }
1193b4be Poul Sander 2025-02-03 21:28 20107
#endif
1193b4be Poul Sander 2025-02-03 21:28 20108
1193b4be Poul Sander 2025-02-03 21:28 20109
    // Undock if our dockspace node disappeared
1193b4be Poul Sander 2025-02-03 21:28 20110
    // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
1193b4be Poul Sander 2025-02-03 21:28 20111
    if (node->LastFrameAlive < g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 20112
    {
1193b4be Poul Sander 2025-02-03 21:28 20113
        // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
1193b4be Poul Sander 2025-02-03 21:28 20114
        ImGuiDockNode* root_node = DockNodeGetRootNode(node);
1193b4be Poul Sander 2025-02-03 21:28 20115
        if (root_node->LastFrameAlive < g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 20116
            DockContextProcessUndockWindow(&g, window);
1193b4be Poul Sander 2025-02-03 21:28 20117
        else
1193b4be Poul Sander 2025-02-03 21:28 20118
            window->DockIsActive = true;
1193b4be Poul Sander 2025-02-03 21:28 20119
        return;
1193b4be Poul Sander 2025-02-03 21:28 20120
    }
1193b4be Poul Sander 2025-02-03 21:28 20121
1193b4be Poul Sander 2025-02-03 21:28 20122
    // Store style overrides
1193b4be Poul Sander 2025-02-03 21:28 20123
    StoreDockStyleForWindow(window);
1193b4be Poul Sander 2025-02-03 21:28 20124
1193b4be Poul Sander 2025-02-03 21:28 20125
    // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
1193b4be Poul Sander 2025-02-03 21:28 20126
    // and never create neither a host window neither a tab bar.
1193b4be Poul Sander 2025-02-03 21:28 20127
    // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
1193b4be Poul Sander 2025-02-03 21:28 20128
    if (node->HostWindow == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20129
    {
1193b4be Poul Sander 2025-02-03 21:28 20130
        if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)
1193b4be Poul Sander 2025-02-03 21:28 20131
            window->DockIsActive = true;
1193b4be Poul Sander 2025-02-03 21:28 20132
        if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window
1193b4be Poul Sander 2025-02-03 21:28 20133
            DockNodeHideWindowDuringHostWindowCreation(window);
1193b4be Poul Sander 2025-02-03 21:28 20134
        return;
1193b4be Poul Sander 2025-02-03 21:28 20135
    }
1193b4be Poul Sander 2025-02-03 21:28 20136
1193b4be Poul Sander 2025-02-03 21:28 20137
    // We can have zero-sized nodes (e.g. children of a small-size dockspace)
1193b4be Poul Sander 2025-02-03 21:28 20138
    IM_ASSERT(node->HostWindow);
1193b4be Poul Sander 2025-02-03 21:28 20139
    IM_ASSERT(node->IsLeafNode());
1193b4be Poul Sander 2025-02-03 21:28 20140
    IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 20141
    node->State = ImGuiDockNodeState_HostWindowVisible;
1193b4be Poul Sander 2025-02-03 21:28 20142
1193b4be Poul Sander 2025-02-03 21:28 20143
    // Undock if we are submitted earlier than the host window
1193b4be Poul Sander 2025-02-03 21:28 20144
    if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
1193b4be Poul Sander 2025-02-03 21:28 20145
    {
1193b4be Poul Sander 2025-02-03 21:28 20146
        DockContextProcessUndockWindow(&g, window);
1193b4be Poul Sander 2025-02-03 21:28 20147
        return;
1193b4be Poul Sander 2025-02-03 21:28 20148
    }
1193b4be Poul Sander 2025-02-03 21:28 20149
1193b4be Poul Sander 2025-02-03 21:28 20150
    // Position/Size window
1193b4be Poul Sander 2025-02-03 21:28 20151
    SetNextWindowPos(node->Pos);
1193b4be Poul Sander 2025-02-03 21:28 20152
    SetNextWindowSize(node->Size);
1193b4be Poul Sander 2025-02-03 21:28 20153
    g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
1193b4be Poul Sander 2025-02-03 21:28 20154
    window->DockIsActive = true;
1193b4be Poul Sander 2025-02-03 21:28 20155
    window->DockNodeIsVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 20156
    window->DockTabIsVisible = false;
1193b4be Poul Sander 2025-02-03 21:28 20157
    if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
1193b4be Poul Sander 2025-02-03 21:28 20158
        return;
1193b4be Poul Sander 2025-02-03 21:28 20159
1193b4be Poul Sander 2025-02-03 21:28 20160
    // When the window is selected we mark it as visible.
1193b4be Poul Sander 2025-02-03 21:28 20161
    if (node->VisibleWindow == window)
1193b4be Poul Sander 2025-02-03 21:28 20162
        window->DockTabIsVisible = true;
1193b4be Poul Sander 2025-02-03 21:28 20163
1193b4be Poul Sander 2025-02-03 21:28 20164
    // Update window flag
1193b4be Poul Sander 2025-02-03 21:28 20165
    IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
1193b4be Poul Sander 2025-02-03 21:28 20166
    window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize;
1193b4be Poul Sander 2025-02-03 21:28 20167
    window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding;
1193b4be Poul Sander 2025-02-03 21:28 20168
    if (node->IsHiddenTabBar() || node->IsNoTabBar())
1193b4be Poul Sander 2025-02-03 21:28 20169
        window->Flags |= ImGuiWindowFlags_NoTitleBar;
1193b4be Poul Sander 2025-02-03 21:28 20170
    else
1193b4be Poul Sander 2025-02-03 21:28 20171
        window->Flags &= ~ImGuiWindowFlags_NoTitleBar;      // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed!
1193b4be Poul Sander 2025-02-03 21:28 20172
1193b4be Poul Sander 2025-02-03 21:28 20173
    // Save new dock order only if the window has been visible once already
1193b4be Poul Sander 2025-02-03 21:28 20174
    // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
1193b4be Poul Sander 2025-02-03 21:28 20175
    if (node->TabBar && window->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 20176
        window->DockOrder = (short)DockNodeGetTabOrder(window);
1193b4be Poul Sander 2025-02-03 21:28 20177
1193b4be Poul Sander 2025-02-03 21:28 20178
    if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL)
1193b4be Poul Sander 2025-02-03 21:28 20179
        *p_open = false;
1193b4be Poul Sander 2025-02-03 21:28 20180
1193b4be Poul Sander 2025-02-03 21:28 20181
    // Update ChildId to allow returning from Child to Parent with Escape
1193b4be Poul Sander 2025-02-03 21:28 20182
    ImGuiWindow* parent_window = window->DockNode->HostWindow;
1193b4be Poul Sander 2025-02-03 21:28 20183
    window->ChildId = parent_window->GetID(window->Name);
1193b4be Poul Sander 2025-02-03 21:28 20184
}
1193b4be Poul Sander 2025-02-03 21:28 20185
1193b4be Poul Sander 2025-02-03 21:28 20186
void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 20187
{
1193b4be Poul Sander 2025-02-03 21:28 20188
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20189
    IM_ASSERT(g.ActiveId == window->MoveId);
1193b4be Poul Sander 2025-02-03 21:28 20190
    IM_ASSERT(g.MovingWindow == window);
1193b4be Poul Sander 2025-02-03 21:28 20191
    IM_ASSERT(g.CurrentWindow == window);
1193b4be Poul Sander 2025-02-03 21:28 20192
1193b4be Poul Sander 2025-02-03 21:28 20193
    // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking.
1193b4be Poul Sander 2025-02-03 21:28 20194
    if (g.IO.ConfigDockingWithShift != g.IO.KeyShift)
1193b4be Poul Sander 2025-02-03 21:28 20195
    {
1193b4be Poul Sander 2025-02-03 21:28 20196
        // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance.
1193b4be Poul Sander 2025-02-03 21:28 20197
        // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active
1193b4be Poul Sander 2025-02-03 21:28 20198
        // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function)
1193b4be Poul Sander 2025-02-03 21:28 20199
        IM_ASSERT(g.NextWindowData.Flags == 0);
1193b4be Poul Sander 2025-02-03 21:28 20200
        if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f)
1193b4be Poul Sander 2025-02-03 21:28 20201
            SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock));
1193b4be Poul Sander 2025-02-03 21:28 20202
        return;
1193b4be Poul Sander 2025-02-03 21:28 20203
    }
1193b4be Poul Sander 2025-02-03 21:28 20204
1193b4be Poul Sander 2025-02-03 21:28 20205
    g.LastItemData.ID = window->MoveId;
1193b4be Poul Sander 2025-02-03 21:28 20206
    window = window->RootWindowDockTree;
1193b4be Poul Sander 2025-02-03 21:28 20207
    IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
1193b4be Poul Sander 2025-02-03 21:28 20208
    bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit
1193b4be Poul Sander 2025-02-03 21:28 20209
    ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess;
1193b4be Poul Sander 2025-02-03 21:28 20210
    if (is_drag_docking && BeginDragDropSource(drag_drop_flags))
1193b4be Poul Sander 2025-02-03 21:28 20211
    {
1193b4be Poul Sander 2025-02-03 21:28 20212
        SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
1193b4be Poul Sander 2025-02-03 21:28 20213
        EndDragDropSource();
1193b4be Poul Sander 2025-02-03 21:28 20214
        StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it.
1193b4be Poul Sander 2025-02-03 21:28 20215
    }
1193b4be Poul Sander 2025-02-03 21:28 20216
}
1193b4be Poul Sander 2025-02-03 21:28 20217
1193b4be Poul Sander 2025-02-03 21:28 20218
void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
1193b4be Poul Sander 2025-02-03 21:28 20219
{
1193b4be Poul Sander 2025-02-03 21:28 20220
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20221
1193b4be Poul Sander 2025-02-03 21:28 20222
    //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace
1193b4be Poul Sander 2025-02-03 21:28 20223
    IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
1193b4be Poul Sander 2025-02-03 21:28 20224
    if (!g.DragDropActive)
1193b4be Poul Sander 2025-02-03 21:28 20225
        return;
1193b4be Poul Sander 2025-02-03 21:28 20226
    //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 20227
    if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
1193b4be Poul Sander 2025-02-03 21:28 20228
        return;
1193b4be Poul Sander 2025-02-03 21:28 20229
1193b4be Poul Sander 2025-02-03 21:28 20230
    // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
1193b4be Poul Sander 2025-02-03 21:28 20231
    // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
1193b4be Poul Sander 2025-02-03 21:28 20232
    const ImGuiPayload* payload = &g.DragDropPayload;
1193b4be Poul Sander 2025-02-03 21:28 20233
    if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
1193b4be Poul Sander 2025-02-03 21:28 20234
    {
1193b4be Poul Sander 2025-02-03 21:28 20235
        EndDragDropTarget();
1193b4be Poul Sander 2025-02-03 21:28 20236
        return;
1193b4be Poul Sander 2025-02-03 21:28 20237
    }
1193b4be Poul Sander 2025-02-03 21:28 20238
1193b4be Poul Sander 2025-02-03 21:28 20239
    ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
1193b4be Poul Sander 2025-02-03 21:28 20240
    if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
1193b4be Poul Sander 2025-02-03 21:28 20241
    {
1193b4be Poul Sander 2025-02-03 21:28 20242
        // Select target node
1193b4be Poul Sander 2025-02-03 21:28 20243
        // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation)
1193b4be Poul Sander 2025-02-03 21:28 20244
        bool dock_into_floating_window = false;
1193b4be Poul Sander 2025-02-03 21:28 20245
        ImGuiDockNode* node = NULL;
1193b4be Poul Sander 2025-02-03 21:28 20246
        if (window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 20247
        {
1193b4be Poul Sander 2025-02-03 21:28 20248
            // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
1193b4be Poul Sander 2025-02-03 21:28 20249
            node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
1193b4be Poul Sander 2025-02-03 21:28 20250
1193b4be Poul Sander 2025-02-03 21:28 20251
            // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
1193b4be Poul Sander 2025-02-03 21:28 20252
            // In this case we need to fallback into any leaf mode, possibly the central node.
1193b4be Poul Sander 2025-02-03 21:28 20253
            // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
1193b4be Poul Sander 2025-02-03 21:28 20254
            if (node && node->IsDockSpace() && node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 20255
                node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
1193b4be Poul Sander 2025-02-03 21:28 20256
        }
1193b4be Poul Sander 2025-02-03 21:28 20257
        else
1193b4be Poul Sander 2025-02-03 21:28 20258
        {
1193b4be Poul Sander 2025-02-03 21:28 20259
            if (window->DockNode)
1193b4be Poul Sander 2025-02-03 21:28 20260
                node = window->DockNode;
1193b4be Poul Sander 2025-02-03 21:28 20261
            else
1193b4be Poul Sander 2025-02-03 21:28 20262
                dock_into_floating_window = true; // Dock into a regular window
1193b4be Poul Sander 2025-02-03 21:28 20263
        }
1193b4be Poul Sander 2025-02-03 21:28 20264
1193b4be Poul Sander 2025-02-03 21:28 20265
        const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight()));
1193b4be Poul Sander 2025-02-03 21:28 20266
        const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
1193b4be Poul Sander 2025-02-03 21:28 20267
1193b4be Poul Sander 2025-02-03 21:28 20268
        // Preview docking request and find out split direction/ratio
1193b4be Poul Sander 2025-02-03 21:28 20269
        //const bool do_preview = true;     // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window.
1193b4be Poul Sander 2025-02-03 21:28 20270
        const bool do_preview = payload->IsPreview() || payload->IsDelivery();
1193b4be Poul Sander 2025-02-03 21:28 20271
        if (do_preview && (node != NULL || dock_into_floating_window))
1193b4be Poul Sander 2025-02-03 21:28 20272
        {
1193b4be Poul Sander 2025-02-03 21:28 20273
            // If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear.
1193b4be Poul Sander 2025-02-03 21:28 20274
            ImGuiDockPreviewData split_inner;
1193b4be Poul Sander 2025-02-03 21:28 20275
            ImGuiDockPreviewData split_outer;
1193b4be Poul Sander 2025-02-03 21:28 20276
            ImGuiDockPreviewData* split_data = &split_inner;
1193b4be Poul Sander 2025-02-03 21:28 20277
            if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode()))
1193b4be Poul Sander 2025-02-03 21:28 20278
                if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
1193b4be Poul Sander 2025-02-03 21:28 20279
                {
1193b4be Poul Sander 2025-02-03 21:28 20280
                    DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true);
1193b4be Poul Sander 2025-02-03 21:28 20281
                    if (split_outer.IsSplitDirExplicit)
1193b4be Poul Sander 2025-02-03 21:28 20282
                        split_data = &split_outer;
1193b4be Poul Sander 2025-02-03 21:28 20283
                }
1193b4be Poul Sander 2025-02-03 21:28 20284
            if (!node || node->IsLeafNode())
1193b4be Poul Sander 2025-02-03 21:28 20285
                DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false);
1193b4be Poul Sander 2025-02-03 21:28 20286
            if (split_data == &split_outer)
1193b4be Poul Sander 2025-02-03 21:28 20287
                split_inner.IsDropAllowed = false;
1193b4be Poul Sander 2025-02-03 21:28 20288
1193b4be Poul Sander 2025-02-03 21:28 20289
            // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
1193b4be Poul Sander 2025-02-03 21:28 20290
            DockNodePreviewDockRender(window, node, payload_window, &split_inner);
1193b4be Poul Sander 2025-02-03 21:28 20291
            DockNodePreviewDockRender(window, node, payload_window, &split_outer);
1193b4be Poul Sander 2025-02-03 21:28 20292
1193b4be Poul Sander 2025-02-03 21:28 20293
            // Queue docking request
1193b4be Poul Sander 2025-02-03 21:28 20294
            if (split_data->IsDropAllowed && payload->IsDelivery())
1193b4be Poul Sander 2025-02-03 21:28 20295
                DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
1193b4be Poul Sander 2025-02-03 21:28 20296
        }
1193b4be Poul Sander 2025-02-03 21:28 20297
    }
1193b4be Poul Sander 2025-02-03 21:28 20298
    EndDragDropTarget();
1193b4be Poul Sander 2025-02-03 21:28 20299
}
1193b4be Poul Sander 2025-02-03 21:28 20300
1193b4be Poul Sander 2025-02-03 21:28 20301
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20302
// Docking: Settings
1193b4be Poul Sander 2025-02-03 21:28 20303
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20304
// - DockSettingsRenameNodeReferences()
1193b4be Poul Sander 2025-02-03 21:28 20305
// - DockSettingsRemoveNodeReferences()
1193b4be Poul Sander 2025-02-03 21:28 20306
// - DockSettingsFindNodeSettings()
1193b4be Poul Sander 2025-02-03 21:28 20307
// - DockSettingsHandler_ApplyAll()
1193b4be Poul Sander 2025-02-03 21:28 20308
// - DockSettingsHandler_ReadOpen()
1193b4be Poul Sander 2025-02-03 21:28 20309
// - DockSettingsHandler_ReadLine()
1193b4be Poul Sander 2025-02-03 21:28 20310
// - DockSettingsHandler_DockNodeToSettings()
1193b4be Poul Sander 2025-02-03 21:28 20311
// - DockSettingsHandler_WriteAll()
1193b4be Poul Sander 2025-02-03 21:28 20312
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20313
1193b4be Poul Sander 2025-02-03 21:28 20314
static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
1193b4be Poul Sander 2025-02-03 21:28 20315
{
1193b4be Poul Sander 2025-02-03 21:28 20316
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20317
    IMGUI_DEBUG_LOG_DOCKING("[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
1193b4be Poul Sander 2025-02-03 21:28 20318
    for (int window_n = 0; window_n < g.Windows.Size; window_n++)
1193b4be Poul Sander 2025-02-03 21:28 20319
    {
1193b4be Poul Sander 2025-02-03 21:28 20320
        ImGuiWindow* window = g.Windows[window_n];
1193b4be Poul Sander 2025-02-03 21:28 20321
        if (window->DockId == old_node_id && window->DockNode == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20322
            window->DockId = new_node_id;
1193b4be Poul Sander 2025-02-03 21:28 20323
    }
1193b4be Poul Sander 2025-02-03 21:28 20324
    //// FIXME-OPT: We could remove this loop by storing the index in the map
1193b4be Poul Sander 2025-02-03 21:28 20325
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 20326
        if (settings->DockId == old_node_id)
1193b4be Poul Sander 2025-02-03 21:28 20327
            settings->DockId = new_node_id;
1193b4be Poul Sander 2025-02-03 21:28 20328
}
1193b4be Poul Sander 2025-02-03 21:28 20329
1193b4be Poul Sander 2025-02-03 21:28 20330
// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
1193b4be Poul Sander 2025-02-03 21:28 20331
static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
1193b4be Poul Sander 2025-02-03 21:28 20332
{
1193b4be Poul Sander 2025-02-03 21:28 20333
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20334
    int found = 0;
1193b4be Poul Sander 2025-02-03 21:28 20335
    //// FIXME-OPT: We could remove this loop by storing the index in the map
1193b4be Poul Sander 2025-02-03 21:28 20336
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 20337
        for (int node_n = 0; node_n < node_ids_count; node_n++)
1193b4be Poul Sander 2025-02-03 21:28 20338
            if (settings->DockId == node_ids[node_n])
1193b4be Poul Sander 2025-02-03 21:28 20339
            {
1193b4be Poul Sander 2025-02-03 21:28 20340
                settings->DockId = 0;
1193b4be Poul Sander 2025-02-03 21:28 20341
                settings->DockOrder = -1;
1193b4be Poul Sander 2025-02-03 21:28 20342
                if (++found < node_ids_count)
1193b4be Poul Sander 2025-02-03 21:28 20343
                    break;
1193b4be Poul Sander 2025-02-03 21:28 20344
                return;
1193b4be Poul Sander 2025-02-03 21:28 20345
            }
1193b4be Poul Sander 2025-02-03 21:28 20346
}
1193b4be Poul Sander 2025-02-03 21:28 20347
1193b4be Poul Sander 2025-02-03 21:28 20348
static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
1193b4be Poul Sander 2025-02-03 21:28 20349
{
1193b4be Poul Sander 2025-02-03 21:28 20350
    // FIXME-OPT
1193b4be Poul Sander 2025-02-03 21:28 20351
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 20352
    for (int n = 0; n < dc->NodesSettings.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 20353
        if (dc->NodesSettings[n].ID == id)
1193b4be Poul Sander 2025-02-03 21:28 20354
            return &dc->NodesSettings[n];
1193b4be Poul Sander 2025-02-03 21:28 20355
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20356
}
1193b4be Poul Sander 2025-02-03 21:28 20357
1193b4be Poul Sander 2025-02-03 21:28 20358
// Clear settings data
1193b4be Poul Sander 2025-02-03 21:28 20359
static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
1193b4be Poul Sander 2025-02-03 21:28 20360
{
1193b4be Poul Sander 2025-02-03 21:28 20361
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 20362
    dc->NodesSettings.clear();
1193b4be Poul Sander 2025-02-03 21:28 20363
    DockContextClearNodes(ctx, 0, true);
1193b4be Poul Sander 2025-02-03 21:28 20364
}
1193b4be Poul Sander 2025-02-03 21:28 20365
1193b4be Poul Sander 2025-02-03 21:28 20366
// Recreate nodes based on settings data
1193b4be Poul Sander 2025-02-03 21:28 20367
static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
1193b4be Poul Sander 2025-02-03 21:28 20368
{
1193b4be Poul Sander 2025-02-03 21:28 20369
    // Prune settings at boot time only
1193b4be Poul Sander 2025-02-03 21:28 20370
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 20371
    if (ctx->Windows.Size == 0)
1193b4be Poul Sander 2025-02-03 21:28 20372
        DockContextPruneUnusedSettingsNodes(ctx);
1193b4be Poul Sander 2025-02-03 21:28 20373
    DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
1193b4be Poul Sander 2025-02-03 21:28 20374
    DockContextBuildAddWindowsToNodes(ctx, 0);
1193b4be Poul Sander 2025-02-03 21:28 20375
}
1193b4be Poul Sander 2025-02-03 21:28 20376
1193b4be Poul Sander 2025-02-03 21:28 20377
static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
1193b4be Poul Sander 2025-02-03 21:28 20378
{
1193b4be Poul Sander 2025-02-03 21:28 20379
    if (strcmp(name, "Data") != 0)
1193b4be Poul Sander 2025-02-03 21:28 20380
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20381
    return (void*)1;
1193b4be Poul Sander 2025-02-03 21:28 20382
}
1193b4be Poul Sander 2025-02-03 21:28 20383
1193b4be Poul Sander 2025-02-03 21:28 20384
static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
1193b4be Poul Sander 2025-02-03 21:28 20385
{
1193b4be Poul Sander 2025-02-03 21:28 20386
    char c = 0;
1193b4be Poul Sander 2025-02-03 21:28 20387
    int x = 0, y = 0;
1193b4be Poul Sander 2025-02-03 21:28 20388
    int r = 0;
1193b4be Poul Sander 2025-02-03 21:28 20389
1193b4be Poul Sander 2025-02-03 21:28 20390
    // Parsing, e.g.
1193b4be Poul Sander 2025-02-03 21:28 20391
    // " DockNode   ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
1193b4be Poul Sander 2025-02-03 21:28 20392
    // "   DockNode ID=0x00000002 Parent=0x00000001 "
1193b4be Poul Sander 2025-02-03 21:28 20393
    // Important: this code expect currently fields in a fixed order.
1193b4be Poul Sander 2025-02-03 21:28 20394
    ImGuiDockNodeSettings node;
1193b4be Poul Sander 2025-02-03 21:28 20395
    line = ImStrSkipBlank(line);
1193b4be Poul Sander 2025-02-03 21:28 20396
    if      (strncmp(line, "DockNode", 8) == 0)  { line = ImStrSkipBlank(line + strlen("DockNode")); }
1193b4be Poul Sander 2025-02-03 21:28 20397
    else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
1193b4be Poul Sander 2025-02-03 21:28 20398
    else return;
1193b4be Poul Sander 2025-02-03 21:28 20399
    if (sscanf(line, "ID=0x%08X%n",      &node.ID, &r) == 1)            { line += r; } else return;
1193b4be Poul Sander 2025-02-03 21:28 20400
    if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1)  { line += r; if (node.ParentNodeId == 0) return; }
1193b4be Poul Sander 2025-02-03 21:28 20401
    if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
1193b4be Poul Sander 2025-02-03 21:28 20402
    if (node.ParentNodeId == 0)
1193b4be Poul Sander 2025-02-03 21:28 20403
    {
1193b4be Poul Sander 2025-02-03 21:28 20404
        if (sscanf(line, " Pos=%i,%i%n",  &x, &y, &r) == 2)         { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
1193b4be Poul Sander 2025-02-03 21:28 20405
        if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2)         { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
1193b4be Poul Sander 2025-02-03 21:28 20406
    }
1193b4be Poul Sander 2025-02-03 21:28 20407
    else
1193b4be Poul Sander 2025-02-03 21:28 20408
    {
1193b4be Poul Sander 2025-02-03 21:28 20409
        if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2)      { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
1193b4be Poul Sander 2025-02-03 21:28 20410
    }
1193b4be Poul Sander 2025-02-03 21:28 20411
    if (sscanf(line, " Split=%c%n", &c, &r) == 1)                   { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; }
1193b4be Poul Sander 2025-02-03 21:28 20412
    if (sscanf(line, " NoResize=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
1193b4be Poul Sander 2025-02-03 21:28 20413
    if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1)             { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
1193b4be Poul Sander 2025-02-03 21:28 20414
    if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
1193b4be Poul Sander 2025-02-03 21:28 20415
    if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1)            { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
1193b4be Poul Sander 2025-02-03 21:28 20416
    if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1)      { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
1193b4be Poul Sander 2025-02-03 21:28 20417
    if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1)           { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
1193b4be Poul Sander 2025-02-03 21:28 20418
    if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; }
1193b4be Poul Sander 2025-02-03 21:28 20419
    if (node.ParentNodeId != 0)
1193b4be Poul Sander 2025-02-03 21:28 20420
        if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
1193b4be Poul Sander 2025-02-03 21:28 20421
            node.Depth = parent_settings->Depth + 1;
1193b4be Poul Sander 2025-02-03 21:28 20422
    ctx->DockContext.NodesSettings.push_back(node);
1193b4be Poul Sander 2025-02-03 21:28 20423
}
1193b4be Poul Sander 2025-02-03 21:28 20424
1193b4be Poul Sander 2025-02-03 21:28 20425
static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
1193b4be Poul Sander 2025-02-03 21:28 20426
{
1193b4be Poul Sander 2025-02-03 21:28 20427
    ImGuiDockNodeSettings node_settings;
1193b4be Poul Sander 2025-02-03 21:28 20428
    IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
1193b4be Poul Sander 2025-02-03 21:28 20429
    node_settings.ID = node->ID;
1193b4be Poul Sander 2025-02-03 21:28 20430
    node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
1193b4be Poul Sander 2025-02-03 21:28 20431
    node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
1193b4be Poul Sander 2025-02-03 21:28 20432
    node_settings.SelectedTabId = node->SelectedTabId;
1193b4be Poul Sander 2025-02-03 21:28 20433
    node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
1193b4be Poul Sander 2025-02-03 21:28 20434
    node_settings.Depth = (char)depth;
1193b4be Poul Sander 2025-02-03 21:28 20435
    node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
1193b4be Poul Sander 2025-02-03 21:28 20436
    node_settings.Pos = ImVec2ih(node->Pos);
1193b4be Poul Sander 2025-02-03 21:28 20437
    node_settings.Size = ImVec2ih(node->Size);
1193b4be Poul Sander 2025-02-03 21:28 20438
    node_settings.SizeRef = ImVec2ih(node->SizeRef);
1193b4be Poul Sander 2025-02-03 21:28 20439
    dc->NodesSettings.push_back(node_settings);
1193b4be Poul Sander 2025-02-03 21:28 20440
    if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 20441
        DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
1193b4be Poul Sander 2025-02-03 21:28 20442
    if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 20443
        DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
1193b4be Poul Sander 2025-02-03 21:28 20444
}
1193b4be Poul Sander 2025-02-03 21:28 20445
1193b4be Poul Sander 2025-02-03 21:28 20446
static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
1193b4be Poul Sander 2025-02-03 21:28 20447
{
1193b4be Poul Sander 2025-02-03 21:28 20448
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20449
    ImGuiDockContext* dc = &ctx->DockContext;
1193b4be Poul Sander 2025-02-03 21:28 20450
    if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
1193b4be Poul Sander 2025-02-03 21:28 20451
        return;
1193b4be Poul Sander 2025-02-03 21:28 20452
1193b4be Poul Sander 2025-02-03 21:28 20453
    // Gather settings data
1193b4be Poul Sander 2025-02-03 21:28 20454
    // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
1193b4be Poul Sander 2025-02-03 21:28 20455
    dc->NodesSettings.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 20456
    dc->NodesSettings.reserve(dc->Nodes.Data.Size);
1193b4be Poul Sander 2025-02-03 21:28 20457
    for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 20458
        if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 20459
            if (node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 20460
                DockSettingsHandler_DockNodeToSettings(dc, node, 0);
1193b4be Poul Sander 2025-02-03 21:28 20461
1193b4be Poul Sander 2025-02-03 21:28 20462
    int max_depth = 0;
1193b4be Poul Sander 2025-02-03 21:28 20463
    for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
1193b4be Poul Sander 2025-02-03 21:28 20464
        max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
1193b4be Poul Sander 2025-02-03 21:28 20465
1193b4be Poul Sander 2025-02-03 21:28 20466
    // Write to text buffer
1193b4be Poul Sander 2025-02-03 21:28 20467
    buf->appendf("[%s][Data]\n", handler->TypeName);
1193b4be Poul Sander 2025-02-03 21:28 20468
    for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
1193b4be Poul Sander 2025-02-03 21:28 20469
    {
1193b4be Poul Sander 2025-02-03 21:28 20470
        const int line_start_pos = buf->size(); (void)line_start_pos;
1193b4be Poul Sander 2025-02-03 21:28 20471
        const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
1193b4be Poul Sander 2025-02-03 21:28 20472
        buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, "");  // Text align nodes to facilitate looking at .ini file
1193b4be Poul Sander 2025-02-03 21:28 20473
        buf->appendf(" ID=0x%08X", node_settings->ID);
1193b4be Poul Sander 2025-02-03 21:28 20474
        if (node_settings->ParentNodeId)
1193b4be Poul Sander 2025-02-03 21:28 20475
        {
1193b4be Poul Sander 2025-02-03 21:28 20476
            buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
1193b4be Poul Sander 2025-02-03 21:28 20477
        }
1193b4be Poul Sander 2025-02-03 21:28 20478
        else
1193b4be Poul Sander 2025-02-03 21:28 20479
        {
1193b4be Poul Sander 2025-02-03 21:28 20480
            if (node_settings->ParentWindowId)
1193b4be Poul Sander 2025-02-03 21:28 20481
                buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
1193b4be Poul Sander 2025-02-03 21:28 20482
            buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 20483
        }
1193b4be Poul Sander 2025-02-03 21:28 20484
        if (node_settings->SplitAxis != ImGuiAxis_None)
1193b4be Poul Sander 2025-02-03 21:28 20485
            buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
1193b4be Poul Sander 2025-02-03 21:28 20486
        if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
1193b4be Poul Sander 2025-02-03 21:28 20487
            buf->appendf(" NoResize=1");
1193b4be Poul Sander 2025-02-03 21:28 20488
        if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
1193b4be Poul Sander 2025-02-03 21:28 20489
            buf->appendf(" CentralNode=1");
1193b4be Poul Sander 2025-02-03 21:28 20490
        if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
1193b4be Poul Sander 2025-02-03 21:28 20491
            buf->appendf(" NoTabBar=1");
1193b4be Poul Sander 2025-02-03 21:28 20492
        if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
1193b4be Poul Sander 2025-02-03 21:28 20493
            buf->appendf(" HiddenTabBar=1");
1193b4be Poul Sander 2025-02-03 21:28 20494
        if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
1193b4be Poul Sander 2025-02-03 21:28 20495
            buf->appendf(" NoWindowMenuButton=1");
1193b4be Poul Sander 2025-02-03 21:28 20496
        if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
1193b4be Poul Sander 2025-02-03 21:28 20497
            buf->appendf(" NoCloseButton=1");
1193b4be Poul Sander 2025-02-03 21:28 20498
        if (node_settings->SelectedTabId)
1193b4be Poul Sander 2025-02-03 21:28 20499
            buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId);
1193b4be Poul Sander 2025-02-03 21:28 20500
1193b4be Poul Sander 2025-02-03 21:28 20501
        // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!)
1193b4be Poul Sander 2025-02-03 21:28 20502
        if (g.IO.ConfigDebugIniSettings)
1193b4be Poul Sander 2025-02-03 21:28 20503
            if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
1193b4be Poul Sander 2025-02-03 21:28 20504
            {
1193b4be Poul Sander 2025-02-03 21:28 20505
                buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), "");     // Align everything
1193b4be Poul Sander 2025-02-03 21:28 20506
                if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
1193b4be Poul Sander 2025-02-03 21:28 20507
                    buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 20508
                // Iterate settings so we can give info about windows that didn't exist during the session.
1193b4be Poul Sander 2025-02-03 21:28 20509
                int contains_window = 0;
1193b4be Poul Sander 2025-02-03 21:28 20510
                for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 20511
                    if (settings->DockId == node_settings->ID)
1193b4be Poul Sander 2025-02-03 21:28 20512
                    {
1193b4be Poul Sander 2025-02-03 21:28 20513
                        if (contains_window++ == 0)
1193b4be Poul Sander 2025-02-03 21:28 20514
                            buf->appendf(" ; contains ");
1193b4be Poul Sander 2025-02-03 21:28 20515
                        buf->appendf("'%s' ", settings->GetName());
1193b4be Poul Sander 2025-02-03 21:28 20516
                    }
1193b4be Poul Sander 2025-02-03 21:28 20517
            }
1193b4be Poul Sander 2025-02-03 21:28 20518
1193b4be Poul Sander 2025-02-03 21:28 20519
        buf->appendf("\n");
1193b4be Poul Sander 2025-02-03 21:28 20520
    }
1193b4be Poul Sander 2025-02-03 21:28 20521
    buf->appendf("\n");
1193b4be Poul Sander 2025-02-03 21:28 20522
}
1193b4be Poul Sander 2025-02-03 21:28 20523
1193b4be Poul Sander 2025-02-03 21:28 20524
1193b4be Poul Sander 2025-02-03 21:28 20525
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20526
// [SECTION] PLATFORM DEPENDENT HELPERS
1193b4be Poul Sander 2025-02-03 21:28 20527
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20528
// - Default clipboard handlers
1193b4be Poul Sander 2025-02-03 21:28 20529
// - Default shell function handlers
1193b4be Poul Sander 2025-02-03 21:28 20530
// - Default IME handlers
1193b4be Poul Sander 2025-02-03 21:28 20531
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20532
1193b4be Poul Sander 2025-02-03 21:28 20533
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 20534
1193b4be Poul Sander 2025-02-03 21:28 20535
#ifdef _MSC_VER
1193b4be Poul Sander 2025-02-03 21:28 20536
#pragma comment(lib, "user32")
1193b4be Poul Sander 2025-02-03 21:28 20537
#pragma comment(lib, "kernel32")
1193b4be Poul Sander 2025-02-03 21:28 20538
#endif
1193b4be Poul Sander 2025-02-03 21:28 20539
1193b4be Poul Sander 2025-02-03 21:28 20540
// Win32 clipboard implementation
1193b4be Poul Sander 2025-02-03 21:28 20541
// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
1193b4be Poul Sander 2025-02-03 21:28 20542
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 20543
{
1193b4be Poul Sander 2025-02-03 21:28 20544
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20545
    g.ClipboardHandlerData.clear();
1193b4be Poul Sander 2025-02-03 21:28 20546
    if (!::OpenClipboard(NULL))
1193b4be Poul Sander 2025-02-03 21:28 20547
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20548
    HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
1193b4be Poul Sander 2025-02-03 21:28 20549
    if (wbuf_handle == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20550
    {
1193b4be Poul Sander 2025-02-03 21:28 20551
        ::CloseClipboard();
1193b4be Poul Sander 2025-02-03 21:28 20552
        return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20553
    }
1193b4be Poul Sander 2025-02-03 21:28 20554
    if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
1193b4be Poul Sander 2025-02-03 21:28 20555
    {
1193b4be Poul Sander 2025-02-03 21:28 20556
        int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 20557
        g.ClipboardHandlerData.resize(buf_len);
1193b4be Poul Sander 2025-02-03 21:28 20558
        ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
1193b4be Poul Sander 2025-02-03 21:28 20559
    }
1193b4be Poul Sander 2025-02-03 21:28 20560
    ::GlobalUnlock(wbuf_handle);
1193b4be Poul Sander 2025-02-03 21:28 20561
    ::CloseClipboard();
1193b4be Poul Sander 2025-02-03 21:28 20562
    return g.ClipboardHandlerData.Data;
1193b4be Poul Sander 2025-02-03 21:28 20563
}
1193b4be Poul Sander 2025-02-03 21:28 20564
1193b4be Poul Sander 2025-02-03 21:28 20565
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
1193b4be Poul Sander 2025-02-03 21:28 20566
{
1193b4be Poul Sander 2025-02-03 21:28 20567
    if (!::OpenClipboard(NULL))
1193b4be Poul Sander 2025-02-03 21:28 20568
        return;
1193b4be Poul Sander 2025-02-03 21:28 20569
    const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
1193b4be Poul Sander 2025-02-03 21:28 20570
    HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
1193b4be Poul Sander 2025-02-03 21:28 20571
    if (wbuf_handle == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20572
    {
1193b4be Poul Sander 2025-02-03 21:28 20573
        ::CloseClipboard();
1193b4be Poul Sander 2025-02-03 21:28 20574
        return;
1193b4be Poul Sander 2025-02-03 21:28 20575
    }
1193b4be Poul Sander 2025-02-03 21:28 20576
    WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
1193b4be Poul Sander 2025-02-03 21:28 20577
    ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
1193b4be Poul Sander 2025-02-03 21:28 20578
    ::GlobalUnlock(wbuf_handle);
1193b4be Poul Sander 2025-02-03 21:28 20579
    ::EmptyClipboard();
1193b4be Poul Sander 2025-02-03 21:28 20580
    if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
1193b4be Poul Sander 2025-02-03 21:28 20581
        ::GlobalFree(wbuf_handle);
1193b4be Poul Sander 2025-02-03 21:28 20582
    ::CloseClipboard();
1193b4be Poul Sander 2025-02-03 21:28 20583
}
1193b4be Poul Sander 2025-02-03 21:28 20584
1193b4be Poul Sander 2025-02-03 21:28 20585
#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 20586
1193b4be Poul Sander 2025-02-03 21:28 20587
#include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
1193b4be Poul Sander 2025-02-03 21:28 20588
static PasteboardRef main_clipboard = 0;
1193b4be Poul Sander 2025-02-03 21:28 20589
1193b4be Poul Sander 2025-02-03 21:28 20590
// OSX clipboard implementation
1193b4be Poul Sander 2025-02-03 21:28 20591
// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
1193b4be Poul Sander 2025-02-03 21:28 20592
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
1193b4be Poul Sander 2025-02-03 21:28 20593
{
1193b4be Poul Sander 2025-02-03 21:28 20594
    if (!main_clipboard)
1193b4be Poul Sander 2025-02-03 21:28 20595
        PasteboardCreate(kPasteboardClipboard, &main_clipboard);
1193b4be Poul Sander 2025-02-03 21:28 20596
    PasteboardClear(main_clipboard);
1193b4be Poul Sander 2025-02-03 21:28 20597
    CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
1193b4be Poul Sander 2025-02-03 21:28 20598
    if (cf_data)
1193b4be Poul Sander 2025-02-03 21:28 20599
    {
1193b4be Poul Sander 2025-02-03 21:28 20600
        PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
1193b4be Poul Sander 2025-02-03 21:28 20601
        CFRelease(cf_data);
1193b4be Poul Sander 2025-02-03 21:28 20602
    }
1193b4be Poul Sander 2025-02-03 21:28 20603
}
1193b4be Poul Sander 2025-02-03 21:28 20604
1193b4be Poul Sander 2025-02-03 21:28 20605
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 20606
{
1193b4be Poul Sander 2025-02-03 21:28 20607
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20608
    if (!main_clipboard)
1193b4be Poul Sander 2025-02-03 21:28 20609
        PasteboardCreate(kPasteboardClipboard, &main_clipboard);
1193b4be Poul Sander 2025-02-03 21:28 20610
    PasteboardSynchronize(main_clipboard);
1193b4be Poul Sander 2025-02-03 21:28 20611
1193b4be Poul Sander 2025-02-03 21:28 20612
    ItemCount item_count = 0;
1193b4be Poul Sander 2025-02-03 21:28 20613
    PasteboardGetItemCount(main_clipboard, &item_count);
1193b4be Poul Sander 2025-02-03 21:28 20614
    for (ItemCount i = 0; i < item_count; i++)
1193b4be Poul Sander 2025-02-03 21:28 20615
    {
1193b4be Poul Sander 2025-02-03 21:28 20616
        PasteboardItemID item_id = 0;
1193b4be Poul Sander 2025-02-03 21:28 20617
        PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
1193b4be Poul Sander 2025-02-03 21:28 20618
        CFArrayRef flavor_type_array = 0;
1193b4be Poul Sander 2025-02-03 21:28 20619
        PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
1193b4be Poul Sander 2025-02-03 21:28 20620
        for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
1193b4be Poul Sander 2025-02-03 21:28 20621
        {
1193b4be Poul Sander 2025-02-03 21:28 20622
            CFDataRef cf_data;
1193b4be Poul Sander 2025-02-03 21:28 20623
            if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
1193b4be Poul Sander 2025-02-03 21:28 20624
            {
1193b4be Poul Sander 2025-02-03 21:28 20625
                g.ClipboardHandlerData.clear();
1193b4be Poul Sander 2025-02-03 21:28 20626
                int length = (int)CFDataGetLength(cf_data);
1193b4be Poul Sander 2025-02-03 21:28 20627
                g.ClipboardHandlerData.resize(length + 1);
1193b4be Poul Sander 2025-02-03 21:28 20628
                CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
1193b4be Poul Sander 2025-02-03 21:28 20629
                g.ClipboardHandlerData[length] = 0;
1193b4be Poul Sander 2025-02-03 21:28 20630
                CFRelease(cf_data);
1193b4be Poul Sander 2025-02-03 21:28 20631
                return g.ClipboardHandlerData.Data;
1193b4be Poul Sander 2025-02-03 21:28 20632
            }
1193b4be Poul Sander 2025-02-03 21:28 20633
        }
1193b4be Poul Sander 2025-02-03 21:28 20634
    }
1193b4be Poul Sander 2025-02-03 21:28 20635
    return NULL;
1193b4be Poul Sander 2025-02-03 21:28 20636
}
1193b4be Poul Sander 2025-02-03 21:28 20637
1193b4be Poul Sander 2025-02-03 21:28 20638
#else
1193b4be Poul Sander 2025-02-03 21:28 20639
1193b4be Poul Sander 2025-02-03 21:28 20640
// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
1193b4be Poul Sander 2025-02-03 21:28 20641
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
1193b4be Poul Sander 2025-02-03 21:28 20642
{
1193b4be Poul Sander 2025-02-03 21:28 20643
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20644
    return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
1193b4be Poul Sander 2025-02-03 21:28 20645
}
1193b4be Poul Sander 2025-02-03 21:28 20646
1193b4be Poul Sander 2025-02-03 21:28 20647
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)
1193b4be Poul Sander 2025-02-03 21:28 20648
{
1193b4be Poul Sander 2025-02-03 21:28 20649
    ImGuiContext& g = *ctx;
1193b4be Poul Sander 2025-02-03 21:28 20650
    g.ClipboardHandlerData.clear();
1193b4be Poul Sander 2025-02-03 21:28 20651
    const char* text_end = text + strlen(text);
1193b4be Poul Sander 2025-02-03 21:28 20652
    g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
1193b4be Poul Sander 2025-02-03 21:28 20653
    memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
1193b4be Poul Sander 2025-02-03 21:28 20654
    g.ClipboardHandlerData[(int)(text_end - text)] = 0;
1193b4be Poul Sander 2025-02-03 21:28 20655
}
1193b4be Poul Sander 2025-02-03 21:28 20656
1193b4be Poul Sander 2025-02-03 21:28 20657
#endif // Default clipboard handlers
1193b4be Poul Sander 2025-02-03 21:28 20658
1193b4be Poul Sander 2025-02-03 21:28 20659
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20660
1193b4be Poul Sander 2025-02-03 21:28 20661
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 20662
#if defined(__APPLE__) && TARGET_OS_IPHONE
1193b4be Poul Sander 2025-02-03 21:28 20663
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 20664
#endif
1193b4be Poul Sander 2025-02-03 21:28 20665
1193b4be Poul Sander 2025-02-03 21:28 20666
#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 20667
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 20668
#endif
1193b4be Poul Sander 2025-02-03 21:28 20669
#endif
1193b4be Poul Sander 2025-02-03 21:28 20670
1193b4be Poul Sander 2025-02-03 21:28 20671
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1193b4be Poul Sander 2025-02-03 21:28 20672
#ifdef _WIN32
1193b4be Poul Sander 2025-02-03 21:28 20673
#include <shellapi.h>   // ShellExecuteA()
1193b4be Poul Sander 2025-02-03 21:28 20674
#ifdef _MSC_VER
1193b4be Poul Sander 2025-02-03 21:28 20675
#pragma comment(lib, "shell32")
1193b4be Poul Sander 2025-02-03 21:28 20676
#endif
1193b4be Poul Sander 2025-02-03 21:28 20677
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
1193b4be Poul Sander 2025-02-03 21:28 20678
{
1193b4be Poul Sander 2025-02-03 21:28 20679
    return (INT_PTR)::ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32;
1193b4be Poul Sander 2025-02-03 21:28 20680
}
1193b4be Poul Sander 2025-02-03 21:28 20681
#else
1193b4be Poul Sander 2025-02-03 21:28 20682
#include <sys/wait.h>
1193b4be Poul Sander 2025-02-03 21:28 20683
#include <unistd.h>
1193b4be Poul Sander 2025-02-03 21:28 20684
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
1193b4be Poul Sander 2025-02-03 21:28 20685
{
1193b4be Poul Sander 2025-02-03 21:28 20686
#if defined(__APPLE__)
1193b4be Poul Sander 2025-02-03 21:28 20687
    const char* args[] { "open", "--", path, NULL };
1193b4be Poul Sander 2025-02-03 21:28 20688
#else
1193b4be Poul Sander 2025-02-03 21:28 20689
    const char* args[] { "xdg-open", path, NULL };
1193b4be Poul Sander 2025-02-03 21:28 20690
#endif
1193b4be Poul Sander 2025-02-03 21:28 20691
    pid_t pid = fork();
1193b4be Poul Sander 2025-02-03 21:28 20692
    if (pid < 0)
1193b4be Poul Sander 2025-02-03 21:28 20693
        return false;
1193b4be Poul Sander 2025-02-03 21:28 20694
    if (!pid)
1193b4be Poul Sander 2025-02-03 21:28 20695
    {
1193b4be Poul Sander 2025-02-03 21:28 20696
        execvp(args[0], const_cast<char **>(args));
1193b4be Poul Sander 2025-02-03 21:28 20697
        exit(-1);
1193b4be Poul Sander 2025-02-03 21:28 20698
    }
1193b4be Poul Sander 2025-02-03 21:28 20699
    else
1193b4be Poul Sander 2025-02-03 21:28 20700
    {
1193b4be Poul Sander 2025-02-03 21:28 20701
        int status;
1193b4be Poul Sander 2025-02-03 21:28 20702
        waitpid(pid, &status, 0);
1193b4be Poul Sander 2025-02-03 21:28 20703
        return WEXITSTATUS(status) == 0;
1193b4be Poul Sander 2025-02-03 21:28 20704
    }
1193b4be Poul Sander 2025-02-03 21:28 20705
}
1193b4be Poul Sander 2025-02-03 21:28 20706
#endif
1193b4be Poul Sander 2025-02-03 21:28 20707
#else
1193b4be Poul Sander 2025-02-03 21:28 20708
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
1193b4be Poul Sander 2025-02-03 21:28 20709
#endif // Default shell handlers
1193b4be Poul Sander 2025-02-03 21:28 20710
1193b4be Poul Sander 2025-02-03 21:28 20711
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20712
1193b4be Poul Sander 2025-02-03 21:28 20713
// Win32 API IME support (for Asian languages, etc.)
1193b4be Poul Sander 2025-02-03 21:28 20714
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1193b4be Poul Sander 2025-02-03 21:28 20715
1193b4be Poul Sander 2025-02-03 21:28 20716
#include <imm.h>
1193b4be Poul Sander 2025-02-03 21:28 20717
#ifdef _MSC_VER
1193b4be Poul Sander 2025-02-03 21:28 20718
#pragma comment(lib, "imm32")
1193b4be Poul Sander 2025-02-03 21:28 20719
#endif
1193b4be Poul Sander 2025-02-03 21:28 20720
1193b4be Poul Sander 2025-02-03 21:28 20721
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
1193b4be Poul Sander 2025-02-03 21:28 20722
{
1193b4be Poul Sander 2025-02-03 21:28 20723
    // Notify OS Input Method Editor of text input position
1193b4be Poul Sander 2025-02-03 21:28 20724
    HWND hwnd = (HWND)viewport->PlatformHandleRaw;
1193b4be Poul Sander 2025-02-03 21:28 20725
    if (hwnd == 0)
1193b4be Poul Sander 2025-02-03 21:28 20726
        return;
1193b4be Poul Sander 2025-02-03 21:28 20727
1193b4be Poul Sander 2025-02-03 21:28 20728
    //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
1193b4be Poul Sander 2025-02-03 21:28 20729
    if (HIMC himc = ::ImmGetContext(hwnd))
1193b4be Poul Sander 2025-02-03 21:28 20730
    {
1193b4be Poul Sander 2025-02-03 21:28 20731
        COMPOSITIONFORM composition_form = {};
1193b4be Poul Sander 2025-02-03 21:28 20732
        composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
1193b4be Poul Sander 2025-02-03 21:28 20733
        composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
1193b4be Poul Sander 2025-02-03 21:28 20734
        composition_form.dwStyle = CFS_FORCE_POSITION;
1193b4be Poul Sander 2025-02-03 21:28 20735
        ::ImmSetCompositionWindow(himc, &composition_form);
1193b4be Poul Sander 2025-02-03 21:28 20736
        CANDIDATEFORM candidate_form = {};
1193b4be Poul Sander 2025-02-03 21:28 20737
        candidate_form.dwStyle = CFS_CANDIDATEPOS;
1193b4be Poul Sander 2025-02-03 21:28 20738
        candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
1193b4be Poul Sander 2025-02-03 21:28 20739
        candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
1193b4be Poul Sander 2025-02-03 21:28 20740
        ::ImmSetCandidateWindow(himc, &candidate_form);
1193b4be Poul Sander 2025-02-03 21:28 20741
        ::ImmReleaseContext(hwnd, himc);
1193b4be Poul Sander 2025-02-03 21:28 20742
    }
1193b4be Poul Sander 2025-02-03 21:28 20743
}
1193b4be Poul Sander 2025-02-03 21:28 20744
1193b4be Poul Sander 2025-02-03 21:28 20745
#else
1193b4be Poul Sander 2025-02-03 21:28 20746
1193b4be Poul Sander 2025-02-03 21:28 20747
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
1193b4be Poul Sander 2025-02-03 21:28 20748
1193b4be Poul Sander 2025-02-03 21:28 20749
#endif // Default IME handlers
1193b4be Poul Sander 2025-02-03 21:28 20750
1193b4be Poul Sander 2025-02-03 21:28 20751
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20752
// [SECTION] METRICS/DEBUGGER WINDOW
1193b4be Poul Sander 2025-02-03 21:28 20753
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20754
// - DebugRenderViewportThumbnail() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20755
// - RenderViewportsThumbnails() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20756
// - DebugTextEncoding()
1193b4be Poul Sander 2025-02-03 21:28 20757
// - MetricsHelpMarker() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20758
// - ShowFontAtlas() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20759
// - ShowMetricsWindow()
1193b4be Poul Sander 2025-02-03 21:28 20760
// - DebugNodeColumns() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20761
// - DebugNodeDockNode() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20762
// - DebugNodeDrawList() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20763
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20764
// - DebugNodeFont() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20765
// - DebugNodeFontGlyph() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20766
// - DebugNodeStorage() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20767
// - DebugNodeTabBar() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20768
// - DebugNodeViewport() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20769
// - DebugNodeWindow() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20770
// - DebugNodeWindowSettings() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20771
// - DebugNodeWindowsList() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20772
// - DebugNodeWindowsListByBeginStackParent() [Internal]
1193b4be Poul Sander 2025-02-03 21:28 20773
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 20774
1193b4be Poul Sander 2025-02-03 21:28 20775
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 20776
1193b4be Poul Sander 2025-02-03 21:28 20777
void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
1193b4be Poul Sander 2025-02-03 21:28 20778
{
1193b4be Poul Sander 2025-02-03 21:28 20779
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20780
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 20781
1193b4be Poul Sander 2025-02-03 21:28 20782
    ImVec2 scale = bb.GetSize() / viewport->Size;
1193b4be Poul Sander 2025-02-03 21:28 20783
    ImVec2 off = bb.Min - viewport->Pos * scale;
1193b4be Poul Sander 2025-02-03 21:28 20784
    float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f;
1193b4be Poul Sander 2025-02-03 21:28 20785
    window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
1193b4be Poul Sander 2025-02-03 21:28 20786
    for (ImGuiWindow* thumb_window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 20787
    {
1193b4be Poul Sander 2025-02-03 21:28 20788
        if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 20789
            continue;
1193b4be Poul Sander 2025-02-03 21:28 20790
        if (thumb_window->Viewport != viewport)
1193b4be Poul Sander 2025-02-03 21:28 20791
            continue;
1193b4be Poul Sander 2025-02-03 21:28 20792
1193b4be Poul Sander 2025-02-03 21:28 20793
        ImRect thumb_r = thumb_window->Rect();
1193b4be Poul Sander 2025-02-03 21:28 20794
        ImRect title_r = thumb_window->TitleBarRect();
1193b4be Poul Sander 2025-02-03 21:28 20795
        thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off +  thumb_r.Max * scale));
1193b4be Poul Sander 2025-02-03 21:28 20796
        title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off +  ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height
1193b4be Poul Sander 2025-02-03 21:28 20797
        thumb_r.ClipWithFull(bb);
1193b4be Poul Sander 2025-02-03 21:28 20798
        title_r.ClipWithFull(bb);
1193b4be Poul Sander 2025-02-03 21:28 20799
        const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
1193b4be Poul Sander 2025-02-03 21:28 20800
        window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
1193b4be Poul Sander 2025-02-03 21:28 20801
        window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
1193b4be Poul Sander 2025-02-03 21:28 20802
        window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
1193b4be Poul Sander 2025-02-03 21:28 20803
        window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
1193b4be Poul Sander 2025-02-03 21:28 20804
    }
1193b4be Poul Sander 2025-02-03 21:28 20805
    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
1193b4be Poul Sander 2025-02-03 21:28 20806
    if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
1193b4be Poul Sander 2025-02-03 21:28 20807
        window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 20808
}
1193b4be Poul Sander 2025-02-03 21:28 20809
1193b4be Poul Sander 2025-02-03 21:28 20810
static void RenderViewportsThumbnails()
1193b4be Poul Sander 2025-02-03 21:28 20811
{
1193b4be Poul Sander 2025-02-03 21:28 20812
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20813
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 20814
1193b4be Poul Sander 2025-02-03 21:28 20815
    // Draw monitor and calculate their boundaries
1193b4be Poul Sander 2025-02-03 21:28 20816
    float SCALE = 1.0f / 8.0f;
1193b4be Poul Sander 2025-02-03 21:28 20817
    ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 20818
    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
1193b4be Poul Sander 2025-02-03 21:28 20819
        bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize));
1193b4be Poul Sander 2025-02-03 21:28 20820
    ImVec2 p = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 20821
    ImVec2 off = p - bb_full.Min * SCALE;
1193b4be Poul Sander 2025-02-03 21:28 20822
    for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
1193b4be Poul Sander 2025-02-03 21:28 20823
    {
1193b4be Poul Sander 2025-02-03 21:28 20824
        ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE);
1193b4be Poul Sander 2025-02-03 21:28 20825
        window->DrawList->AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors.index_from_ptr(&monitor)) ? IM_COL32(255, 255, 0, 255) : ImGui::GetColorU32(ImGuiCol_Border), 4.0f);
1193b4be Poul Sander 2025-02-03 21:28 20826
        window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f);
1193b4be Poul Sander 2025-02-03 21:28 20827
    }
1193b4be Poul Sander 2025-02-03 21:28 20828
1193b4be Poul Sander 2025-02-03 21:28 20829
    // Draw viewports
1193b4be Poul Sander 2025-02-03 21:28 20830
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 20831
    {
1193b4be Poul Sander 2025-02-03 21:28 20832
        ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
1193b4be Poul Sander 2025-02-03 21:28 20833
        ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
1193b4be Poul Sander 2025-02-03 21:28 20834
    }
1193b4be Poul Sander 2025-02-03 21:28 20835
    ImGui::Dummy(bb_full.GetSize() * SCALE);
1193b4be Poul Sander 2025-02-03 21:28 20836
}
1193b4be Poul Sander 2025-02-03 21:28 20837
1193b4be Poul Sander 2025-02-03 21:28 20838
static int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs)
1193b4be Poul Sander 2025-02-03 21:28 20839
{
1193b4be Poul Sander 2025-02-03 21:28 20840
    const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs;
1193b4be Poul Sander 2025-02-03 21:28 20841
    const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs;
1193b4be Poul Sander 2025-02-03 21:28 20842
    return b->LastFocusedStampCount - a->LastFocusedStampCount;
1193b4be Poul Sander 2025-02-03 21:28 20843
}
1193b4be Poul Sander 2025-02-03 21:28 20844
1193b4be Poul Sander 2025-02-03 21:28 20845
// Draw an arbitrary US keyboard layout to visualize translated keys
1193b4be Poul Sander 2025-02-03 21:28 20846
void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
1193b4be Poul Sander 2025-02-03 21:28 20847
{
1193b4be Poul Sander 2025-02-03 21:28 20848
    const float scale = ImGui::GetFontSize() / 13.0f;
1193b4be Poul Sander 2025-02-03 21:28 20849
    const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
1193b4be Poul Sander 2025-02-03 21:28 20850
    const float  key_rounding = 3.0f * scale;
1193b4be Poul Sander 2025-02-03 21:28 20851
    const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
1193b4be Poul Sander 2025-02-03 21:28 20852
    const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
1193b4be Poul Sander 2025-02-03 21:28 20853
    const float  key_face_rounding = 2.0f * scale;
1193b4be Poul Sander 2025-02-03 21:28 20854
    const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
1193b4be Poul Sander 2025-02-03 21:28 20855
    const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 20856
    const float  key_row_offset = 9.0f * scale;
1193b4be Poul Sander 2025-02-03 21:28 20857
1193b4be Poul Sander 2025-02-03 21:28 20858
    ImVec2 board_min = GetCursorScreenPos();
1193b4be Poul Sander 2025-02-03 21:28 20859
    ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
1193b4be Poul Sander 2025-02-03 21:28 20860
    ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
1193b4be Poul Sander 2025-02-03 21:28 20861
1193b4be Poul Sander 2025-02-03 21:28 20862
    struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
1193b4be Poul Sander 2025-02-03 21:28 20863
    const KeyLayoutData keys_to_display[] =
1193b4be Poul Sander 2025-02-03 21:28 20864
    {
1193b4be Poul Sander 2025-02-03 21:28 20865
        { 0, 0, "", ImGuiKey_Tab },      { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
1193b4be Poul Sander 2025-02-03 21:28 20866
        { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
1193b4be Poul Sander 2025-02-03 21:28 20867
        { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
1193b4be Poul Sander 2025-02-03 21:28 20868
    };
1193b4be Poul Sander 2025-02-03 21:28 20869
1193b4be Poul Sander 2025-02-03 21:28 20870
    // Elements rendered manually via ImDrawList API are not clipped automatically.
1193b4be Poul Sander 2025-02-03 21:28 20871
    // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
1193b4be Poul Sander 2025-02-03 21:28 20872
    Dummy(board_max - board_min);
1193b4be Poul Sander 2025-02-03 21:28 20873
    if (!IsItemVisible())
1193b4be Poul Sander 2025-02-03 21:28 20874
        return;
1193b4be Poul Sander 2025-02-03 21:28 20875
    draw_list->PushClipRect(board_min, board_max, true);
1193b4be Poul Sander 2025-02-03 21:28 20876
    for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
1193b4be Poul Sander 2025-02-03 21:28 20877
    {
1193b4be Poul Sander 2025-02-03 21:28 20878
        const KeyLayoutData* key_data = &keys_to_display[n];
1193b4be Poul Sander 2025-02-03 21:28 20879
        ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
1193b4be Poul Sander 2025-02-03 21:28 20880
        ImVec2 key_max = key_min + key_size;
1193b4be Poul Sander 2025-02-03 21:28 20881
        draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
1193b4be Poul Sander 2025-02-03 21:28 20882
        draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
1193b4be Poul Sander 2025-02-03 21:28 20883
        ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
1193b4be Poul Sander 2025-02-03 21:28 20884
        ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
1193b4be Poul Sander 2025-02-03 21:28 20885
        draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 20886
        draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
1193b4be Poul Sander 2025-02-03 21:28 20887
        ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
1193b4be Poul Sander 2025-02-03 21:28 20888
        draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
1193b4be Poul Sander 2025-02-03 21:28 20889
        if (IsKeyDown(key_data->Key))
1193b4be Poul Sander 2025-02-03 21:28 20890
            draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
1193b4be Poul Sander 2025-02-03 21:28 20891
    }
1193b4be Poul Sander 2025-02-03 21:28 20892
    draw_list->PopClipRect();
1193b4be Poul Sander 2025-02-03 21:28 20893
}
1193b4be Poul Sander 2025-02-03 21:28 20894
1193b4be Poul Sander 2025-02-03 21:28 20895
// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
1193b4be Poul Sander 2025-02-03 21:28 20896
void ImGui::DebugTextEncoding(const char* str)
1193b4be Poul Sander 2025-02-03 21:28 20897
{
1193b4be Poul Sander 2025-02-03 21:28 20898
    Text("Text: \"%s\"", str);
1193b4be Poul Sander 2025-02-03 21:28 20899
    if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
1193b4be Poul Sander 2025-02-03 21:28 20900
        return;
1193b4be Poul Sander 2025-02-03 21:28 20901
    TableSetupColumn("Offset");
1193b4be Poul Sander 2025-02-03 21:28 20902
    TableSetupColumn("UTF-8");
1193b4be Poul Sander 2025-02-03 21:28 20903
    TableSetupColumn("Glyph");
1193b4be Poul Sander 2025-02-03 21:28 20904
    TableSetupColumn("Codepoint");
1193b4be Poul Sander 2025-02-03 21:28 20905
    TableHeadersRow();
1193b4be Poul Sander 2025-02-03 21:28 20906
    for (const char* p = str; *p != 0; )
1193b4be Poul Sander 2025-02-03 21:28 20907
    {
1193b4be Poul Sander 2025-02-03 21:28 20908
        unsigned int c;
1193b4be Poul Sander 2025-02-03 21:28 20909
        const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
1193b4be Poul Sander 2025-02-03 21:28 20910
        TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 20911
        Text("%d", (int)(p - str));
1193b4be Poul Sander 2025-02-03 21:28 20912
        TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 20913
        for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
1193b4be Poul Sander 2025-02-03 21:28 20914
        {
1193b4be Poul Sander 2025-02-03 21:28 20915
            if (byte_index > 0)
1193b4be Poul Sander 2025-02-03 21:28 20916
                SameLine();
1193b4be Poul Sander 2025-02-03 21:28 20917
            Text("0x%02X", (int)(unsigned char)p[byte_index]);
1193b4be Poul Sander 2025-02-03 21:28 20918
        }
1193b4be Poul Sander 2025-02-03 21:28 20919
        TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 20920
        if (GetFont()->FindGlyphNoFallback((ImWchar)c))
1193b4be Poul Sander 2025-02-03 21:28 20921
            TextUnformatted(p, p + c_utf8_len);
1193b4be Poul Sander 2025-02-03 21:28 20922
        else
1193b4be Poul Sander 2025-02-03 21:28 20923
            TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? "[invalid]" : "[missing]");
1193b4be Poul Sander 2025-02-03 21:28 20924
        TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 20925
        Text("U+%04X", (int)c);
1193b4be Poul Sander 2025-02-03 21:28 20926
        p += c_utf8_len;
1193b4be Poul Sander 2025-02-03 21:28 20927
    }
1193b4be Poul Sander 2025-02-03 21:28 20928
    EndTable();
1193b4be Poul Sander 2025-02-03 21:28 20929
}
1193b4be Poul Sander 2025-02-03 21:28 20930
1193b4be Poul Sander 2025-02-03 21:28 20931
static void DebugFlashStyleColorStop()
1193b4be Poul Sander 2025-02-03 21:28 20932
{
1193b4be Poul Sander 2025-02-03 21:28 20933
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20934
    if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
1193b4be Poul Sander 2025-02-03 21:28 20935
        g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
1193b4be Poul Sander 2025-02-03 21:28 20936
    g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
1193b4be Poul Sander 2025-02-03 21:28 20937
}
1193b4be Poul Sander 2025-02-03 21:28 20938
1193b4be Poul Sander 2025-02-03 21:28 20939
// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
1193b4be Poul Sander 2025-02-03 21:28 20940
void ImGui::DebugFlashStyleColor(ImGuiCol idx)
1193b4be Poul Sander 2025-02-03 21:28 20941
{
1193b4be Poul Sander 2025-02-03 21:28 20942
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20943
    DebugFlashStyleColorStop();
1193b4be Poul Sander 2025-02-03 21:28 20944
    g.DebugFlashStyleColorTime = 0.5f;
1193b4be Poul Sander 2025-02-03 21:28 20945
    g.DebugFlashStyleColorIdx = idx;
1193b4be Poul Sander 2025-02-03 21:28 20946
    g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
1193b4be Poul Sander 2025-02-03 21:28 20947
}
1193b4be Poul Sander 2025-02-03 21:28 20948
1193b4be Poul Sander 2025-02-03 21:28 20949
void ImGui::UpdateDebugToolFlashStyleColor()
1193b4be Poul Sander 2025-02-03 21:28 20950
{
1193b4be Poul Sander 2025-02-03 21:28 20951
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20952
    if (g.DebugFlashStyleColorTime <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 20953
        return;
1193b4be Poul Sander 2025-02-03 21:28 20954
    ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
1193b4be Poul Sander 2025-02-03 21:28 20955
    g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 20956
    if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
1193b4be Poul Sander 2025-02-03 21:28 20957
        DebugFlashStyleColorStop();
1193b4be Poul Sander 2025-02-03 21:28 20958
}
1193b4be Poul Sander 2025-02-03 21:28 20959
1193b4be Poul Sander 2025-02-03 21:28 20960
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
1193b4be Poul Sander 2025-02-03 21:28 20961
static void MetricsHelpMarker(const char* desc)
1193b4be Poul Sander 2025-02-03 21:28 20962
{
1193b4be Poul Sander 2025-02-03 21:28 20963
    ImGui::TextDisabled("(?)");
1193b4be Poul Sander 2025-02-03 21:28 20964
    if (ImGui::BeginItemTooltip())
1193b4be Poul Sander 2025-02-03 21:28 20965
    {
1193b4be Poul Sander 2025-02-03 21:28 20966
        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
1193b4be Poul Sander 2025-02-03 21:28 20967
        ImGui::TextUnformatted(desc);
1193b4be Poul Sander 2025-02-03 21:28 20968
        ImGui::PopTextWrapPos();
1193b4be Poul Sander 2025-02-03 21:28 20969
        ImGui::EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 20970
    }
1193b4be Poul Sander 2025-02-03 21:28 20971
}
1193b4be Poul Sander 2025-02-03 21:28 20972
1193b4be Poul Sander 2025-02-03 21:28 20973
// [DEBUG] List fonts in a font atlas and display its texture
1193b4be Poul Sander 2025-02-03 21:28 20974
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
1193b4be Poul Sander 2025-02-03 21:28 20975
{
1193b4be Poul Sander 2025-02-03 21:28 20976
    for (ImFont* font : atlas->Fonts)
1193b4be Poul Sander 2025-02-03 21:28 20977
    {
1193b4be Poul Sander 2025-02-03 21:28 20978
        PushID(font);
1193b4be Poul Sander 2025-02-03 21:28 20979
        DebugNodeFont(font);
1193b4be Poul Sander 2025-02-03 21:28 20980
        PopID();
1193b4be Poul Sander 2025-02-03 21:28 20981
    }
1193b4be Poul Sander 2025-02-03 21:28 20982
    if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
1193b4be Poul Sander 2025-02-03 21:28 20983
    {
1193b4be Poul Sander 2025-02-03 21:28 20984
        ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20985
        ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
1193b4be Poul Sander 2025-02-03 21:28 20986
        Checkbox("Tint with Text Color", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons
1193b4be Poul Sander 2025-02-03 21:28 20987
        ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 20988
        ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border);
1193b4be Poul Sander 2025-02-03 21:28 20989
        Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
1193b4be Poul Sander 2025-02-03 21:28 20990
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 20991
    }
1193b4be Poul Sander 2025-02-03 21:28 20992
}
1193b4be Poul Sander 2025-02-03 21:28 20993
1193b4be Poul Sander 2025-02-03 21:28 20994
void ImGui::ShowMetricsWindow(bool* p_open)
1193b4be Poul Sander 2025-02-03 21:28 20995
{
1193b4be Poul Sander 2025-02-03 21:28 20996
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 20997
    ImGuiIO& io = g.IO;
1193b4be Poul Sander 2025-02-03 21:28 20998
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
1193b4be Poul Sander 2025-02-03 21:28 20999
    if (cfg->ShowDebugLog)
1193b4be Poul Sander 2025-02-03 21:28 21000
        ShowDebugLogWindow(&cfg->ShowDebugLog);
1193b4be Poul Sander 2025-02-03 21:28 21001
    if (cfg->ShowIDStackTool)
1193b4be Poul Sander 2025-02-03 21:28 21002
        ShowIDStackToolWindow(&cfg->ShowIDStackTool);
1193b4be Poul Sander 2025-02-03 21:28 21003
1193b4be Poul Sander 2025-02-03 21:28 21004
    if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
1193b4be Poul Sander 2025-02-03 21:28 21005
    {
1193b4be Poul Sander 2025-02-03 21:28 21006
        End();
1193b4be Poul Sander 2025-02-03 21:28 21007
        return;
1193b4be Poul Sander 2025-02-03 21:28 21008
    }
1193b4be Poul Sander 2025-02-03 21:28 21009
1193b4be Poul Sander 2025-02-03 21:28 21010
    // [DEBUG] Clear debug breaks hooks after exactly one cycle.
1193b4be Poul Sander 2025-02-03 21:28 21011
    DebugBreakClearData();
1193b4be Poul Sander 2025-02-03 21:28 21012
1193b4be Poul Sander 2025-02-03 21:28 21013
    // Basic info
1193b4be Poul Sander 2025-02-03 21:28 21014
    Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
1193b4be Poul Sander 2025-02-03 21:28 21015
    if (g.ContextName[0] != 0)
1193b4be Poul Sander 2025-02-03 21:28 21016
    {
1193b4be Poul Sander 2025-02-03 21:28 21017
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21018
        Text("(Context Name: \"%s\")", g.ContextName);
1193b4be Poul Sander 2025-02-03 21:28 21019
    }
1193b4be Poul Sander 2025-02-03 21:28 21020
    Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
1193b4be Poul Sander 2025-02-03 21:28 21021
    Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
1193b4be Poul Sander 2025-02-03 21:28 21022
    Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
1193b4be Poul Sander 2025-02-03 21:28 21023
    //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
1193b4be Poul Sander 2025-02-03 21:28 21024
1193b4be Poul Sander 2025-02-03 21:28 21025
    Separator();
1193b4be Poul Sander 2025-02-03 21:28 21026
1193b4be Poul Sander 2025-02-03 21:28 21027
    // Debugging enums
1193b4be Poul Sander 2025-02-03 21:28 21028
    enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
1193b4be Poul Sander 2025-02-03 21:28 21029
    const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
1193b4be Poul Sander 2025-02-03 21:28 21030
    enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
1193b4be Poul Sander 2025-02-03 21:28 21031
    const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
1193b4be Poul Sander 2025-02-03 21:28 21032
    if (cfg->ShowWindowsRectsType < 0)
1193b4be Poul Sander 2025-02-03 21:28 21033
        cfg->ShowWindowsRectsType = WRT_WorkRect;
1193b4be Poul Sander 2025-02-03 21:28 21034
    if (cfg->ShowTablesRectsType < 0)
1193b4be Poul Sander 2025-02-03 21:28 21035
        cfg->ShowTablesRectsType = TRT_WorkRect;
1193b4be Poul Sander 2025-02-03 21:28 21036
1193b4be Poul Sander 2025-02-03 21:28 21037
    struct Funcs
1193b4be Poul Sander 2025-02-03 21:28 21038
    {
1193b4be Poul Sander 2025-02-03 21:28 21039
        static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
1193b4be Poul Sander 2025-02-03 21:28 21040
        {
1193b4be Poul Sander 2025-02-03 21:28 21041
            ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
1193b4be Poul Sander 2025-02-03 21:28 21042
            if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
1193b4be Poul Sander 2025-02-03 21:28 21043
            else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
1193b4be Poul Sander 2025-02-03 21:28 21044
            else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
1193b4be Poul Sander 2025-02-03 21:28 21045
            else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
1193b4be Poul Sander 2025-02-03 21:28 21046
            else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
1193b4be Poul Sander 2025-02-03 21:28 21047
            else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
1193b4be Poul Sander 2025-02-03 21:28 21048
            else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
1193b4be Poul Sander 2025-02-03 21:28 21049
            else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
1193b4be Poul Sander 2025-02-03 21:28 21050
            else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
1193b4be Poul Sander 2025-02-03 21:28 21051
            else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate
1193b4be Poul Sander 2025-02-03 21:28 21052
            else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }
1193b4be Poul Sander 2025-02-03 21:28 21053
            else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }
1193b4be Poul Sander 2025-02-03 21:28 21054
            else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
1193b4be Poul Sander 2025-02-03 21:28 21055
            IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 21056
            return ImRect();
1193b4be Poul Sander 2025-02-03 21:28 21057
        }
1193b4be Poul Sander 2025-02-03 21:28 21058
1193b4be Poul Sander 2025-02-03 21:28 21059
        static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
1193b4be Poul Sander 2025-02-03 21:28 21060
        {
1193b4be Poul Sander 2025-02-03 21:28 21061
            if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
1193b4be Poul Sander 2025-02-03 21:28 21062
            else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
1193b4be Poul Sander 2025-02-03 21:28 21063
            else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
1193b4be Poul Sander 2025-02-03 21:28 21064
            else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
1193b4be Poul Sander 2025-02-03 21:28 21065
            else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
1193b4be Poul Sander 2025-02-03 21:28 21066
            else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
1193b4be Poul Sander 2025-02-03 21:28 21067
            else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
1193b4be Poul Sander 2025-02-03 21:28 21068
            else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
1193b4be Poul Sander 2025-02-03 21:28 21069
            IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 21070
            return ImRect();
1193b4be Poul Sander 2025-02-03 21:28 21071
        }
1193b4be Poul Sander 2025-02-03 21:28 21072
    };
1193b4be Poul Sander 2025-02-03 21:28 21073
1193b4be Poul Sander 2025-02-03 21:28 21074
    // Tools
1193b4be Poul Sander 2025-02-03 21:28 21075
    if (TreeNode("Tools"))
1193b4be Poul Sander 2025-02-03 21:28 21076
    {
1193b4be Poul Sander 2025-02-03 21:28 21077
        // Debug Break features
1193b4be Poul Sander 2025-02-03 21:28 21078
        // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
1193b4be Poul Sander 2025-02-03 21:28 21079
        SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
1193b4be Poul Sander 2025-02-03 21:28 21080
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21081
        MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
1193b4be Poul Sander 2025-02-03 21:28 21082
        if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
1193b4be Poul Sander 2025-02-03 21:28 21083
            DebugStartItemPicker();
1193b4be Poul Sander 2025-02-03 21:28 21084
        Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
1193b4be Poul Sander 2025-02-03 21:28 21085
1193b4be Poul Sander 2025-02-03 21:28 21086
        SeparatorText("Visualize");
1193b4be Poul Sander 2025-02-03 21:28 21087
1193b4be Poul Sander 2025-02-03 21:28 21088
        Checkbox("Show Debug Log", &cfg->ShowDebugLog);
1193b4be Poul Sander 2025-02-03 21:28 21089
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21090
        MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
1193b4be Poul Sander 2025-02-03 21:28 21091
1193b4be Poul Sander 2025-02-03 21:28 21092
        Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
1193b4be Poul Sander 2025-02-03 21:28 21093
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21094
        MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
1193b4be Poul Sander 2025-02-03 21:28 21095
1193b4be Poul Sander 2025-02-03 21:28 21096
        Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
1193b4be Poul Sander 2025-02-03 21:28 21097
        Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
1193b4be Poul Sander 2025-02-03 21:28 21098
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21099
        SetNextItemWidth(GetFontSize() * 12);
1193b4be Poul Sander 2025-02-03 21:28 21100
        cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
1193b4be Poul Sander 2025-02-03 21:28 21101
        if (cfg->ShowWindowsRects && g.NavWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 21102
        {
1193b4be Poul Sander 2025-02-03 21:28 21103
            BulletText("'%s':", g.NavWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 21104
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21105
            for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
1193b4be Poul Sander 2025-02-03 21:28 21106
            {
1193b4be Poul Sander 2025-02-03 21:28 21107
                ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
1193b4be Poul Sander 2025-02-03 21:28 21108
                Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
1193b4be Poul Sander 2025-02-03 21:28 21109
            }
1193b4be Poul Sander 2025-02-03 21:28 21110
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21111
        }
1193b4be Poul Sander 2025-02-03 21:28 21112
1193b4be Poul Sander 2025-02-03 21:28 21113
        Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
1193b4be Poul Sander 2025-02-03 21:28 21114
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21115
        SetNextItemWidth(GetFontSize() * 12);
1193b4be Poul Sander 2025-02-03 21:28 21116
        cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
1193b4be Poul Sander 2025-02-03 21:28 21117
        if (cfg->ShowTablesRects && g.NavWindow != NULL)
1193b4be Poul Sander 2025-02-03 21:28 21118
        {
1193b4be Poul Sander 2025-02-03 21:28 21119
            for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
1193b4be Poul Sander 2025-02-03 21:28 21120
            {
1193b4be Poul Sander 2025-02-03 21:28 21121
                ImGuiTable* table = g.Tables.TryGetMapData(table_n);
1193b4be Poul Sander 2025-02-03 21:28 21122
                if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
1193b4be Poul Sander 2025-02-03 21:28 21123
                    continue;
1193b4be Poul Sander 2025-02-03 21:28 21124
1193b4be Poul Sander 2025-02-03 21:28 21125
                BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
1193b4be Poul Sander 2025-02-03 21:28 21126
                if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21127
                    GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 21128
                Indent();
1193b4be Poul Sander 2025-02-03 21:28 21129
                char buf[128];
1193b4be Poul Sander 2025-02-03 21:28 21130
                for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
1193b4be Poul Sander 2025-02-03 21:28 21131
                {
1193b4be Poul Sander 2025-02-03 21:28 21132
                    if (rect_n >= TRT_ColumnsRect)
1193b4be Poul Sander 2025-02-03 21:28 21133
                    {
1193b4be Poul Sander 2025-02-03 21:28 21134
                        if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
1193b4be Poul Sander 2025-02-03 21:28 21135
                            continue;
1193b4be Poul Sander 2025-02-03 21:28 21136
                        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
1193b4be Poul Sander 2025-02-03 21:28 21137
                        {
1193b4be Poul Sander 2025-02-03 21:28 21138
                            ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
1193b4be Poul Sander 2025-02-03 21:28 21139
                            ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
1193b4be Poul Sander 2025-02-03 21:28 21140
                            Selectable(buf);
1193b4be Poul Sander 2025-02-03 21:28 21141
                            if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21142
                                GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 21143
                        }
1193b4be Poul Sander 2025-02-03 21:28 21144
                    }
1193b4be Poul Sander 2025-02-03 21:28 21145
                    else
1193b4be Poul Sander 2025-02-03 21:28 21146
                    {
1193b4be Poul Sander 2025-02-03 21:28 21147
                        ImRect r = Funcs::GetTableRect(table, rect_n, -1);
1193b4be Poul Sander 2025-02-03 21:28 21148
                        ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
1193b4be Poul Sander 2025-02-03 21:28 21149
                        Selectable(buf);
1193b4be Poul Sander 2025-02-03 21:28 21150
                        if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21151
                            GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
1193b4be Poul Sander 2025-02-03 21:28 21152
                    }
1193b4be Poul Sander 2025-02-03 21:28 21153
                }
1193b4be Poul Sander 2025-02-03 21:28 21154
                Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21155
            }
1193b4be Poul Sander 2025-02-03 21:28 21156
        }
1193b4be Poul Sander 2025-02-03 21:28 21157
        Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
1193b4be Poul Sander 2025-02-03 21:28 21158
1193b4be Poul Sander 2025-02-03 21:28 21159
        SeparatorText("Validate");
1193b4be Poul Sander 2025-02-03 21:28 21160
1193b4be Poul Sander 2025-02-03 21:28 21161
        Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
1193b4be Poul Sander 2025-02-03 21:28 21162
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21163
        MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
1193b4be Poul Sander 2025-02-03 21:28 21164
1193b4be Poul Sander 2025-02-03 21:28 21165
        Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
1193b4be Poul Sander 2025-02-03 21:28 21166
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21167
        MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
1193b4be Poul Sander 2025-02-03 21:28 21168
        if (cfg->ShowTextEncodingViewer)
1193b4be Poul Sander 2025-02-03 21:28 21169
        {
1193b4be Poul Sander 2025-02-03 21:28 21170
            static char buf[64] = "";
1193b4be Poul Sander 2025-02-03 21:28 21171
            SetNextItemWidth(-FLT_MIN);
1193b4be Poul Sander 2025-02-03 21:28 21172
            InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
1193b4be Poul Sander 2025-02-03 21:28 21173
            if (buf[0] != 0)
1193b4be Poul Sander 2025-02-03 21:28 21174
                DebugTextEncoding(buf);
1193b4be Poul Sander 2025-02-03 21:28 21175
        }
1193b4be Poul Sander 2025-02-03 21:28 21176
1193b4be Poul Sander 2025-02-03 21:28 21177
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21178
    }
1193b4be Poul Sander 2025-02-03 21:28 21179
1193b4be Poul Sander 2025-02-03 21:28 21180
    // Windows
1193b4be Poul Sander 2025-02-03 21:28 21181
    if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
1193b4be Poul Sander 2025-02-03 21:28 21182
    {
1193b4be Poul Sander 2025-02-03 21:28 21183
        //SetNextItemOpen(true, ImGuiCond_Once);
1193b4be Poul Sander 2025-02-03 21:28 21184
        DebugNodeWindowsList(&g.Windows, "By display order");
1193b4be Poul Sander 2025-02-03 21:28 21185
        DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
1193b4be Poul Sander 2025-02-03 21:28 21186
        if (TreeNode("By submission order (begin stack)"))
1193b4be Poul Sander 2025-02-03 21:28 21187
        {
1193b4be Poul Sander 2025-02-03 21:28 21188
            // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
1193b4be Poul Sander 2025-02-03 21:28 21189
            ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
1193b4be Poul Sander 2025-02-03 21:28 21190
            temp_buffer.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 21191
            for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 21192
                if (window->LastFrameActive + 1 >= g.FrameCount)
1193b4be Poul Sander 2025-02-03 21:28 21193
                    temp_buffer.push_back(window);
1193b4be Poul Sander 2025-02-03 21:28 21194
            struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };
1193b4be Poul Sander 2025-02-03 21:28 21195
            ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
1193b4be Poul Sander 2025-02-03 21:28 21196
            DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
1193b4be Poul Sander 2025-02-03 21:28 21197
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21198
        }
1193b4be Poul Sander 2025-02-03 21:28 21199
1193b4be Poul Sander 2025-02-03 21:28 21200
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21201
    }
1193b4be Poul Sander 2025-02-03 21:28 21202
1193b4be Poul Sander 2025-02-03 21:28 21203
    // DrawLists
1193b4be Poul Sander 2025-02-03 21:28 21204
    int drawlist_count = 0;
1193b4be Poul Sander 2025-02-03 21:28 21205
    for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 21206
        drawlist_count += viewport->DrawDataP.CmdLists.Size;
1193b4be Poul Sander 2025-02-03 21:28 21207
    if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
1193b4be Poul Sander 2025-02-03 21:28 21208
    {
1193b4be Poul Sander 2025-02-03 21:28 21209
        Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
1193b4be Poul Sander 2025-02-03 21:28 21210
        Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
1193b4be Poul Sander 2025-02-03 21:28 21211
        for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 21212
        {
1193b4be Poul Sander 2025-02-03 21:28 21213
            bool viewport_has_drawlist = false;
1193b4be Poul Sander 2025-02-03 21:28 21214
            for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
1193b4be Poul Sander 2025-02-03 21:28 21215
            {
1193b4be Poul Sander 2025-02-03 21:28 21216
                if (!viewport_has_drawlist)
1193b4be Poul Sander 2025-02-03 21:28 21217
                    Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID);
1193b4be Poul Sander 2025-02-03 21:28 21218
                viewport_has_drawlist = true;
1193b4be Poul Sander 2025-02-03 21:28 21219
                DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
1193b4be Poul Sander 2025-02-03 21:28 21220
            }
1193b4be Poul Sander 2025-02-03 21:28 21221
        }
1193b4be Poul Sander 2025-02-03 21:28 21222
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21223
    }
1193b4be Poul Sander 2025-02-03 21:28 21224
1193b4be Poul Sander 2025-02-03 21:28 21225
    // Viewports
1193b4be Poul Sander 2025-02-03 21:28 21226
    if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
1193b4be Poul Sander 2025-02-03 21:28 21227
    {
1193b4be Poul Sander 2025-02-03 21:28 21228
        cfg->HighlightMonitorIdx = -1;
1193b4be Poul Sander 2025-02-03 21:28 21229
        bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
1193b4be Poul Sander 2025-02-03 21:28 21230
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21231
        MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors.");
1193b4be Poul Sander 2025-02-03 21:28 21232
        if (open)
1193b4be Poul Sander 2025-02-03 21:28 21233
        {
1193b4be Poul Sander 2025-02-03 21:28 21234
            for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
1193b4be Poul Sander 2025-02-03 21:28 21235
            {
1193b4be Poul Sander 2025-02-03 21:28 21236
                DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], "Monitor", i);
1193b4be Poul Sander 2025-02-03 21:28 21237
                if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21238
                    cfg->HighlightMonitorIdx = i;
1193b4be Poul Sander 2025-02-03 21:28 21239
            }
1193b4be Poul Sander 2025-02-03 21:28 21240
            DebugNodePlatformMonitor(&g.FallbackMonitor, "Fallback", 0);
1193b4be Poul Sander 2025-02-03 21:28 21241
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21242
        }
1193b4be Poul Sander 2025-02-03 21:28 21243
1193b4be Poul Sander 2025-02-03 21:28 21244
        SetNextItemOpen(true, ImGuiCond_Once);
1193b4be Poul Sander 2025-02-03 21:28 21245
        if (TreeNode("Windows Minimap"))
1193b4be Poul Sander 2025-02-03 21:28 21246
        {
1193b4be Poul Sander 2025-02-03 21:28 21247
            RenderViewportsThumbnails();
1193b4be Poul Sander 2025-02-03 21:28 21248
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21249
        }
1193b4be Poul Sander 2025-02-03 21:28 21250
        cfg->HighlightViewportID = 0;
1193b4be Poul Sander 2025-02-03 21:28 21251
1193b4be Poul Sander 2025-02-03 21:28 21252
        BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
1193b4be Poul Sander 2025-02-03 21:28 21253
        if (TreeNode("Inferred Z order (front-to-back)"))
1193b4be Poul Sander 2025-02-03 21:28 21254
        {
1193b4be Poul Sander 2025-02-03 21:28 21255
            static ImVector<ImGuiViewportP*> viewports;
1193b4be Poul Sander 2025-02-03 21:28 21256
            viewports.resize(g.Viewports.Size);
1193b4be Poul Sander 2025-02-03 21:28 21257
            memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes());
1193b4be Poul Sander 2025-02-03 21:28 21258
            if (viewports.Size > 1)
1193b4be Poul Sander 2025-02-03 21:28 21259
                ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount);
1193b4be Poul Sander 2025-02-03 21:28 21260
            for (ImGuiViewportP* viewport : viewports)
1193b4be Poul Sander 2025-02-03 21:28 21261
            {
1193b4be Poul Sander 2025-02-03 21:28 21262
                BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"",
1193b4be Poul Sander 2025-02-03 21:28 21263
                    viewport->Idx, viewport->ID, viewport->LastFocusedStampCount,
1193b4be Poul Sander 2025-02-03 21:28 21264
                    (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A",
1193b4be Poul Sander 2025-02-03 21:28 21265
                    viewport->Window ? viewport->Window->Name : "N/A");
1193b4be Poul Sander 2025-02-03 21:28 21266
                if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21267
                    cfg->HighlightViewportID = viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 21268
            }
1193b4be Poul Sander 2025-02-03 21:28 21269
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21270
        }
1193b4be Poul Sander 2025-02-03 21:28 21271
1193b4be Poul Sander 2025-02-03 21:28 21272
        for (ImGuiViewportP* viewport : g.Viewports)
1193b4be Poul Sander 2025-02-03 21:28 21273
            DebugNodeViewport(viewport);
1193b4be Poul Sander 2025-02-03 21:28 21274
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21275
    }
1193b4be Poul Sander 2025-02-03 21:28 21276
1193b4be Poul Sander 2025-02-03 21:28 21277
    // Details for Popups
1193b4be Poul Sander 2025-02-03 21:28 21278
    if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
1193b4be Poul Sander 2025-02-03 21:28 21279
    {
1193b4be Poul Sander 2025-02-03 21:28 21280
        for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
1193b4be Poul Sander 2025-02-03 21:28 21281
        {
1193b4be Poul Sander 2025-02-03 21:28 21282
            // As it's difficult to interact with tree nodes while popups are open, we display everything inline.
1193b4be Poul Sander 2025-02-03 21:28 21283
            ImGuiWindow* window = popup_data.Window;
1193b4be Poul Sander 2025-02-03 21:28 21284
            BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
1193b4be Poul Sander 2025-02-03 21:28 21285
                popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
1193b4be Poul Sander 2025-02-03 21:28 21286
                popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21287
        }
1193b4be Poul Sander 2025-02-03 21:28 21288
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21289
    }
1193b4be Poul Sander 2025-02-03 21:28 21290
1193b4be Poul Sander 2025-02-03 21:28 21291
    // Details for TabBars
1193b4be Poul Sander 2025-02-03 21:28 21292
    if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
1193b4be Poul Sander 2025-02-03 21:28 21293
    {
1193b4be Poul Sander 2025-02-03 21:28 21294
        for (int n = 0; n < g.TabBars.GetMapSize(); n++)
1193b4be Poul Sander 2025-02-03 21:28 21295
            if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
1193b4be Poul Sander 2025-02-03 21:28 21296
            {
1193b4be Poul Sander 2025-02-03 21:28 21297
                PushID(tab_bar);
1193b4be Poul Sander 2025-02-03 21:28 21298
                DebugNodeTabBar(tab_bar, "TabBar");
1193b4be Poul Sander 2025-02-03 21:28 21299
                PopID();
1193b4be Poul Sander 2025-02-03 21:28 21300
            }
1193b4be Poul Sander 2025-02-03 21:28 21301
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21302
    }
1193b4be Poul Sander 2025-02-03 21:28 21303
1193b4be Poul Sander 2025-02-03 21:28 21304
    // Details for Tables
1193b4be Poul Sander 2025-02-03 21:28 21305
    if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
1193b4be Poul Sander 2025-02-03 21:28 21306
    {
1193b4be Poul Sander 2025-02-03 21:28 21307
        for (int n = 0; n < g.Tables.GetMapSize(); n++)
1193b4be Poul Sander 2025-02-03 21:28 21308
            if (ImGuiTable* table = g.Tables.TryGetMapData(n))
1193b4be Poul Sander 2025-02-03 21:28 21309
                DebugNodeTable(table);
1193b4be Poul Sander 2025-02-03 21:28 21310
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21311
    }
1193b4be Poul Sander 2025-02-03 21:28 21312
1193b4be Poul Sander 2025-02-03 21:28 21313
    // Details for Fonts
1193b4be Poul Sander 2025-02-03 21:28 21314
    ImFontAtlas* atlas = g.IO.Fonts;
1193b4be Poul Sander 2025-02-03 21:28 21315
    if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
1193b4be Poul Sander 2025-02-03 21:28 21316
    {
1193b4be Poul Sander 2025-02-03 21:28 21317
        ShowFontAtlas(atlas);
1193b4be Poul Sander 2025-02-03 21:28 21318
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21319
    }
1193b4be Poul Sander 2025-02-03 21:28 21320
1193b4be Poul Sander 2025-02-03 21:28 21321
    // Details for InputText
1193b4be Poul Sander 2025-02-03 21:28 21322
    if (TreeNode("InputText"))
1193b4be Poul Sander 2025-02-03 21:28 21323
    {
1193b4be Poul Sander 2025-02-03 21:28 21324
        DebugNodeInputTextState(&g.InputTextState);
1193b4be Poul Sander 2025-02-03 21:28 21325
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21326
    }
1193b4be Poul Sander 2025-02-03 21:28 21327
1193b4be Poul Sander 2025-02-03 21:28 21328
    // Details for TypingSelect
1193b4be Poul Sander 2025-02-03 21:28 21329
    if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
1193b4be Poul Sander 2025-02-03 21:28 21330
    {
1193b4be Poul Sander 2025-02-03 21:28 21331
        DebugNodeTypingSelectState(&g.TypingSelectState);
1193b4be Poul Sander 2025-02-03 21:28 21332
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21333
    }
1193b4be Poul Sander 2025-02-03 21:28 21334
1193b4be Poul Sander 2025-02-03 21:28 21335
    // Details for MultiSelect
1193b4be Poul Sander 2025-02-03 21:28 21336
    if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
1193b4be Poul Sander 2025-02-03 21:28 21337
    {
1193b4be Poul Sander 2025-02-03 21:28 21338
        ImGuiBoxSelectState* bs = &g.BoxSelectState;
1193b4be Poul Sander 2025-02-03 21:28 21339
        BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
1193b4be Poul Sander 2025-02-03 21:28 21340
        for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
1193b4be Poul Sander 2025-02-03 21:28 21341
            if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
1193b4be Poul Sander 2025-02-03 21:28 21342
                DebugNodeMultiSelectState(state);
1193b4be Poul Sander 2025-02-03 21:28 21343
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21344
    }
1193b4be Poul Sander 2025-02-03 21:28 21345
1193b4be Poul Sander 2025-02-03 21:28 21346
    // Details for Docking
1193b4be Poul Sander 2025-02-03 21:28 21347
#ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21348
    if (TreeNode("Docking"))
1193b4be Poul Sander 2025-02-03 21:28 21349
    {
1193b4be Poul Sander 2025-02-03 21:28 21350
        static bool root_nodes_only = true;
1193b4be Poul Sander 2025-02-03 21:28 21351
        ImGuiDockContext* dc = &g.DockContext;
1193b4be Poul Sander 2025-02-03 21:28 21352
        Checkbox("List root nodes", &root_nodes_only);
1193b4be Poul Sander 2025-02-03 21:28 21353
        Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
1193b4be Poul Sander 2025-02-03 21:28 21354
        if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
1193b4be Poul Sander 2025-02-03 21:28 21355
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21356
        if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
1193b4be Poul Sander 2025-02-03 21:28 21357
        for (int n = 0; n < dc->Nodes.Data.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 21358
            if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
1193b4be Poul Sander 2025-02-03 21:28 21359
                if (!root_nodes_only || node->IsRootNode())
1193b4be Poul Sander 2025-02-03 21:28 21360
                    DebugNodeDockNode(node, "Node");
1193b4be Poul Sander 2025-02-03 21:28 21361
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21362
    }
1193b4be Poul Sander 2025-02-03 21:28 21363
#endif // #ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21364
1193b4be Poul Sander 2025-02-03 21:28 21365
    // Settings
1193b4be Poul Sander 2025-02-03 21:28 21366
    if (TreeNode("Settings"))
1193b4be Poul Sander 2025-02-03 21:28 21367
    {
1193b4be Poul Sander 2025-02-03 21:28 21368
        if (SmallButton("Clear"))
1193b4be Poul Sander 2025-02-03 21:28 21369
            ClearIniSettings();
1193b4be Poul Sander 2025-02-03 21:28 21370
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21371
        if (SmallButton("Save to memory"))
1193b4be Poul Sander 2025-02-03 21:28 21372
            SaveIniSettingsToMemory();
1193b4be Poul Sander 2025-02-03 21:28 21373
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21374
        if (SmallButton("Save to disk"))
1193b4be Poul Sander 2025-02-03 21:28 21375
            SaveIniSettingsToDisk(g.IO.IniFilename);
1193b4be Poul Sander 2025-02-03 21:28 21376
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21377
        if (g.IO.IniFilename)
1193b4be Poul Sander 2025-02-03 21:28 21378
            Text("\"%s\"", g.IO.IniFilename);
1193b4be Poul Sander 2025-02-03 21:28 21379
        else
1193b4be Poul Sander 2025-02-03 21:28 21380
            TextUnformatted("<NULL>");
1193b4be Poul Sander 2025-02-03 21:28 21381
        Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
1193b4be Poul Sander 2025-02-03 21:28 21382
        Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
1193b4be Poul Sander 2025-02-03 21:28 21383
        if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
1193b4be Poul Sander 2025-02-03 21:28 21384
        {
1193b4be Poul Sander 2025-02-03 21:28 21385
            for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
1193b4be Poul Sander 2025-02-03 21:28 21386
                BulletText("\"%s\"", handler.TypeName);
1193b4be Poul Sander 2025-02-03 21:28 21387
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21388
        }
1193b4be Poul Sander 2025-02-03 21:28 21389
        if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
1193b4be Poul Sander 2025-02-03 21:28 21390
        {
1193b4be Poul Sander 2025-02-03 21:28 21391
            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 21392
                DebugNodeWindowSettings(settings);
1193b4be Poul Sander 2025-02-03 21:28 21393
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21394
        }
1193b4be Poul Sander 2025-02-03 21:28 21395
1193b4be Poul Sander 2025-02-03 21:28 21396
        if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
1193b4be Poul Sander 2025-02-03 21:28 21397
        {
1193b4be Poul Sander 2025-02-03 21:28 21398
            for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 21399
                DebugNodeTableSettings(settings);
1193b4be Poul Sander 2025-02-03 21:28 21400
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21401
        }
1193b4be Poul Sander 2025-02-03 21:28 21402
1193b4be Poul Sander 2025-02-03 21:28 21403
#ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21404
        if (TreeNode("SettingsDocking", "Settings packed data: Docking"))
1193b4be Poul Sander 2025-02-03 21:28 21405
        {
1193b4be Poul Sander 2025-02-03 21:28 21406
            ImGuiDockContext* dc = &g.DockContext;
1193b4be Poul Sander 2025-02-03 21:28 21407
            Text("In SettingsWindows:");
1193b4be Poul Sander 2025-02-03 21:28 21408
            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
1193b4be Poul Sander 2025-02-03 21:28 21409
                if (settings->DockId != 0)
1193b4be Poul Sander 2025-02-03 21:28 21410
                    BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder);
1193b4be Poul Sander 2025-02-03 21:28 21411
            Text("In SettingsNodes:");
1193b4be Poul Sander 2025-02-03 21:28 21412
            for (int n = 0; n < dc->NodesSettings.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 21413
            {
1193b4be Poul Sander 2025-02-03 21:28 21414
                ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
1193b4be Poul Sander 2025-02-03 21:28 21415
                const char* selected_tab_name = NULL;
1193b4be Poul Sander 2025-02-03 21:28 21416
                if (settings->SelectedTabId)
1193b4be Poul Sander 2025-02-03 21:28 21417
                {
1193b4be Poul Sander 2025-02-03 21:28 21418
                    if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId))
1193b4be Poul Sander 2025-02-03 21:28 21419
                        selected_tab_name = window->Name;
1193b4be Poul Sander 2025-02-03 21:28 21420
                    else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId))
1193b4be Poul Sander 2025-02-03 21:28 21421
                        selected_tab_name = window_settings->GetName();
1193b4be Poul Sander 2025-02-03 21:28 21422
                }
1193b4be Poul Sander 2025-02-03 21:28 21423
                BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : "");
1193b4be Poul Sander 2025-02-03 21:28 21424
            }
1193b4be Poul Sander 2025-02-03 21:28 21425
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21426
        }
1193b4be Poul Sander 2025-02-03 21:28 21427
#endif // #ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21428
1193b4be Poul Sander 2025-02-03 21:28 21429
        if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
1193b4be Poul Sander 2025-02-03 21:28 21430
        {
1193b4be Poul Sander 2025-02-03 21:28 21431
            InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
1193b4be Poul Sander 2025-02-03 21:28 21432
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21433
        }
1193b4be Poul Sander 2025-02-03 21:28 21434
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21435
    }
1193b4be Poul Sander 2025-02-03 21:28 21436
1193b4be Poul Sander 2025-02-03 21:28 21437
    // Settings
1193b4be Poul Sander 2025-02-03 21:28 21438
    if (TreeNode("Memory allocations"))
1193b4be Poul Sander 2025-02-03 21:28 21439
    {
1193b4be Poul Sander 2025-02-03 21:28 21440
        ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
1193b4be Poul Sander 2025-02-03 21:28 21441
        Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
1193b4be Poul Sander 2025-02-03 21:28 21442
        if (SmallButton("GC now")) { g.GcCompactAll = true; }
1193b4be Poul Sander 2025-02-03 21:28 21443
        Text("Recent frames with allocations:");
1193b4be Poul Sander 2025-02-03 21:28 21444
        int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
1193b4be Poul Sander 2025-02-03 21:28 21445
        for (int n = buf_size - 1; n >= 0; n--)
1193b4be Poul Sander 2025-02-03 21:28 21446
        {
1193b4be Poul Sander 2025-02-03 21:28 21447
            ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
1193b4be Poul Sander 2025-02-03 21:28 21448
            BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
1193b4be Poul Sander 2025-02-03 21:28 21449
            if (n == 0)
1193b4be Poul Sander 2025-02-03 21:28 21450
            {
1193b4be Poul Sander 2025-02-03 21:28 21451
                SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21452
                Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 21453
            }
1193b4be Poul Sander 2025-02-03 21:28 21454
        }
1193b4be Poul Sander 2025-02-03 21:28 21455
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21456
    }
1193b4be Poul Sander 2025-02-03 21:28 21457
1193b4be Poul Sander 2025-02-03 21:28 21458
    if (TreeNode("Inputs"))
1193b4be Poul Sander 2025-02-03 21:28 21459
    {
1193b4be Poul Sander 2025-02-03 21:28 21460
        Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
1193b4be Poul Sander 2025-02-03 21:28 21461
        {
1193b4be Poul Sander 2025-02-03 21:28 21462
            // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.
1193b4be Poul Sander 2025-02-03 21:28 21463
            // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
1193b4be Poul Sander 2025-02-03 21:28 21464
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21465
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
1193b4be Poul Sander 2025-02-03 21:28 21466
            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
1193b4be Poul Sander 2025-02-03 21:28 21467
#else
1193b4be Poul Sander 2025-02-03 21:28 21468
            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
1193b4be Poul Sander 2025-02-03 21:28 21469
            //Text("Legacy raw:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
1193b4be Poul Sander 2025-02-03 21:28 21470
#endif
1193b4be Poul Sander 2025-02-03 21:28 21471
            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
1193b4be Poul Sander 2025-02-03 21:28 21472
            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
1193b4be Poul Sander 2025-02-03 21:28 21473
            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
1193b4be Poul Sander 2025-02-03 21:28 21474
            Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
1193b4be Poul Sander 2025-02-03 21:28 21475
            Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
1193b4be Poul Sander 2025-02-03 21:28 21476
            DebugRenderKeyboardPreview(GetWindowDrawList());
1193b4be Poul Sander 2025-02-03 21:28 21477
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21478
        }
1193b4be Poul Sander 2025-02-03 21:28 21479
1193b4be Poul Sander 2025-02-03 21:28 21480
        Text("MOUSE STATE");
1193b4be Poul Sander 2025-02-03 21:28 21481
        {
1193b4be Poul Sander 2025-02-03 21:28 21482
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21483
            if (IsMousePosValid())
1193b4be Poul Sander 2025-02-03 21:28 21484
                Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
1193b4be Poul Sander 2025-02-03 21:28 21485
            else
1193b4be Poul Sander 2025-02-03 21:28 21486
                Text("Mouse pos: <INVALID>");
1193b4be Poul Sander 2025-02-03 21:28 21487
            Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
1193b4be Poul Sander 2025-02-03 21:28 21488
            int count = IM_ARRAYSIZE(io.MouseDown);
1193b4be Poul Sander 2025-02-03 21:28 21489
            Text("Mouse down:");     for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
1193b4be Poul Sander 2025-02-03 21:28 21490
            Text("Mouse clicked:");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
1193b4be Poul Sander 2025-02-03 21:28 21491
            Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
1193b4be Poul Sander 2025-02-03 21:28 21492
            Text("Mouse wheel: %.1f", io.MouseWheel);
1193b4be Poul Sander 2025-02-03 21:28 21493
            Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
1193b4be Poul Sander 2025-02-03 21:28 21494
            Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
1193b4be Poul Sander 2025-02-03 21:28 21495
            Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
1193b4be Poul Sander 2025-02-03 21:28 21496
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21497
        }
1193b4be Poul Sander 2025-02-03 21:28 21498
1193b4be Poul Sander 2025-02-03 21:28 21499
        Text("MOUSE WHEELING");
1193b4be Poul Sander 2025-02-03 21:28 21500
        {
1193b4be Poul Sander 2025-02-03 21:28 21501
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21502
            Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21503
            Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
1193b4be Poul Sander 2025-02-03 21:28 21504
            Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
1193b4be Poul Sander 2025-02-03 21:28 21505
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21506
        }
1193b4be Poul Sander 2025-02-03 21:28 21507
1193b4be Poul Sander 2025-02-03 21:28 21508
        Text("KEY OWNERS");
1193b4be Poul Sander 2025-02-03 21:28 21509
        {
1193b4be Poul Sander 2025-02-03 21:28 21510
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21511
            if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
1193b4be Poul Sander 2025-02-03 21:28 21512
                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 21513
                {
1193b4be Poul Sander 2025-02-03 21:28 21514
                    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
1193b4be Poul Sander 2025-02-03 21:28 21515
                    if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
1193b4be Poul Sander 2025-02-03 21:28 21516
                        continue;
1193b4be Poul Sander 2025-02-03 21:28 21517
                    Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
1193b4be Poul Sander 2025-02-03 21:28 21518
                        owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
1193b4be Poul Sander 2025-02-03 21:28 21519
                    DebugLocateItemOnHover(owner_data->OwnerCurr);
1193b4be Poul Sander 2025-02-03 21:28 21520
                }
1193b4be Poul Sander 2025-02-03 21:28 21521
            EndChild();
1193b4be Poul Sander 2025-02-03 21:28 21522
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21523
        }
1193b4be Poul Sander 2025-02-03 21:28 21524
        Text("SHORTCUT ROUTING");
1193b4be Poul Sander 2025-02-03 21:28 21525
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21526
        MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
1193b4be Poul Sander 2025-02-03 21:28 21527
        {
1193b4be Poul Sander 2025-02-03 21:28 21528
            Indent();
1193b4be Poul Sander 2025-02-03 21:28 21529
            if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
1193b4be Poul Sander 2025-02-03 21:28 21530
                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
1193b4be Poul Sander 2025-02-03 21:28 21531
                {
1193b4be Poul Sander 2025-02-03 21:28 21532
                    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
1193b4be Poul Sander 2025-02-03 21:28 21533
                    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
1193b4be Poul Sander 2025-02-03 21:28 21534
                    {
1193b4be Poul Sander 2025-02-03 21:28 21535
                        ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
1193b4be Poul Sander 2025-02-03 21:28 21536
                        ImGuiKeyChord key_chord = key | routing_data->Mods;
1193b4be Poul Sander 2025-02-03 21:28 21537
                        Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
1193b4be Poul Sander 2025-02-03 21:28 21538
                        DebugLocateItemOnHover(routing_data->RoutingCurr);
1193b4be Poul Sander 2025-02-03 21:28 21539
                        if (g.IO.ConfigDebugIsDebuggerPresent)
1193b4be Poul Sander 2025-02-03 21:28 21540
                        {
1193b4be Poul Sander 2025-02-03 21:28 21541
                            SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21542
                            if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
1193b4be Poul Sander 2025-02-03 21:28 21543
                                g.DebugBreakInShortcutRouting = key_chord;
1193b4be Poul Sander 2025-02-03 21:28 21544
                        }
1193b4be Poul Sander 2025-02-03 21:28 21545
                        idx = routing_data->NextEntryIndex;
1193b4be Poul Sander 2025-02-03 21:28 21546
                    }
1193b4be Poul Sander 2025-02-03 21:28 21547
                }
1193b4be Poul Sander 2025-02-03 21:28 21548
            EndChild();
1193b4be Poul Sander 2025-02-03 21:28 21549
            Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
1193b4be Poul Sander 2025-02-03 21:28 21550
            Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21551
        }
1193b4be Poul Sander 2025-02-03 21:28 21552
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21553
    }
1193b4be Poul Sander 2025-02-03 21:28 21554
1193b4be Poul Sander 2025-02-03 21:28 21555
    if (TreeNode("Internal state"))
1193b4be Poul Sander 2025-02-03 21:28 21556
    {
1193b4be Poul Sander 2025-02-03 21:28 21557
        Text("WINDOWING");
1193b4be Poul Sander 2025-02-03 21:28 21558
        Indent();
1193b4be Poul Sander 2025-02-03 21:28 21559
        Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21560
        Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21561
        Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21562
        Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0);
1193b4be Poul Sander 2025-02-03 21:28 21563
        Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21564
        Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
1193b4be Poul Sander 2025-02-03 21:28 21565
        Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21566
1193b4be Poul Sander 2025-02-03 21:28 21567
        Text("ITEMS");
1193b4be Poul Sander 2025-02-03 21:28 21568
        Indent();
1193b4be Poul Sander 2025-02-03 21:28 21569
        Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
1193b4be Poul Sander 2025-02-03 21:28 21570
        DebugLocateItemOnHover(g.ActiveId);
1193b4be Poul Sander 2025-02-03 21:28 21571
        Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21572
        Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
1193b4be Poul Sander 2025-02-03 21:28 21573
        Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
1193b4be Poul Sander 2025-02-03 21:28 21574
        Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
1193b4be Poul Sander 2025-02-03 21:28 21575
        Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
1193b4be Poul Sander 2025-02-03 21:28 21576
        DebugLocateItemOnHover(g.DragDropPayload.SourceId);
1193b4be Poul Sander 2025-02-03 21:28 21577
        Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21578
1193b4be Poul Sander 2025-02-03 21:28 21579
        Text("NAV,FOCUS");
1193b4be Poul Sander 2025-02-03 21:28 21580
        Indent();
1193b4be Poul Sander 2025-02-03 21:28 21581
        Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21582
        Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
1193b4be Poul Sander 2025-02-03 21:28 21583
        DebugLocateItemOnHover(g.NavId);
1193b4be Poul Sander 2025-02-03 21:28 21584
        Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
1193b4be Poul Sander 2025-02-03 21:28 21585
        Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
1193b4be Poul Sander 2025-02-03 21:28 21586
        Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
1193b4be Poul Sander 2025-02-03 21:28 21587
        Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
1193b4be Poul Sander 2025-02-03 21:28 21588
        Text("NavActivateFlags: %04X", g.NavActivateFlags);
1193b4be Poul Sander 2025-02-03 21:28 21589
        Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
1193b4be Poul Sander 2025-02-03 21:28 21590
        Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
1193b4be Poul Sander 2025-02-03 21:28 21591
        Text("NavFocusRoute[] = ");
1193b4be Poul Sander 2025-02-03 21:28 21592
        for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
1193b4be Poul Sander 2025-02-03 21:28 21593
        {
1193b4be Poul Sander 2025-02-03 21:28 21594
            const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
1193b4be Poul Sander 2025-02-03 21:28 21595
            SameLine(0.0f, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 21596
            Text("0x%08X/", focus_scope.ID);
1193b4be Poul Sander 2025-02-03 21:28 21597
            SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
1193b4be Poul Sander 2025-02-03 21:28 21598
        }
1193b4be Poul Sander 2025-02-03 21:28 21599
        Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21600
        Unindent();
1193b4be Poul Sander 2025-02-03 21:28 21601
1193b4be Poul Sander 2025-02-03 21:28 21602
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21603
    }
1193b4be Poul Sander 2025-02-03 21:28 21604
1193b4be Poul Sander 2025-02-03 21:28 21605
    // Overlay: Display windows Rectangles and Begin Order
1193b4be Poul Sander 2025-02-03 21:28 21606
    if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
1193b4be Poul Sander 2025-02-03 21:28 21607
    {
1193b4be Poul Sander 2025-02-03 21:28 21608
        for (ImGuiWindow* window : g.Windows)
1193b4be Poul Sander 2025-02-03 21:28 21609
        {
1193b4be Poul Sander 2025-02-03 21:28 21610
            if (!window->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 21611
                continue;
1193b4be Poul Sander 2025-02-03 21:28 21612
            ImDrawList* draw_list = GetForegroundDrawList(window);
1193b4be Poul Sander 2025-02-03 21:28 21613
            if (cfg->ShowWindowsRects)
1193b4be Poul Sander 2025-02-03 21:28 21614
            {
1193b4be Poul Sander 2025-02-03 21:28 21615
                ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
1193b4be Poul Sander 2025-02-03 21:28 21616
                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
1193b4be Poul Sander 2025-02-03 21:28 21617
            }
1193b4be Poul Sander 2025-02-03 21:28 21618
            if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
1193b4be Poul Sander 2025-02-03 21:28 21619
            {
1193b4be Poul Sander 2025-02-03 21:28 21620
                char buf[32];
1193b4be Poul Sander 2025-02-03 21:28 21621
                ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
1193b4be Poul Sander 2025-02-03 21:28 21622
                float font_size = GetFontSize();
1193b4be Poul Sander 2025-02-03 21:28 21623
                draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
1193b4be Poul Sander 2025-02-03 21:28 21624
                draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
1193b4be Poul Sander 2025-02-03 21:28 21625
            }
1193b4be Poul Sander 2025-02-03 21:28 21626
        }
1193b4be Poul Sander 2025-02-03 21:28 21627
    }
1193b4be Poul Sander 2025-02-03 21:28 21628
1193b4be Poul Sander 2025-02-03 21:28 21629
    // Overlay: Display Tables Rectangles
1193b4be Poul Sander 2025-02-03 21:28 21630
    if (cfg->ShowTablesRects)
1193b4be Poul Sander 2025-02-03 21:28 21631
    {
1193b4be Poul Sander 2025-02-03 21:28 21632
        for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
1193b4be Poul Sander 2025-02-03 21:28 21633
        {
1193b4be Poul Sander 2025-02-03 21:28 21634
            ImGuiTable* table = g.Tables.TryGetMapData(table_n);
1193b4be Poul Sander 2025-02-03 21:28 21635
            if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
1193b4be Poul Sander 2025-02-03 21:28 21636
                continue;
1193b4be Poul Sander 2025-02-03 21:28 21637
            ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
1193b4be Poul Sander 2025-02-03 21:28 21638
            if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
1193b4be Poul Sander 2025-02-03 21:28 21639
            {
1193b4be Poul Sander 2025-02-03 21:28 21640
                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
1193b4be Poul Sander 2025-02-03 21:28 21641
                {
1193b4be Poul Sander 2025-02-03 21:28 21642
                    ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
1193b4be Poul Sander 2025-02-03 21:28 21643
                    ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
1193b4be Poul Sander 2025-02-03 21:28 21644
                    float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
1193b4be Poul Sander 2025-02-03 21:28 21645
                    draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
1193b4be Poul Sander 2025-02-03 21:28 21646
                }
1193b4be Poul Sander 2025-02-03 21:28 21647
            }
1193b4be Poul Sander 2025-02-03 21:28 21648
            else
1193b4be Poul Sander 2025-02-03 21:28 21649
            {
1193b4be Poul Sander 2025-02-03 21:28 21650
                ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
1193b4be Poul Sander 2025-02-03 21:28 21651
                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
1193b4be Poul Sander 2025-02-03 21:28 21652
            }
1193b4be Poul Sander 2025-02-03 21:28 21653
        }
1193b4be Poul Sander 2025-02-03 21:28 21654
    }
1193b4be Poul Sander 2025-02-03 21:28 21655
1193b4be Poul Sander 2025-02-03 21:28 21656
#ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21657
    // Overlay: Display Docking info
1193b4be Poul Sander 2025-02-03 21:28 21658
    if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode)
1193b4be Poul Sander 2025-02-03 21:28 21659
    {
1193b4be Poul Sander 2025-02-03 21:28 21660
        char buf[64] = "";
1193b4be Poul Sander 2025-02-03 21:28 21661
        char* p = buf;
1193b4be Poul Sander 2025-02-03 21:28 21662
        ImGuiDockNode* node = g.DebugHoveredDockNode;
1193b4be Poul Sander 2025-02-03 21:28 21663
        ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
1193b4be Poul Sander 2025-02-03 21:28 21664
        p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
1193b4be Poul Sander 2025-02-03 21:28 21665
        p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
1193b4be Poul Sander 2025-02-03 21:28 21666
        p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
1193b4be Poul Sander 2025-02-03 21:28 21667
        p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
1193b4be Poul Sander 2025-02-03 21:28 21668
        int depth = DockNodeGetDepth(node);
1193b4be Poul Sander 2025-02-03 21:28 21669
        overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255));
1193b4be Poul Sander 2025-02-03 21:28 21670
        ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
1193b4be Poul Sander 2025-02-03 21:28 21671
        overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
1193b4be Poul Sander 2025-02-03 21:28 21672
        overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
1193b4be Poul Sander 2025-02-03 21:28 21673
    }
1193b4be Poul Sander 2025-02-03 21:28 21674
#endif // #ifdef IMGUI_HAS_DOCK
1193b4be Poul Sander 2025-02-03 21:28 21675
1193b4be Poul Sander 2025-02-03 21:28 21676
    End();
1193b4be Poul Sander 2025-02-03 21:28 21677
}
1193b4be Poul Sander 2025-02-03 21:28 21678
1193b4be Poul Sander 2025-02-03 21:28 21679
void ImGui::DebugBreakClearData()
1193b4be Poul Sander 2025-02-03 21:28 21680
{
1193b4be Poul Sander 2025-02-03 21:28 21681
    // Those fields are scattered in their respective subsystem to stay in hot-data locations
1193b4be Poul Sander 2025-02-03 21:28 21682
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 21683
    g.DebugBreakInWindow = 0;
1193b4be Poul Sander 2025-02-03 21:28 21684
    g.DebugBreakInTable = 0;
1193b4be Poul Sander 2025-02-03 21:28 21685
    g.DebugBreakInShortcutRouting = ImGuiKey_None;
1193b4be Poul Sander 2025-02-03 21:28 21686
}
1193b4be Poul Sander 2025-02-03 21:28 21687
1193b4be Poul Sander 2025-02-03 21:28 21688
void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
1193b4be Poul Sander 2025-02-03 21:28 21689
{
1193b4be Poul Sander 2025-02-03 21:28 21690
    if (!BeginItemTooltip())
1193b4be Poul Sander 2025-02-03 21:28 21691
        return;
1193b4be Poul Sander 2025-02-03 21:28 21692
    Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
1193b4be Poul Sander 2025-02-03 21:28 21693
    Separator();
1193b4be Poul Sander 2025-02-03 21:28 21694
    TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
1193b4be Poul Sander 2025-02-03 21:28 21695
    Separator();
1193b4be Poul Sander 2025-02-03 21:28 21696
    TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
1193b4be Poul Sander 2025-02-03 21:28 21697
    EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 21698
}
1193b4be Poul Sander 2025-02-03 21:28 21699
1193b4be Poul Sander 2025-02-03 21:28 21700
// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
1193b4be Poul Sander 2025-02-03 21:28 21701
// In order to reduce interferences with the contents we are trying to debug into.
1193b4be Poul Sander 2025-02-03 21:28 21702
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
1193b4be Poul Sander 2025-02-03 21:28 21703
{
1193b4be Poul Sander 2025-02-03 21:28 21704
    ImGuiWindow* window = GetCurrentWindow();
1193b4be Poul Sander 2025-02-03 21:28 21705
    if (window->SkipItems)
1193b4be Poul Sander 2025-02-03 21:28 21706
        return false;
1193b4be Poul Sander 2025-02-03 21:28 21707
1193b4be Poul Sander 2025-02-03 21:28 21708
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 21709
    const ImGuiID id = window->GetID(label);
1193b4be Poul Sander 2025-02-03 21:28 21710
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
1193b4be Poul Sander 2025-02-03 21:28 21711
    ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
1193b4be Poul Sander 2025-02-03 21:28 21712
    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
1193b4be Poul Sander 2025-02-03 21:28 21713
1193b4be Poul Sander 2025-02-03 21:28 21714
    const ImRect bb(pos, pos + size);
1193b4be Poul Sander 2025-02-03 21:28 21715
    ItemSize(size, 0.0f);
1193b4be Poul Sander 2025-02-03 21:28 21716
    if (!ItemAdd(bb, id))
1193b4be Poul Sander 2025-02-03 21:28 21717
        return false;
1193b4be Poul Sander 2025-02-03 21:28 21718
1193b4be Poul Sander 2025-02-03 21:28 21719
    // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
1193b4be Poul Sander 2025-02-03 21:28 21720
    bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
1193b4be Poul Sander 2025-02-03 21:28 21721
    bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
1193b4be Poul Sander 2025-02-03 21:28 21722
    DebugBreakButtonTooltip(false, description_of_location);
1193b4be Poul Sander 2025-02-03 21:28 21723
1193b4be Poul Sander 2025-02-03 21:28 21724
    ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1193b4be Poul Sander 2025-02-03 21:28 21725
    ImVec4 hsv;
1193b4be Poul Sander 2025-02-03 21:28 21726
    ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
1193b4be Poul Sander 2025-02-03 21:28 21727
    ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
1193b4be Poul Sander 2025-02-03 21:28 21728
1193b4be Poul Sander 2025-02-03 21:28 21729
    RenderNavCursor(bb, id);
1193b4be Poul Sander 2025-02-03 21:28 21730
    RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
1193b4be Poul Sander 2025-02-03 21:28 21731
    RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
1193b4be Poul Sander 2025-02-03 21:28 21732
1193b4be Poul Sander 2025-02-03 21:28 21733
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
1193b4be Poul Sander 2025-02-03 21:28 21734
    return pressed;
1193b4be Poul Sander 2025-02-03 21:28 21735
}
1193b4be Poul Sander 2025-02-03 21:28 21736
1193b4be Poul Sander 2025-02-03 21:28 21737
// [DEBUG] Display contents of Columns
1193b4be Poul Sander 2025-02-03 21:28 21738
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
1193b4be Poul Sander 2025-02-03 21:28 21739
{
1193b4be Poul Sander 2025-02-03 21:28 21740
    if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
1193b4be Poul Sander 2025-02-03 21:28 21741
        return;
1193b4be Poul Sander 2025-02-03 21:28 21742
    BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
1193b4be Poul Sander 2025-02-03 21:28 21743
    for (ImGuiOldColumnData& column : columns->Columns)
1193b4be Poul Sander 2025-02-03 21:28 21744
        BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
1193b4be Poul Sander 2025-02-03 21:28 21745
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21746
}
1193b4be Poul Sander 2025-02-03 21:28 21747
1193b4be Poul Sander 2025-02-03 21:28 21748
static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled)
1193b4be Poul Sander 2025-02-03 21:28 21749
{
1193b4be Poul Sander 2025-02-03 21:28 21750
    using namespace ImGui;
1193b4be Poul Sander 2025-02-03 21:28 21751
    PushID(label);
1193b4be Poul Sander 2025-02-03 21:28 21752
    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
1193b4be Poul Sander 2025-02-03 21:28 21753
    Text("%s:", label);
1193b4be Poul Sander 2025-02-03 21:28 21754
    if (!enabled)
1193b4be Poul Sander 2025-02-03 21:28 21755
        BeginDisabled();
1193b4be Poul Sander 2025-02-03 21:28 21756
    CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize);
1193b4be Poul Sander 2025-02-03 21:28 21757
    CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX);
1193b4be Poul Sander 2025-02-03 21:28 21758
    CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY);
1193b4be Poul Sander 2025-02-03 21:28 21759
    CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar);
1193b4be Poul Sander 2025-02-03 21:28 21760
    CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar);
1193b4be Poul Sander 2025-02-03 21:28 21761
    CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton);
1193b4be Poul Sander 2025-02-03 21:28 21762
    CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton);
1193b4be Poul Sander 2025-02-03 21:28 21763
    CheckboxFlags("DockedWindowsInFocusRoute", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute);
1193b4be Poul Sander 2025-02-03 21:28 21764
    CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags
1193b4be Poul Sander 2025-02-03 21:28 21765
    CheckboxFlags("NoDockingSplit", p_flags, ImGuiDockNodeFlags_NoDockingSplit);
1193b4be Poul Sander 2025-02-03 21:28 21766
    CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther);
1193b4be Poul Sander 2025-02-03 21:28 21767
    CheckboxFlags("NoDockingOver", p_flags, ImGuiDockNodeFlags_NoDockingOverMe);
1193b4be Poul Sander 2025-02-03 21:28 21768
    CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther);
1193b4be Poul Sander 2025-02-03 21:28 21769
    CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);
1193b4be Poul Sander 2025-02-03 21:28 21770
    CheckboxFlags("NoUndocking", p_flags, ImGuiDockNodeFlags_NoUndocking);
1193b4be Poul Sander 2025-02-03 21:28 21771
    if (!enabled)
1193b4be Poul Sander 2025-02-03 21:28 21772
        EndDisabled();
1193b4be Poul Sander 2025-02-03 21:28 21773
    PopStyleVar();
1193b4be Poul Sander 2025-02-03 21:28 21774
    PopID();
1193b4be Poul Sander 2025-02-03 21:28 21775
}
1193b4be Poul Sander 2025-02-03 21:28 21776
1193b4be Poul Sander 2025-02-03 21:28 21777
// [DEBUG] Display contents of ImDockNode
1193b4be Poul Sander 2025-02-03 21:28 21778
void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 21779
{
1193b4be Poul Sander 2025-02-03 21:28 21780
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 21781
    const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2);    // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
1193b4be Poul Sander 2025-02-03 21:28 21782
    const bool is_active = (g.FrameCount - node->LastFrameActive < 2);  // Submitted
1193b4be Poul Sander 2025-02-03 21:28 21783
    if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
1193b4be Poul Sander 2025-02-03 21:28 21784
    bool open;
1193b4be Poul Sander 2025-02-03 21:28 21785
    ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 21786
    if (node->Windows.Size > 0)
1193b4be Poul Sander 2025-02-03 21:28 21787
        open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21788
    else
1193b4be Poul Sander 2025-02-03 21:28 21789
        open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal split" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical split" : "empty", node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 21790
    if (!is_alive) { PopStyleColor(); }
1193b4be Poul Sander 2025-02-03 21:28 21791
    if (is_active && IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21792
        if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)
1193b4be Poul Sander 2025-02-03 21:28 21793
            GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 21794
    if (open)
1193b4be Poul Sander 2025-02-03 21:28 21795
    {
1193b4be Poul Sander 2025-02-03 21:28 21796
        IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
1193b4be Poul Sander 2025-02-03 21:28 21797
        IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
1193b4be Poul Sander 2025-02-03 21:28 21798
        BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
1193b4be Poul Sander 2025-02-03 21:28 21799
            node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
1193b4be Poul Sander 2025-02-03 21:28 21800
        DebugNodeWindow(node->HostWindow, "HostWindow");
1193b4be Poul Sander 2025-02-03 21:28 21801
        DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
1193b4be Poul Sander 2025-02-03 21:28 21802
        BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
1193b4be Poul Sander 2025-02-03 21:28 21803
        BulletText("Misc:%s%s%s%s%s%s%s",
1193b4be Poul Sander 2025-02-03 21:28 21804
            node->IsDockSpace() ? " IsDockSpace" : "",
1193b4be Poul Sander 2025-02-03 21:28 21805
            node->IsCentralNode() ? " IsCentralNode" : "",
1193b4be Poul Sander 2025-02-03 21:28 21806
            is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "",
1193b4be Poul Sander 2025-02-03 21:28 21807
            node->WantLockSizeOnce ? " WantLockSizeOnce" : "",
1193b4be Poul Sander 2025-02-03 21:28 21808
            node->HasCentralNodeChild ? " HasCentralNodeChild" : "");
1193b4be Poul Sander 2025-02-03 21:28 21809
        if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))
1193b4be Poul Sander 2025-02-03 21:28 21810
        {
1193b4be Poul Sander 2025-02-03 21:28 21811
            if (BeginTable("flags", 4))
1193b4be Poul Sander 2025-02-03 21:28 21812
            {
1193b4be Poul Sander 2025-02-03 21:28 21813
                TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false);
1193b4be Poul Sander 2025-02-03 21:28 21814
                TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true);
1193b4be Poul Sander 2025-02-03 21:28 21815
                TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false);
1193b4be Poul Sander 2025-02-03 21:28 21816
                TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true);
1193b4be Poul Sander 2025-02-03 21:28 21817
                EndTable();
1193b4be Poul Sander 2025-02-03 21:28 21818
            }
1193b4be Poul Sander 2025-02-03 21:28 21819
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21820
        }
1193b4be Poul Sander 2025-02-03 21:28 21821
        if (node->ParentNode)
1193b4be Poul Sander 2025-02-03 21:28 21822
            DebugNodeDockNode(node->ParentNode, "ParentNode");
1193b4be Poul Sander 2025-02-03 21:28 21823
        if (node->ChildNodes[0])
1193b4be Poul Sander 2025-02-03 21:28 21824
            DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
1193b4be Poul Sander 2025-02-03 21:28 21825
        if (node->ChildNodes[1])
1193b4be Poul Sander 2025-02-03 21:28 21826
            DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
1193b4be Poul Sander 2025-02-03 21:28 21827
        if (node->TabBar)
1193b4be Poul Sander 2025-02-03 21:28 21828
            DebugNodeTabBar(node->TabBar, "TabBar");
1193b4be Poul Sander 2025-02-03 21:28 21829
        DebugNodeWindowsList(&node->Windows, "Windows");
1193b4be Poul Sander 2025-02-03 21:28 21830
1193b4be Poul Sander 2025-02-03 21:28 21831
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21832
    }
1193b4be Poul Sander 2025-02-03 21:28 21833
}
1193b4be Poul Sander 2025-02-03 21:28 21834
1193b4be Poul Sander 2025-02-03 21:28 21835
static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
1193b4be Poul Sander 2025-02-03 21:28 21836
{
1193b4be Poul Sander 2025-02-03 21:28 21837
    union { void* ptr; int integer; } tex_id_opaque;
1193b4be Poul Sander 2025-02-03 21:28 21838
    memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
1193b4be Poul Sander 2025-02-03 21:28 21839
    if (sizeof(tex_id) >= sizeof(void*))
1193b4be Poul Sander 2025-02-03 21:28 21840
        ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
1193b4be Poul Sander 2025-02-03 21:28 21841
    else
1193b4be Poul Sander 2025-02-03 21:28 21842
        ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
1193b4be Poul Sander 2025-02-03 21:28 21843
}
1193b4be Poul Sander 2025-02-03 21:28 21844
1193b4be Poul Sander 2025-02-03 21:28 21845
// [DEBUG] Display contents of ImDrawList
1193b4be Poul Sander 2025-02-03 21:28 21846
// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
1193b4be Poul Sander 2025-02-03 21:28 21847
void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 21848
{
1193b4be Poul Sander 2025-02-03 21:28 21849
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 21850
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
1193b4be Poul Sander 2025-02-03 21:28 21851
    int cmd_count = draw_list->CmdBuffer.Size;
1193b4be Poul Sander 2025-02-03 21:28 21852
    if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
1193b4be Poul Sander 2025-02-03 21:28 21853
        cmd_count--;
1193b4be Poul Sander 2025-02-03 21:28 21854
    bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
1193b4be Poul Sander 2025-02-03 21:28 21855
    if (draw_list == GetWindowDrawList())
1193b4be Poul Sander 2025-02-03 21:28 21856
    {
1193b4be Poul Sander 2025-02-03 21:28 21857
        SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21858
        TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
1193b4be Poul Sander 2025-02-03 21:28 21859
        if (node_open)
1193b4be Poul Sander 2025-02-03 21:28 21860
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21861
        return;
1193b4be Poul Sander 2025-02-03 21:28 21862
    }
1193b4be Poul Sander 2025-02-03 21:28 21863
1193b4be Poul Sander 2025-02-03 21:28 21864
    ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
1193b4be Poul Sander 2025-02-03 21:28 21865
    if (window && IsItemHovered() && fg_draw_list)
1193b4be Poul Sander 2025-02-03 21:28 21866
        fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 21867
    if (!node_open)
1193b4be Poul Sander 2025-02-03 21:28 21868
        return;
1193b4be Poul Sander 2025-02-03 21:28 21869
1193b4be Poul Sander 2025-02-03 21:28 21870
    if (window && !window->WasActive)
1193b4be Poul Sander 2025-02-03 21:28 21871
        TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
1193b4be Poul Sander 2025-02-03 21:28 21872
1193b4be Poul Sander 2025-02-03 21:28 21873
    for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
1193b4be Poul Sander 2025-02-03 21:28 21874
    {
1193b4be Poul Sander 2025-02-03 21:28 21875
        if (pcmd->UserCallback)
1193b4be Poul Sander 2025-02-03 21:28 21876
        {
1193b4be Poul Sander 2025-02-03 21:28 21877
            BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
1193b4be Poul Sander 2025-02-03 21:28 21878
            continue;
1193b4be Poul Sander 2025-02-03 21:28 21879
        }
1193b4be Poul Sander 2025-02-03 21:28 21880
1193b4be Poul Sander 2025-02-03 21:28 21881
        char texid_desc[20];
1193b4be Poul Sander 2025-02-03 21:28 21882
        FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId);
1193b4be Poul Sander 2025-02-03 21:28 21883
        char buf[300];
1193b4be Poul Sander 2025-02-03 21:28 21884
        ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
1193b4be Poul Sander 2025-02-03 21:28 21885
            pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
1193b4be Poul Sander 2025-02-03 21:28 21886
        bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
1193b4be Poul Sander 2025-02-03 21:28 21887
        if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
1193b4be Poul Sander 2025-02-03 21:28 21888
            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
1193b4be Poul Sander 2025-02-03 21:28 21889
        if (!pcmd_node_open)
1193b4be Poul Sander 2025-02-03 21:28 21890
            continue;
1193b4be Poul Sander 2025-02-03 21:28 21891
1193b4be Poul Sander 2025-02-03 21:28 21892
        // Calculate approximate coverage area (touched pixel count)
1193b4be Poul Sander 2025-02-03 21:28 21893
        // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
1193b4be Poul Sander 2025-02-03 21:28 21894
        const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
1193b4be Poul Sander 2025-02-03 21:28 21895
        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
1193b4be Poul Sander 2025-02-03 21:28 21896
        float total_area = 0.0f;
1193b4be Poul Sander 2025-02-03 21:28 21897
        for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
1193b4be Poul Sander 2025-02-03 21:28 21898
        {
1193b4be Poul Sander 2025-02-03 21:28 21899
            ImVec2 triangle[3];
1193b4be Poul Sander 2025-02-03 21:28 21900
            for (int n = 0; n < 3; n++, idx_n++)
1193b4be Poul Sander 2025-02-03 21:28 21901
                triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
1193b4be Poul Sander 2025-02-03 21:28 21902
            total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
1193b4be Poul Sander 2025-02-03 21:28 21903
        }
1193b4be Poul Sander 2025-02-03 21:28 21904
1193b4be Poul Sander 2025-02-03 21:28 21905
        // Display vertex information summary. Hover to get all triangles drawn in wire-frame
1193b4be Poul Sander 2025-02-03 21:28 21906
        ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
1193b4be Poul Sander 2025-02-03 21:28 21907
        Selectable(buf);
1193b4be Poul Sander 2025-02-03 21:28 21908
        if (IsItemHovered() && fg_draw_list)
1193b4be Poul Sander 2025-02-03 21:28 21909
            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
1193b4be Poul Sander 2025-02-03 21:28 21910
1193b4be Poul Sander 2025-02-03 21:28 21911
        // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
1193b4be Poul Sander 2025-02-03 21:28 21912
        ImGuiListClipper clipper;
1193b4be Poul Sander 2025-02-03 21:28 21913
        clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
1193b4be Poul Sander 2025-02-03 21:28 21914
        while (clipper.Step())
1193b4be Poul Sander 2025-02-03 21:28 21915
            for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
1193b4be Poul Sander 2025-02-03 21:28 21916
            {
1193b4be Poul Sander 2025-02-03 21:28 21917
                char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
1193b4be Poul Sander 2025-02-03 21:28 21918
                ImVec2 triangle[3];
1193b4be Poul Sander 2025-02-03 21:28 21919
                for (int n = 0; n < 3; n++, idx_i++)
1193b4be Poul Sander 2025-02-03 21:28 21920
                {
1193b4be Poul Sander 2025-02-03 21:28 21921
                    const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
1193b4be Poul Sander 2025-02-03 21:28 21922
                    triangle[n] = v.pos;
1193b4be Poul Sander 2025-02-03 21:28 21923
                    buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
1193b4be Poul Sander 2025-02-03 21:28 21924
                        (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
1193b4be Poul Sander 2025-02-03 21:28 21925
                }
1193b4be Poul Sander 2025-02-03 21:28 21926
1193b4be Poul Sander 2025-02-03 21:28 21927
                Selectable(buf, false);
1193b4be Poul Sander 2025-02-03 21:28 21928
                if (fg_draw_list && IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 21929
                {
1193b4be Poul Sander 2025-02-03 21:28 21930
                    ImDrawListFlags backup_flags = fg_draw_list->Flags;
1193b4be Poul Sander 2025-02-03 21:28 21931
                    fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
1193b4be Poul Sander 2025-02-03 21:28 21932
                    fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 21933
                    fg_draw_list->Flags = backup_flags;
1193b4be Poul Sander 2025-02-03 21:28 21934
                }
1193b4be Poul Sander 2025-02-03 21:28 21935
            }
1193b4be Poul Sander 2025-02-03 21:28 21936
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21937
    }
1193b4be Poul Sander 2025-02-03 21:28 21938
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 21939
}
1193b4be Poul Sander 2025-02-03 21:28 21940
1193b4be Poul Sander 2025-02-03 21:28 21941
// [DEBUG] Display mesh/aabb of a ImDrawCmd
1193b4be Poul Sander 2025-02-03 21:28 21942
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
1193b4be Poul Sander 2025-02-03 21:28 21943
{
1193b4be Poul Sander 2025-02-03 21:28 21944
    IM_ASSERT(show_mesh || show_aabb);
1193b4be Poul Sander 2025-02-03 21:28 21945
1193b4be Poul Sander 2025-02-03 21:28 21946
    // Draw wire-frame version of all triangles
1193b4be Poul Sander 2025-02-03 21:28 21947
    ImRect clip_rect = draw_cmd->ClipRect;
1193b4be Poul Sander 2025-02-03 21:28 21948
    ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1193b4be Poul Sander 2025-02-03 21:28 21949
    ImDrawListFlags backup_flags = out_draw_list->Flags;
1193b4be Poul Sander 2025-02-03 21:28 21950
    out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
1193b4be Poul Sander 2025-02-03 21:28 21951
    for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
1193b4be Poul Sander 2025-02-03 21:28 21952
    {
1193b4be Poul Sander 2025-02-03 21:28 21953
        ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
1193b4be Poul Sander 2025-02-03 21:28 21954
        ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
1193b4be Poul Sander 2025-02-03 21:28 21955
1193b4be Poul Sander 2025-02-03 21:28 21956
        ImVec2 triangle[3];
1193b4be Poul Sander 2025-02-03 21:28 21957
        for (int n = 0; n < 3; n++, idx_n++)
1193b4be Poul Sander 2025-02-03 21:28 21958
            vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
1193b4be Poul Sander 2025-02-03 21:28 21959
        if (show_mesh)
1193b4be Poul Sander 2025-02-03 21:28 21960
            out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
1193b4be Poul Sander 2025-02-03 21:28 21961
    }
1193b4be Poul Sander 2025-02-03 21:28 21962
    // Draw bounding boxes
1193b4be Poul Sander 2025-02-03 21:28 21963
    if (show_aabb)
1193b4be Poul Sander 2025-02-03 21:28 21964
    {
1193b4be Poul Sander 2025-02-03 21:28 21965
        out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
1193b4be Poul Sander 2025-02-03 21:28 21966
        out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
1193b4be Poul Sander 2025-02-03 21:28 21967
    }
1193b4be Poul Sander 2025-02-03 21:28 21968
    out_draw_list->Flags = backup_flags;
1193b4be Poul Sander 2025-02-03 21:28 21969
}
1193b4be Poul Sander 2025-02-03 21:28 21970
1193b4be Poul Sander 2025-02-03 21:28 21971
// [DEBUG] Display details for a single font, called by ShowStyleEditor().
1193b4be Poul Sander 2025-02-03 21:28 21972
void ImGui::DebugNodeFont(ImFont* font)
1193b4be Poul Sander 2025-02-03 21:28 21973
{
1193b4be Poul Sander 2025-02-03 21:28 21974
    bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
1193b4be Poul Sander 2025-02-03 21:28 21975
        font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
1193b4be Poul Sander 2025-02-03 21:28 21976
    SameLine();
1193b4be Poul Sander 2025-02-03 21:28 21977
    if (SmallButton("Set as default"))
1193b4be Poul Sander 2025-02-03 21:28 21978
        GetIO().FontDefault = font;
1193b4be Poul Sander 2025-02-03 21:28 21979
    if (!opened)
1193b4be Poul Sander 2025-02-03 21:28 21980
        return;
1193b4be Poul Sander 2025-02-03 21:28 21981
1193b4be Poul Sander 2025-02-03 21:28 21982
    // Display preview text
1193b4be Poul Sander 2025-02-03 21:28 21983
    PushFont(font);
1193b4be Poul Sander 2025-02-03 21:28 21984
    Text("The quick brown fox jumps over the lazy dog");
1193b4be Poul Sander 2025-02-03 21:28 21985
    PopFont();
1193b4be Poul Sander 2025-02-03 21:28 21986
1193b4be Poul Sander 2025-02-03 21:28 21987
    // Display details
1193b4be Poul Sander 2025-02-03 21:28 21988
    SetNextItemWidth(GetFontSize() * 8);
1193b4be Poul Sander 2025-02-03 21:28 21989
    DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
1193b4be Poul Sander 2025-02-03 21:28 21990
    SameLine(); MetricsHelpMarker(
1193b4be Poul Sander 2025-02-03 21:28 21991
        "Note that the default embedded font is NOT meant to be scaled.\n\n"
1193b4be Poul Sander 2025-02-03 21:28 21992
        "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
1193b4be Poul Sander 2025-02-03 21:28 21993
        "You may oversample them to get some flexibility with scaling. "
1193b4be Poul Sander 2025-02-03 21:28 21994
        "You can also render at multiple sizes and select which one to use at runtime.\n\n"
1193b4be Poul Sander 2025-02-03 21:28 21995
        "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
1193b4be Poul Sander 2025-02-03 21:28 21996
    Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
1193b4be Poul Sander 2025-02-03 21:28 21997
    char c_str[5];
1193b4be Poul Sander 2025-02-03 21:28 21998
    Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
1193b4be Poul Sander 2025-02-03 21:28 21999
    Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
1193b4be Poul Sander 2025-02-03 21:28 22000
    const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
1193b4be Poul Sander 2025-02-03 21:28 22001
    Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
1193b4be Poul Sander 2025-02-03 21:28 22002
    for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
1193b4be Poul Sander 2025-02-03 21:28 22003
        if (font->ConfigData)
1193b4be Poul Sander 2025-02-03 21:28 22004
            if (const ImFontConfig* cfg = &font->ConfigData[config_i])
1193b4be Poul Sander 2025-02-03 21:28 22005
                BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
1193b4be Poul Sander 2025-02-03 21:28 22006
                    config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
1193b4be Poul Sander 2025-02-03 21:28 22007
1193b4be Poul Sander 2025-02-03 21:28 22008
    // Display all glyphs of the fonts in separate pages of 256 characters
1193b4be Poul Sander 2025-02-03 21:28 22009
    if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
1193b4be Poul Sander 2025-02-03 21:28 22010
    {
1193b4be Poul Sander 2025-02-03 21:28 22011
        ImDrawList* draw_list = GetWindowDrawList();
1193b4be Poul Sander 2025-02-03 21:28 22012
        const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
1193b4be Poul Sander 2025-02-03 21:28 22013
        const float cell_size = font->FontSize * 1;
1193b4be Poul Sander 2025-02-03 21:28 22014
        const float cell_spacing = GetStyle().ItemSpacing.y;
1193b4be Poul Sander 2025-02-03 21:28 22015
        for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
1193b4be Poul Sander 2025-02-03 21:28 22016
        {
1193b4be Poul Sander 2025-02-03 21:28 22017
            // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
1193b4be Poul Sander 2025-02-03 21:28 22018
            // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
1193b4be Poul Sander 2025-02-03 21:28 22019
            // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
1193b4be Poul Sander 2025-02-03 21:28 22020
            if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
1193b4be Poul Sander 2025-02-03 21:28 22021
            {
1193b4be Poul Sander 2025-02-03 21:28 22022
                base += 4096 - 256;
1193b4be Poul Sander 2025-02-03 21:28 22023
                continue;
1193b4be Poul Sander 2025-02-03 21:28 22024
            }
1193b4be Poul Sander 2025-02-03 21:28 22025
1193b4be Poul Sander 2025-02-03 21:28 22026
            int count = 0;
1193b4be Poul Sander 2025-02-03 21:28 22027
            for (unsigned int n = 0; n < 256; n++)
1193b4be Poul Sander 2025-02-03 21:28 22028
                if (font->FindGlyphNoFallback((ImWchar)(base + n)))
1193b4be Poul Sander 2025-02-03 21:28 22029
                    count++;
1193b4be Poul Sander 2025-02-03 21:28 22030
            if (count <= 0)
1193b4be Poul Sander 2025-02-03 21:28 22031
                continue;
1193b4be Poul Sander 2025-02-03 21:28 22032
            if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
1193b4be Poul Sander 2025-02-03 21:28 22033
                continue;
1193b4be Poul Sander 2025-02-03 21:28 22034
1193b4be Poul Sander 2025-02-03 21:28 22035
            // Draw a 16x16 grid of glyphs
1193b4be Poul Sander 2025-02-03 21:28 22036
            ImVec2 base_pos = GetCursorScreenPos();
1193b4be Poul Sander 2025-02-03 21:28 22037
            for (unsigned int n = 0; n < 256; n++)
1193b4be Poul Sander 2025-02-03 21:28 22038
            {
1193b4be Poul Sander 2025-02-03 21:28 22039
                // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
1193b4be Poul Sander 2025-02-03 21:28 22040
                // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
1193b4be Poul Sander 2025-02-03 21:28 22041
                ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
1193b4be Poul Sander 2025-02-03 21:28 22042
                ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
1193b4be Poul Sander 2025-02-03 21:28 22043
                const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
1193b4be Poul Sander 2025-02-03 21:28 22044
                draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
1193b4be Poul Sander 2025-02-03 21:28 22045
                if (!glyph)
1193b4be Poul Sander 2025-02-03 21:28 22046
                    continue;
1193b4be Poul Sander 2025-02-03 21:28 22047
                font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
1193b4be Poul Sander 2025-02-03 21:28 22048
                if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
1193b4be Poul Sander 2025-02-03 21:28 22049
                {
1193b4be Poul Sander 2025-02-03 21:28 22050
                    DebugNodeFontGlyph(font, glyph);
1193b4be Poul Sander 2025-02-03 21:28 22051
                    EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 22052
                }
1193b4be Poul Sander 2025-02-03 21:28 22053
            }
1193b4be Poul Sander 2025-02-03 21:28 22054
            Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
1193b4be Poul Sander 2025-02-03 21:28 22055
            TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22056
        }
1193b4be Poul Sander 2025-02-03 21:28 22057
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22058
    }
1193b4be Poul Sander 2025-02-03 21:28 22059
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22060
}
1193b4be Poul Sander 2025-02-03 21:28 22061
1193b4be Poul Sander 2025-02-03 21:28 22062
void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph)
1193b4be Poul Sander 2025-02-03 21:28 22063
{
1193b4be Poul Sander 2025-02-03 21:28 22064
    Text("Codepoint: U+%04X", glyph->Codepoint);
1193b4be Poul Sander 2025-02-03 21:28 22065
    Separator();
1193b4be Poul Sander 2025-02-03 21:28 22066
    Text("Visible: %d", glyph->Visible);
1193b4be Poul Sander 2025-02-03 21:28 22067
    Text("AdvanceX: %.1f", glyph->AdvanceX);
1193b4be Poul Sander 2025-02-03 21:28 22068
    Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
1193b4be Poul Sander 2025-02-03 21:28 22069
    Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
1193b4be Poul Sander 2025-02-03 21:28 22070
}
1193b4be Poul Sander 2025-02-03 21:28 22071
1193b4be Poul Sander 2025-02-03 21:28 22072
// [DEBUG] Display contents of ImGuiStorage
1193b4be Poul Sander 2025-02-03 21:28 22073
void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 22074
{
1193b4be Poul Sander 2025-02-03 21:28 22075
    if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
1193b4be Poul Sander 2025-02-03 21:28 22076
        return;
1193b4be Poul Sander 2025-02-03 21:28 22077
    for (const ImGuiStoragePair& p : storage->Data)
1193b4be Poul Sander 2025-02-03 21:28 22078
    {
1193b4be Poul Sander 2025-02-03 21:28 22079
        BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
1193b4be Poul Sander 2025-02-03 21:28 22080
        DebugLocateItemOnHover(p.key);
1193b4be Poul Sander 2025-02-03 21:28 22081
    }
1193b4be Poul Sander 2025-02-03 21:28 22082
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22083
}
1193b4be Poul Sander 2025-02-03 21:28 22084
1193b4be Poul Sander 2025-02-03 21:28 22085
// [DEBUG] Display contents of ImGuiTabBar
1193b4be Poul Sander 2025-02-03 21:28 22086
void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 22087
{
1193b4be Poul Sander 2025-02-03 21:28 22088
    // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
1193b4be Poul Sander 2025-02-03 21:28 22089
    char buf[256];
1193b4be Poul Sander 2025-02-03 21:28 22090
    char* p = buf;
1193b4be Poul Sander 2025-02-03 21:28 22091
    const char* buf_end = buf + IM_ARRAYSIZE(buf);
1193b4be Poul Sander 2025-02-03 21:28 22092
    const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
1193b4be Poul Sander 2025-02-03 21:28 22093
    p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s  {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
1193b4be Poul Sander 2025-02-03 21:28 22094
    for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
1193b4be Poul Sander 2025-02-03 21:28 22095
    {
1193b4be Poul Sander 2025-02-03 21:28 22096
        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
1193b4be Poul Sander 2025-02-03 21:28 22097
        p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
1193b4be Poul Sander 2025-02-03 21:28 22098
    }
1193b4be Poul Sander 2025-02-03 21:28 22099
    p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
1193b4be Poul Sander 2025-02-03 21:28 22100
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
1193b4be Poul Sander 2025-02-03 21:28 22101
    bool open = TreeNode(label, "%s", buf);
1193b4be Poul Sander 2025-02-03 21:28 22102
    if (!is_active) { PopStyleColor(); }
1193b4be Poul Sander 2025-02-03 21:28 22103
    if (is_active && IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 22104
    {
1193b4be Poul Sander 2025-02-03 21:28 22105
        ImDrawList* draw_list = GetForegroundDrawList();
1193b4be Poul Sander 2025-02-03 21:28 22106
        draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 22107
        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 22108
        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 22109
    }
1193b4be Poul Sander 2025-02-03 21:28 22110
    if (open)
1193b4be Poul Sander 2025-02-03 21:28 22111
    {
1193b4be Poul Sander 2025-02-03 21:28 22112
        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
1193b4be Poul Sander 2025-02-03 21:28 22113
        {
1193b4be Poul Sander 2025-02-03 21:28 22114
            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
1193b4be Poul Sander 2025-02-03 21:28 22115
            PushID(tab);
1193b4be Poul Sander 2025-02-03 21:28 22116
            if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
1193b4be Poul Sander 2025-02-03 21:28 22117
            if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22118
            Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
1193b4be Poul Sander 2025-02-03 21:28 22119
                tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
1193b4be Poul Sander 2025-02-03 21:28 22120
            PopID();
1193b4be Poul Sander 2025-02-03 21:28 22121
        }
1193b4be Poul Sander 2025-02-03 21:28 22122
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22123
    }
1193b4be Poul Sander 2025-02-03 21:28 22124
}
1193b4be Poul Sander 2025-02-03 21:28 22125
1193b4be Poul Sander 2025-02-03 21:28 22126
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
1193b4be Poul Sander 2025-02-03 21:28 22127
{
1193b4be Poul Sander 2025-02-03 21:28 22128
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22129
    SetNextItemOpen(true, ImGuiCond_Once);
1193b4be Poul Sander 2025-02-03 21:28 22130
    bool open = TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A");
1193b4be Poul Sander 2025-02-03 21:28 22131
    if (IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 22132
        g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
1193b4be Poul Sander 2025-02-03 21:28 22133
    if (open)
1193b4be Poul Sander 2025-02-03 21:28 22134
    {
1193b4be Poul Sander 2025-02-03 21:28 22135
        ImGuiWindowFlags flags = viewport->Flags;
1193b4be Poul Sander 2025-02-03 21:28 22136
        BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
1193b4be Poul Sander 2025-02-03 21:28 22137
            viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
1193b4be Poul Sander 2025-02-03 21:28 22138
            viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y,
1193b4be Poul Sander 2025-02-03 21:28 22139
            viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
1193b4be Poul Sander 2025-02-03 21:28 22140
        if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } }
1193b4be Poul Sander 2025-02-03 21:28 22141
        BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags,
1193b4be Poul Sander 2025-02-03 21:28 22142
            //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard
1193b4be Poul Sander 2025-02-03 21:28 22143
            (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
1193b4be Poul Sander 2025-02-03 21:28 22144
            (flags & ImGuiViewportFlags_IsMinimized) ? " IsMinimized" : "",
1193b4be Poul Sander 2025-02-03 21:28 22145
            (flags & ImGuiViewportFlags_IsFocused) ? " IsFocused" : "",
1193b4be Poul Sander 2025-02-03 21:28 22146
            (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "",
1193b4be Poul Sander 2025-02-03 21:28 22147
            (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
1193b4be Poul Sander 2025-02-03 21:28 22148
            (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "",
1193b4be Poul Sander 2025-02-03 21:28 22149
            (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "",
1193b4be Poul Sander 2025-02-03 21:28 22150
            (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "",
1193b4be Poul Sander 2025-02-03 21:28 22151
            (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "",
1193b4be Poul Sander 2025-02-03 21:28 22152
            (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "",
1193b4be Poul Sander 2025-02-03 21:28 22153
            (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "",
1193b4be Poul Sander 2025-02-03 21:28 22154
            (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "",
1193b4be Poul Sander 2025-02-03 21:28 22155
            (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "");
1193b4be Poul Sander 2025-02-03 21:28 22156
        for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
1193b4be Poul Sander 2025-02-03 21:28 22157
            DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
1193b4be Poul Sander 2025-02-03 21:28 22158
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22159
    }
1193b4be Poul Sander 2025-02-03 21:28 22160
}
1193b4be Poul Sander 2025-02-03 21:28 22161
1193b4be Poul Sander 2025-02-03 21:28 22162
void ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx)
1193b4be Poul Sander 2025-02-03 21:28 22163
{
1193b4be Poul Sander 2025-02-03 21:28 22164
    BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)",
1193b4be Poul Sander 2025-02-03 21:28 22165
        label, idx, monitor->DpiScale * 100.0f,
1193b4be Poul Sander 2025-02-03 21:28 22166
        monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y,
1193b4be Poul Sander 2025-02-03 21:28 22167
        monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y);
1193b4be Poul Sander 2025-02-03 21:28 22168
}
1193b4be Poul Sander 2025-02-03 21:28 22169
1193b4be Poul Sander 2025-02-03 21:28 22170
void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 22171
{
1193b4be Poul Sander 2025-02-03 21:28 22172
    if (window == NULL)
1193b4be Poul Sander 2025-02-03 21:28 22173
    {
1193b4be Poul Sander 2025-02-03 21:28 22174
        BulletText("%s: NULL", label);
1193b4be Poul Sander 2025-02-03 21:28 22175
        return;
1193b4be Poul Sander 2025-02-03 21:28 22176
    }
1193b4be Poul Sander 2025-02-03 21:28 22177
1193b4be Poul Sander 2025-02-03 21:28 22178
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22179
    const bool is_active = window->WasActive;
1193b4be Poul Sander 2025-02-03 21:28 22180
    ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
1193b4be Poul Sander 2025-02-03 21:28 22181
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
1193b4be Poul Sander 2025-02-03 21:28 22182
    const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
1193b4be Poul Sander 2025-02-03 21:28 22183
    if (!is_active) { PopStyleColor(); }
1193b4be Poul Sander 2025-02-03 21:28 22184
    if (IsItemHovered() && is_active)
1193b4be Poul Sander 2025-02-03 21:28 22185
        GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
1193b4be Poul Sander 2025-02-03 21:28 22186
    if (!open)
1193b4be Poul Sander 2025-02-03 21:28 22187
        return;
1193b4be Poul Sander 2025-02-03 21:28 22188
1193b4be Poul Sander 2025-02-03 21:28 22189
    if (window->MemoryCompacted)
1193b4be Poul Sander 2025-02-03 21:28 22190
        TextDisabled("Note: some memory buffers have been compacted/freed.");
1193b4be Poul Sander 2025-02-03 21:28 22191
1193b4be Poul Sander 2025-02-03 21:28 22192
    if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
1193b4be Poul Sander 2025-02-03 21:28 22193
        g.DebugBreakInWindow = window->ID;
1193b4be Poul Sander 2025-02-03 21:28 22194
1193b4be Poul Sander 2025-02-03 21:28 22195
    ImGuiWindowFlags flags = window->Flags;
1193b4be Poul Sander 2025-02-03 21:28 22196
    DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
1193b4be Poul Sander 2025-02-03 21:28 22197
    BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
1193b4be Poul Sander 2025-02-03 21:28 22198
    BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
1193b4be Poul Sander 2025-02-03 21:28 22199
        (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
1193b4be Poul Sander 2025-02-03 21:28 22200
        (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
1193b4be Poul Sander 2025-02-03 21:28 22201
        (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
1193b4be Poul Sander 2025-02-03 21:28 22202
    if (flags & ImGuiWindowFlags_ChildWindow)
1193b4be Poul Sander 2025-02-03 21:28 22203
        BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
1193b4be Poul Sander 2025-02-03 21:28 22204
            (window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "",
1193b4be Poul Sander 2025-02-03 21:28 22205
            (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
1193b4be Poul Sander 2025-02-03 21:28 22206
            (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
1193b4be Poul Sander 2025-02-03 21:28 22207
            (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
1193b4be Poul Sander 2025-02-03 21:28 22208
    BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
1193b4be Poul Sander 2025-02-03 21:28 22209
    BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
1193b4be Poul Sander 2025-02-03 21:28 22210
    BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
1193b4be Poul Sander 2025-02-03 21:28 22211
    BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
1193b4be Poul Sander 2025-02-03 21:28 22212
    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
1193b4be Poul Sander 2025-02-03 21:28 22213
    {
1193b4be Poul Sander 2025-02-03 21:28 22214
        ImRect r = window->NavRectRel[layer];
1193b4be Poul Sander 2025-02-03 21:28 22215
        if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
1193b4be Poul Sander 2025-02-03 21:28 22216
            BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
1193b4be Poul Sander 2025-02-03 21:28 22217
        else
1193b4be Poul Sander 2025-02-03 21:28 22218
            BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
1193b4be Poul Sander 2025-02-03 21:28 22219
        DebugLocateItemOnHover(window->NavLastIds[layer]);
1193b4be Poul Sander 2025-02-03 21:28 22220
    }
1193b4be Poul Sander 2025-02-03 21:28 22221
    const ImVec2* pr = window->NavPreferredScoringPosRel;
1193b4be Poul Sander 2025-02-03 21:28 22222
    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
1193b4be Poul Sander 2025-02-03 21:28 22223
        BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
1193b4be Poul Sander 2025-02-03 21:28 22224
    BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
1193b4be Poul Sander 2025-02-03 21:28 22225
1193b4be Poul Sander 2025-02-03 21:28 22226
    BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y);
1193b4be Poul Sander 2025-02-03 21:28 22227
    BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
1193b4be Poul Sander 2025-02-03 21:28 22228
    BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
1193b4be Poul Sander 2025-02-03 21:28 22229
    if (window->DockNode || window->DockNodeAsHost)
1193b4be Poul Sander 2025-02-03 21:28 22230
        DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
1193b4be Poul Sander 2025-02-03 21:28 22231
1193b4be Poul Sander 2025-02-03 21:28 22232
    if (window->RootWindow != window)               { DebugNodeWindow(window->RootWindow, "RootWindow"); }
1193b4be Poul Sander 2025-02-03 21:28 22233
    if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); }
1193b4be Poul Sander 2025-02-03 21:28 22234
    if (window->ParentWindow != NULL)               { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
1193b4be Poul Sander 2025-02-03 21:28 22235
    if (window->ParentWindowForFocusRoute != NULL)  { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
1193b4be Poul Sander 2025-02-03 21:28 22236
    if (window->DC.ChildWindows.Size > 0)           { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
1193b4be Poul Sander 2025-02-03 21:28 22237
    if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
1193b4be Poul Sander 2025-02-03 21:28 22238
    {
1193b4be Poul Sander 2025-02-03 21:28 22239
        for (ImGuiOldColumns& columns : window->ColumnsStorage)
1193b4be Poul Sander 2025-02-03 21:28 22240
            DebugNodeColumns(&columns);
1193b4be Poul Sander 2025-02-03 21:28 22241
        TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22242
    }
1193b4be Poul Sander 2025-02-03 21:28 22243
    DebugNodeStorage(&window->StateStorage, "Storage");
1193b4be Poul Sander 2025-02-03 21:28 22244
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22245
}
1193b4be Poul Sander 2025-02-03 21:28 22246
1193b4be Poul Sander 2025-02-03 21:28 22247
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
1193b4be Poul Sander 2025-02-03 21:28 22248
{
1193b4be Poul Sander 2025-02-03 21:28 22249
    if (settings->WantDelete)
1193b4be Poul Sander 2025-02-03 21:28 22250
        BeginDisabled();
1193b4be Poul Sander 2025-02-03 21:28 22251
    Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
1193b4be Poul Sander 2025-02-03 21:28 22252
        settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
1193b4be Poul Sander 2025-02-03 21:28 22253
    if (settings->WantDelete)
1193b4be Poul Sander 2025-02-03 21:28 22254
        EndDisabled();
1193b4be Poul Sander 2025-02-03 21:28 22255
}
1193b4be Poul Sander 2025-02-03 21:28 22256
1193b4be Poul Sander 2025-02-03 21:28 22257
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
1193b4be Poul Sander 2025-02-03 21:28 22258
{
1193b4be Poul Sander 2025-02-03 21:28 22259
    if (!TreeNode(label, "%s (%d)", label, windows->Size))
1193b4be Poul Sander 2025-02-03 21:28 22260
        return;
1193b4be Poul Sander 2025-02-03 21:28 22261
    for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
1193b4be Poul Sander 2025-02-03 21:28 22262
    {
1193b4be Poul Sander 2025-02-03 21:28 22263
        PushID((*windows)[i]);
1193b4be Poul Sander 2025-02-03 21:28 22264
        DebugNodeWindow((*windows)[i], "Window");
1193b4be Poul Sander 2025-02-03 21:28 22265
        PopID();
1193b4be Poul Sander 2025-02-03 21:28 22266
    }
1193b4be Poul Sander 2025-02-03 21:28 22267
    TreePop();
1193b4be Poul Sander 2025-02-03 21:28 22268
}
1193b4be Poul Sander 2025-02-03 21:28 22269
1193b4be Poul Sander 2025-02-03 21:28 22270
// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
1193b4be Poul Sander 2025-02-03 21:28 22271
void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
1193b4be Poul Sander 2025-02-03 21:28 22272
{
1193b4be Poul Sander 2025-02-03 21:28 22273
    for (int i = 0; i < windows_size; i++)
1193b4be Poul Sander 2025-02-03 21:28 22274
    {
1193b4be Poul Sander 2025-02-03 21:28 22275
        ImGuiWindow* window = windows[i];
1193b4be Poul Sander 2025-02-03 21:28 22276
        if (window->ParentWindowInBeginStack != parent_in_begin_stack)
1193b4be Poul Sander 2025-02-03 21:28 22277
            continue;
1193b4be Poul Sander 2025-02-03 21:28 22278
        char buf[20];
1193b4be Poul Sander 2025-02-03 21:28 22279
        ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
1193b4be Poul Sander 2025-02-03 21:28 22280
        //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
1193b4be Poul Sander 2025-02-03 21:28 22281
        DebugNodeWindow(window, buf);
1193b4be Poul Sander 2025-02-03 21:28 22282
        Indent();
1193b4be Poul Sander 2025-02-03 21:28 22283
        DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
1193b4be Poul Sander 2025-02-03 21:28 22284
        Unindent();
1193b4be Poul Sander 2025-02-03 21:28 22285
    }
1193b4be Poul Sander 2025-02-03 21:28 22286
}
1193b4be Poul Sander 2025-02-03 21:28 22287
1193b4be Poul Sander 2025-02-03 21:28 22288
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22289
// [SECTION] DEBUG LOG WINDOW
1193b4be Poul Sander 2025-02-03 21:28 22290
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22291
1193b4be Poul Sander 2025-02-03 21:28 22292
void ImGui::DebugLog(const char* fmt, ...)
1193b4be Poul Sander 2025-02-03 21:28 22293
{
1193b4be Poul Sander 2025-02-03 21:28 22294
    va_list args;
1193b4be Poul Sander 2025-02-03 21:28 22295
    va_start(args, fmt);
1193b4be Poul Sander 2025-02-03 21:28 22296
    DebugLogV(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 22297
    va_end(args);
1193b4be Poul Sander 2025-02-03 21:28 22298
}
1193b4be Poul Sander 2025-02-03 21:28 22299
1193b4be Poul Sander 2025-02-03 21:28 22300
void ImGui::DebugLogV(const char* fmt, va_list args)
1193b4be Poul Sander 2025-02-03 21:28 22301
{
1193b4be Poul Sander 2025-02-03 21:28 22302
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22303
    const int old_size = g.DebugLogBuf.size();
1193b4be Poul Sander 2025-02-03 21:28 22304
    if (g.ContextName[0] != 0)
1193b4be Poul Sander 2025-02-03 21:28 22305
        g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 22306
    else
1193b4be Poul Sander 2025-02-03 21:28 22307
        g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
1193b4be Poul Sander 2025-02-03 21:28 22308
    g.DebugLogBuf.appendfv(fmt, args);
1193b4be Poul Sander 2025-02-03 21:28 22309
    g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
1193b4be Poul Sander 2025-02-03 21:28 22310
    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
1193b4be Poul Sander 2025-02-03 21:28 22311
        IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
1193b4be Poul Sander 2025-02-03 21:28 22312
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 22313
    // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
1193b4be Poul Sander 2025-02-03 21:28 22314
    const int new_size = g.DebugLogBuf.size();
1193b4be Poul Sander 2025-02-03 21:28 22315
    const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
1193b4be Poul Sander 2025-02-03 21:28 22316
    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
1193b4be Poul Sander 2025-02-03 21:28 22317
        IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size);
1193b4be Poul Sander 2025-02-03 21:28 22318
#endif
1193b4be Poul Sander 2025-02-03 21:28 22319
}
1193b4be Poul Sander 2025-02-03 21:28 22320
1193b4be Poul Sander 2025-02-03 21:28 22321
// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
1193b4be Poul Sander 2025-02-03 21:28 22322
static void SameLineOrWrap(const ImVec2& size)
1193b4be Poul Sander 2025-02-03 21:28 22323
{
1193b4be Poul Sander 2025-02-03 21:28 22324
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22325
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 22326
    ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
1193b4be Poul Sander 2025-02-03 21:28 22327
    if (window->WorkRect.Contains(ImRect(pos, pos + size)))
1193b4be Poul Sander 2025-02-03 21:28 22328
        ImGui::SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22329
}
1193b4be Poul Sander 2025-02-03 21:28 22330
1193b4be Poul Sander 2025-02-03 21:28 22331
static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
1193b4be Poul Sander 2025-02-03 21:28 22332
{
1193b4be Poul Sander 2025-02-03 21:28 22333
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22334
    ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
1193b4be Poul Sander 2025-02-03 21:28 22335
    SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
1193b4be Poul Sander 2025-02-03 21:28 22336
1193b4be Poul Sander 2025-02-03 21:28 22337
    bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
1193b4be Poul Sander 2025-02-03 21:28 22338
    if (highlight_errors)
1193b4be Poul Sander 2025-02-03 21:28 22339
        ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
1193b4be Poul Sander 2025-02-03 21:28 22340
    if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
1193b4be Poul Sander 2025-02-03 21:28 22341
    {
1193b4be Poul Sander 2025-02-03 21:28 22342
        g.DebugLogAutoDisableFrames = 2;
1193b4be Poul Sander 2025-02-03 21:28 22343
        g.DebugLogAutoDisableFlags |= flags;
1193b4be Poul Sander 2025-02-03 21:28 22344
    }
1193b4be Poul Sander 2025-02-03 21:28 22345
    if (highlight_errors)
1193b4be Poul Sander 2025-02-03 21:28 22346
    {
1193b4be Poul Sander 2025-02-03 21:28 22347
        ImGui::PopStyleColor();
1193b4be Poul Sander 2025-02-03 21:28 22348
        ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
1193b4be Poul Sander 2025-02-03 21:28 22349
    }
1193b4be Poul Sander 2025-02-03 21:28 22350
    else
1193b4be Poul Sander 2025-02-03 21:28 22351
    {
1193b4be Poul Sander 2025-02-03 21:28 22352
        ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
1193b4be Poul Sander 2025-02-03 21:28 22353
    }
1193b4be Poul Sander 2025-02-03 21:28 22354
}
1193b4be Poul Sander 2025-02-03 21:28 22355
1193b4be Poul Sander 2025-02-03 21:28 22356
void ImGui::ShowDebugLogWindow(bool* p_open)
1193b4be Poul Sander 2025-02-03 21:28 22357
{
1193b4be Poul Sander 2025-02-03 21:28 22358
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22359
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0)
1193b4be Poul Sander 2025-02-03 21:28 22360
        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
1193b4be Poul Sander 2025-02-03 21:28 22361
    if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
1193b4be Poul Sander 2025-02-03 21:28 22362
    {
1193b4be Poul Sander 2025-02-03 21:28 22363
        End();
1193b4be Poul Sander 2025-02-03 21:28 22364
        return;
1193b4be Poul Sander 2025-02-03 21:28 22365
    }
1193b4be Poul Sander 2025-02-03 21:28 22366
1193b4be Poul Sander 2025-02-03 21:28 22367
    ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
1193b4be Poul Sander 2025-02-03 21:28 22368
    CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
1193b4be Poul Sander 2025-02-03 21:28 22369
    SetItemTooltip("(except InputRouting which is spammy)");
1193b4be Poul Sander 2025-02-03 21:28 22370
1193b4be Poul Sander 2025-02-03 21:28 22371
    ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
1193b4be Poul Sander 2025-02-03 21:28 22372
    ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
1193b4be Poul Sander 2025-02-03 21:28 22373
    ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
1193b4be Poul Sander 2025-02-03 21:28 22374
    ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking);
1193b4be Poul Sander 2025-02-03 21:28 22375
    ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
1193b4be Poul Sander 2025-02-03 21:28 22376
    ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
1193b4be Poul Sander 2025-02-03 21:28 22377
    ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
1193b4be Poul Sander 2025-02-03 21:28 22378
    ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
1193b4be Poul Sander 2025-02-03 21:28 22379
    ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
1193b4be Poul Sander 2025-02-03 21:28 22380
    ShowDebugLogFlag("Viewport", ImGuiDebugLogFlags_EventViewport);
1193b4be Poul Sander 2025-02-03 21:28 22381
    ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
1193b4be Poul Sander 2025-02-03 21:28 22382
1193b4be Poul Sander 2025-02-03 21:28 22383
    if (SmallButton("Clear"))
1193b4be Poul Sander 2025-02-03 21:28 22384
    {
1193b4be Poul Sander 2025-02-03 21:28 22385
        g.DebugLogBuf.clear();
1193b4be Poul Sander 2025-02-03 21:28 22386
        g.DebugLogIndex.clear();
1193b4be Poul Sander 2025-02-03 21:28 22387
        g.DebugLogSkippedErrors = 0;
1193b4be Poul Sander 2025-02-03 21:28 22388
    }
1193b4be Poul Sander 2025-02-03 21:28 22389
    SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22390
    if (SmallButton("Copy"))
1193b4be Poul Sander 2025-02-03 21:28 22391
        SetClipboardText(g.DebugLogBuf.c_str());
1193b4be Poul Sander 2025-02-03 21:28 22392
    SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22393
    if (SmallButton("Configure Outputs.."))
1193b4be Poul Sander 2025-02-03 21:28 22394
        OpenPopup("Outputs");
1193b4be Poul Sander 2025-02-03 21:28 22395
    if (BeginPopup("Outputs"))
1193b4be Poul Sander 2025-02-03 21:28 22396
    {
1193b4be Poul Sander 2025-02-03 21:28 22397
        CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
1193b4be Poul Sander 2025-02-03 21:28 22398
#ifndef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 22399
        BeginDisabled();
1193b4be Poul Sander 2025-02-03 21:28 22400
#endif
1193b4be Poul Sander 2025-02-03 21:28 22401
        CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
1193b4be Poul Sander 2025-02-03 21:28 22402
#ifndef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 22403
        EndDisabled();
1193b4be Poul Sander 2025-02-03 21:28 22404
#endif
1193b4be Poul Sander 2025-02-03 21:28 22405
        EndPopup();
1193b4be Poul Sander 2025-02-03 21:28 22406
    }
1193b4be Poul Sander 2025-02-03 21:28 22407
1193b4be Poul Sander 2025-02-03 21:28 22408
    BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
1193b4be Poul Sander 2025-02-03 21:28 22409
1193b4be Poul Sander 2025-02-03 21:28 22410
    const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
1193b4be Poul Sander 2025-02-03 21:28 22411
    g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
1193b4be Poul Sander 2025-02-03 21:28 22412
1193b4be Poul Sander 2025-02-03 21:28 22413
    ImGuiListClipper clipper;
1193b4be Poul Sander 2025-02-03 21:28 22414
    clipper.Begin(g.DebugLogIndex.size());
1193b4be Poul Sander 2025-02-03 21:28 22415
    while (clipper.Step())
1193b4be Poul Sander 2025-02-03 21:28 22416
        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
1193b4be Poul Sander 2025-02-03 21:28 22417
            DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
1193b4be Poul Sander 2025-02-03 21:28 22418
    g.DebugLogFlags = backup_log_flags;
1193b4be Poul Sander 2025-02-03 21:28 22419
    if (GetScrollY() >= GetScrollMaxY())
1193b4be Poul Sander 2025-02-03 21:28 22420
        SetScrollHereY(1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22421
    EndChild();
1193b4be Poul Sander 2025-02-03 21:28 22422
1193b4be Poul Sander 2025-02-03 21:28 22423
    End();
1193b4be Poul Sander 2025-02-03 21:28 22424
}
1193b4be Poul Sander 2025-02-03 21:28 22425
1193b4be Poul Sander 2025-02-03 21:28 22426
// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
1193b4be Poul Sander 2025-02-03 21:28 22427
void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
1193b4be Poul Sander 2025-02-03 21:28 22428
{
1193b4be Poul Sander 2025-02-03 21:28 22429
    TextUnformatted(line_begin, line_end);
1193b4be Poul Sander 2025-02-03 21:28 22430
    if (!IsItemHovered())
1193b4be Poul Sander 2025-02-03 21:28 22431
        return;
1193b4be Poul Sander 2025-02-03 21:28 22432
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22433
    ImRect text_rect = g.LastItemData.Rect;
1193b4be Poul Sander 2025-02-03 21:28 22434
    for (const char* p = line_begin; p <= line_end - 10; p++)
1193b4be Poul Sander 2025-02-03 21:28 22435
    {
1193b4be Poul Sander 2025-02-03 21:28 22436
        ImGuiID id = 0;
1193b4be Poul Sander 2025-02-03 21:28 22437
        if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
1193b4be Poul Sander 2025-02-03 21:28 22438
            continue;
1193b4be Poul Sander 2025-02-03 21:28 22439
        ImVec2 p0 = CalcTextSize(line_begin, p);
1193b4be Poul Sander 2025-02-03 21:28 22440
        ImVec2 p1 = CalcTextSize(p, p + 10);
1193b4be Poul Sander 2025-02-03 21:28 22441
        g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
1193b4be Poul Sander 2025-02-03 21:28 22442
        if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
1193b4be Poul Sander 2025-02-03 21:28 22443
            DebugLocateItemOnHover(id);
1193b4be Poul Sander 2025-02-03 21:28 22444
        p += 10;
1193b4be Poul Sander 2025-02-03 21:28 22445
    }
1193b4be Poul Sander 2025-02-03 21:28 22446
}
1193b4be Poul Sander 2025-02-03 21:28 22447
1193b4be Poul Sander 2025-02-03 21:28 22448
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22449
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
1193b4be Poul Sander 2025-02-03 21:28 22450
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22451
1193b4be Poul Sander 2025-02-03 21:28 22452
// Draw a small cross at current CursorPos in current window's DrawList
1193b4be Poul Sander 2025-02-03 21:28 22453
void ImGui::DebugDrawCursorPos(ImU32 col)
1193b4be Poul Sander 2025-02-03 21:28 22454
{
1193b4be Poul Sander 2025-02-03 21:28 22455
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22456
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 22457
    ImVec2 pos = window->DC.CursorPos;
1193b4be Poul Sander 2025-02-03 21:28 22458
    window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22459
    window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22460
}
1193b4be Poul Sander 2025-02-03 21:28 22461
1193b4be Poul Sander 2025-02-03 21:28 22462
// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
1193b4be Poul Sander 2025-02-03 21:28 22463
void ImGui::DebugDrawLineExtents(ImU32 col)
1193b4be Poul Sander 2025-02-03 21:28 22464
{
1193b4be Poul Sander 2025-02-03 21:28 22465
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22466
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 22467
    float curr_x = window->DC.CursorPos.x;
1193b4be Poul Sander 2025-02-03 21:28 22468
    float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
1193b4be Poul Sander 2025-02-03 21:28 22469
    float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
1193b4be Poul Sander 2025-02-03 21:28 22470
    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22471
    window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22472
    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
1193b4be Poul Sander 2025-02-03 21:28 22473
}
1193b4be Poul Sander 2025-02-03 21:28 22474
1193b4be Poul Sander 2025-02-03 21:28 22475
// Draw last item rect in ForegroundDrawList (so it is always visible)
1193b4be Poul Sander 2025-02-03 21:28 22476
void ImGui::DebugDrawItemRect(ImU32 col)
1193b4be Poul Sander 2025-02-03 21:28 22477
{
1193b4be Poul Sander 2025-02-03 21:28 22478
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22479
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 22480
    GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
1193b4be Poul Sander 2025-02-03 21:28 22481
}
1193b4be Poul Sander 2025-02-03 21:28 22482
1193b4be Poul Sander 2025-02-03 21:28 22483
// [DEBUG] Locate item position/rectangle given an ID.
1193b4be Poul Sander 2025-02-03 21:28 22484
static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255);  // Green
1193b4be Poul Sander 2025-02-03 21:28 22485
1193b4be Poul Sander 2025-02-03 21:28 22486
void ImGui::DebugLocateItem(ImGuiID target_id)
1193b4be Poul Sander 2025-02-03 21:28 22487
{
1193b4be Poul Sander 2025-02-03 21:28 22488
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22489
    g.DebugLocateId = target_id;
1193b4be Poul Sander 2025-02-03 21:28 22490
    g.DebugLocateFrames = 2;
1193b4be Poul Sander 2025-02-03 21:28 22491
    g.DebugBreakInLocateId = false;
1193b4be Poul Sander 2025-02-03 21:28 22492
}
1193b4be Poul Sander 2025-02-03 21:28 22493
1193b4be Poul Sander 2025-02-03 21:28 22494
// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
1193b4be Poul Sander 2025-02-03 21:28 22495
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
1193b4be Poul Sander 2025-02-03 21:28 22496
{
1193b4be Poul Sander 2025-02-03 21:28 22497
    if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
1193b4be Poul Sander 2025-02-03 21:28 22498
        return;
1193b4be Poul Sander 2025-02-03 21:28 22499
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22500
    DebugLocateItem(target_id);
1193b4be Poul Sander 2025-02-03 21:28 22501
    GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
1193b4be Poul Sander 2025-02-03 21:28 22502
1193b4be Poul Sander 2025-02-03 21:28 22503
    // Can't easily use a context menu here because it will mess with focus, active id etc.
1193b4be Poul Sander 2025-02-03 21:28 22504
    if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
1193b4be Poul Sander 2025-02-03 21:28 22505
    {
1193b4be Poul Sander 2025-02-03 21:28 22506
        DebugBreakButtonTooltip(false, "in ItemAdd()");
1193b4be Poul Sander 2025-02-03 21:28 22507
        if (IsKeyChordPressed(g.DebugBreakKeyChord))
1193b4be Poul Sander 2025-02-03 21:28 22508
            g.DebugBreakInLocateId = true;
1193b4be Poul Sander 2025-02-03 21:28 22509
    }
1193b4be Poul Sander 2025-02-03 21:28 22510
}
1193b4be Poul Sander 2025-02-03 21:28 22511
1193b4be Poul Sander 2025-02-03 21:28 22512
void ImGui::DebugLocateItemResolveWithLastItem()
1193b4be Poul Sander 2025-02-03 21:28 22513
{
1193b4be Poul Sander 2025-02-03 21:28 22514
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22515
1193b4be Poul Sander 2025-02-03 21:28 22516
    // [DEBUG] Debug break requested by user
1193b4be Poul Sander 2025-02-03 21:28 22517
    if (g.DebugBreakInLocateId)
1193b4be Poul Sander 2025-02-03 21:28 22518
        IM_DEBUG_BREAK();
1193b4be Poul Sander 2025-02-03 21:28 22519
1193b4be Poul Sander 2025-02-03 21:28 22520
    ImGuiLastItemData item_data = g.LastItemData;
1193b4be Poul Sander 2025-02-03 21:28 22521
    g.DebugLocateId = 0;
1193b4be Poul Sander 2025-02-03 21:28 22522
    ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
1193b4be Poul Sander 2025-02-03 21:28 22523
    ImRect r = item_data.Rect;
1193b4be Poul Sander 2025-02-03 21:28 22524
    r.Expand(3.0f);
1193b4be Poul Sander 2025-02-03 21:28 22525
    ImVec2 p1 = g.IO.MousePos;
1193b4be Poul Sander 2025-02-03 21:28 22526
    ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);
1193b4be Poul Sander 2025-02-03 21:28 22527
    draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
1193b4be Poul Sander 2025-02-03 21:28 22528
    draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
1193b4be Poul Sander 2025-02-03 21:28 22529
}
1193b4be Poul Sander 2025-02-03 21:28 22530
1193b4be Poul Sander 2025-02-03 21:28 22531
void ImGui::DebugStartItemPicker()
1193b4be Poul Sander 2025-02-03 21:28 22532
{
1193b4be Poul Sander 2025-02-03 21:28 22533
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22534
    g.DebugItemPickerActive = true;
1193b4be Poul Sander 2025-02-03 21:28 22535
}
1193b4be Poul Sander 2025-02-03 21:28 22536
1193b4be Poul Sander 2025-02-03 21:28 22537
// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
1193b4be Poul Sander 2025-02-03 21:28 22538
void ImGui::UpdateDebugToolItemPicker()
1193b4be Poul Sander 2025-02-03 21:28 22539
{
1193b4be Poul Sander 2025-02-03 21:28 22540
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22541
    g.DebugItemPickerBreakId = 0;
1193b4be Poul Sander 2025-02-03 21:28 22542
    if (!g.DebugItemPickerActive)
1193b4be Poul Sander 2025-02-03 21:28 22543
        return;
1193b4be Poul Sander 2025-02-03 21:28 22544
1193b4be Poul Sander 2025-02-03 21:28 22545
    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
1193b4be Poul Sander 2025-02-03 21:28 22546
    SetMouseCursor(ImGuiMouseCursor_Hand);
1193b4be Poul Sander 2025-02-03 21:28 22547
    if (IsKeyPressed(ImGuiKey_Escape))
1193b4be Poul Sander 2025-02-03 21:28 22548
        g.DebugItemPickerActive = false;
1193b4be Poul Sander 2025-02-03 21:28 22549
    const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
1193b4be Poul Sander 2025-02-03 21:28 22550
    if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
1193b4be Poul Sander 2025-02-03 21:28 22551
    {
1193b4be Poul Sander 2025-02-03 21:28 22552
        g.DebugItemPickerBreakId = hovered_id;
1193b4be Poul Sander 2025-02-03 21:28 22553
        g.DebugItemPickerActive = false;
1193b4be Poul Sander 2025-02-03 21:28 22554
    }
1193b4be Poul Sander 2025-02-03 21:28 22555
    for (int mouse_button = 0; mouse_button < 3; mouse_button++)
1193b4be Poul Sander 2025-02-03 21:28 22556
        if (change_mapping && IsMouseClicked(mouse_button))
1193b4be Poul Sander 2025-02-03 21:28 22557
            g.DebugItemPickerMouseButton = (ImU8)mouse_button;
1193b4be Poul Sander 2025-02-03 21:28 22558
    SetNextWindowBgAlpha(0.70f);
1193b4be Poul Sander 2025-02-03 21:28 22559
    if (!BeginTooltip())
1193b4be Poul Sander 2025-02-03 21:28 22560
        return;
1193b4be Poul Sander 2025-02-03 21:28 22561
    Text("HoveredId: 0x%08X", hovered_id);
1193b4be Poul Sander 2025-02-03 21:28 22562
    Text("Press ESC to abort picking.");
1193b4be Poul Sander 2025-02-03 21:28 22563
    const char* mouse_button_names[] = { "Left", "Right", "Middle" };
1193b4be Poul Sander 2025-02-03 21:28 22564
    if (change_mapping)
1193b4be Poul Sander 2025-02-03 21:28 22565
        Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
1193b4be Poul Sander 2025-02-03 21:28 22566
    else
1193b4be Poul Sander 2025-02-03 21:28 22567
        TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
1193b4be Poul Sander 2025-02-03 21:28 22568
    EndTooltip();
1193b4be Poul Sander 2025-02-03 21:28 22569
}
1193b4be Poul Sander 2025-02-03 21:28 22570
1193b4be Poul Sander 2025-02-03 21:28 22571
// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
1193b4be Poul Sander 2025-02-03 21:28 22572
void ImGui::UpdateDebugToolStackQueries()
1193b4be Poul Sander 2025-02-03 21:28 22573
{
1193b4be Poul Sander 2025-02-03 21:28 22574
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22575
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
1193b4be Poul Sander 2025-02-03 21:28 22576
1193b4be Poul Sander 2025-02-03 21:28 22577
    // Clear hook when id stack tool is not visible
1193b4be Poul Sander 2025-02-03 21:28 22578
    g.DebugHookIdInfo = 0;
1193b4be Poul Sander 2025-02-03 21:28 22579
    if (g.FrameCount != tool->LastActiveFrame + 1)
1193b4be Poul Sander 2025-02-03 21:28 22580
        return;
1193b4be Poul Sander 2025-02-03 21:28 22581
1193b4be Poul Sander 2025-02-03 21:28 22582
    // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
1193b4be Poul Sander 2025-02-03 21:28 22583
    // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
1193b4be Poul Sander 2025-02-03 21:28 22584
    const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
1193b4be Poul Sander 2025-02-03 21:28 22585
    if (tool->QueryId != query_id)
1193b4be Poul Sander 2025-02-03 21:28 22586
    {
1193b4be Poul Sander 2025-02-03 21:28 22587
        tool->QueryId = query_id;
1193b4be Poul Sander 2025-02-03 21:28 22588
        tool->StackLevel = -1;
1193b4be Poul Sander 2025-02-03 21:28 22589
        tool->Results.resize(0);
1193b4be Poul Sander 2025-02-03 21:28 22590
    }
1193b4be Poul Sander 2025-02-03 21:28 22591
    if (query_id == 0)
1193b4be Poul Sander 2025-02-03 21:28 22592
        return;
1193b4be Poul Sander 2025-02-03 21:28 22593
1193b4be Poul Sander 2025-02-03 21:28 22594
    // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
1193b4be Poul Sander 2025-02-03 21:28 22595
    int stack_level = tool->StackLevel;
1193b4be Poul Sander 2025-02-03 21:28 22596
    if (stack_level >= 0 && stack_level < tool->Results.Size)
1193b4be Poul Sander 2025-02-03 21:28 22597
        if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
1193b4be Poul Sander 2025-02-03 21:28 22598
            tool->StackLevel++;
1193b4be Poul Sander 2025-02-03 21:28 22599
1193b4be Poul Sander 2025-02-03 21:28 22600
    // Update hook
1193b4be Poul Sander 2025-02-03 21:28 22601
    stack_level = tool->StackLevel;
1193b4be Poul Sander 2025-02-03 21:28 22602
    if (stack_level == -1)
1193b4be Poul Sander 2025-02-03 21:28 22603
        g.DebugHookIdInfo = query_id;
1193b4be Poul Sander 2025-02-03 21:28 22604
    if (stack_level >= 0 && stack_level < tool->Results.Size)
1193b4be Poul Sander 2025-02-03 21:28 22605
    {
1193b4be Poul Sander 2025-02-03 21:28 22606
        g.DebugHookIdInfo = tool->Results[stack_level].ID;
1193b4be Poul Sander 2025-02-03 21:28 22607
        tool->Results[stack_level].QueryFrameCount++;
1193b4be Poul Sander 2025-02-03 21:28 22608
    }
1193b4be Poul Sander 2025-02-03 21:28 22609
}
1193b4be Poul Sander 2025-02-03 21:28 22610
1193b4be Poul Sander 2025-02-03 21:28 22611
// [DEBUG] ID Stack tool: hooks called by GetID() family functions
1193b4be Poul Sander 2025-02-03 21:28 22612
void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
1193b4be Poul Sander 2025-02-03 21:28 22613
{
1193b4be Poul Sander 2025-02-03 21:28 22614
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22615
    ImGuiWindow* window = g.CurrentWindow;
1193b4be Poul Sander 2025-02-03 21:28 22616
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
1193b4be Poul Sander 2025-02-03 21:28 22617
1193b4be Poul Sander 2025-02-03 21:28 22618
    // Step 0: stack query
1193b4be Poul Sander 2025-02-03 21:28 22619
    // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
1193b4be Poul Sander 2025-02-03 21:28 22620
    if (tool->StackLevel == -1)
1193b4be Poul Sander 2025-02-03 21:28 22621
    {
1193b4be Poul Sander 2025-02-03 21:28 22622
        tool->StackLevel++;
1193b4be Poul Sander 2025-02-03 21:28 22623
        tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
1193b4be Poul Sander 2025-02-03 21:28 22624
        for (int n = 0; n < window->IDStack.Size + 1; n++)
1193b4be Poul Sander 2025-02-03 21:28 22625
            tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
1193b4be Poul Sander 2025-02-03 21:28 22626
        return;
1193b4be Poul Sander 2025-02-03 21:28 22627
    }
1193b4be Poul Sander 2025-02-03 21:28 22628
1193b4be Poul Sander 2025-02-03 21:28 22629
    // Step 1+: query for individual level
1193b4be Poul Sander 2025-02-03 21:28 22630
    IM_ASSERT(tool->StackLevel >= 0);
1193b4be Poul Sander 2025-02-03 21:28 22631
    if (tool->StackLevel != window->IDStack.Size)
1193b4be Poul Sander 2025-02-03 21:28 22632
        return;
1193b4be Poul Sander 2025-02-03 21:28 22633
    ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
1193b4be Poul Sander 2025-02-03 21:28 22634
    IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
1193b4be Poul Sander 2025-02-03 21:28 22635
1193b4be Poul Sander 2025-02-03 21:28 22636
    switch (data_type)
1193b4be Poul Sander 2025-02-03 21:28 22637
    {
1193b4be Poul Sander 2025-02-03 21:28 22638
    case ImGuiDataType_S32:
1193b4be Poul Sander 2025-02-03 21:28 22639
        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
1193b4be Poul Sander 2025-02-03 21:28 22640
        break;
1193b4be Poul Sander 2025-02-03 21:28 22641
    case ImGuiDataType_String:
1193b4be Poul Sander 2025-02-03 21:28 22642
        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id);
1193b4be Poul Sander 2025-02-03 21:28 22643
        break;
1193b4be Poul Sander 2025-02-03 21:28 22644
    case ImGuiDataType_Pointer:
1193b4be Poul Sander 2025-02-03 21:28 22645
        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
1193b4be Poul Sander 2025-02-03 21:28 22646
        break;
1193b4be Poul Sander 2025-02-03 21:28 22647
    case ImGuiDataType_ID:
1193b4be Poul Sander 2025-02-03 21:28 22648
        if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
1193b4be Poul Sander 2025-02-03 21:28 22649
            return;
1193b4be Poul Sander 2025-02-03 21:28 22650
        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
1193b4be Poul Sander 2025-02-03 21:28 22651
        break;
1193b4be Poul Sander 2025-02-03 21:28 22652
    default:
1193b4be Poul Sander 2025-02-03 21:28 22653
        IM_ASSERT(0);
1193b4be Poul Sander 2025-02-03 21:28 22654
    }
1193b4be Poul Sander 2025-02-03 21:28 22655
    info->QuerySuccess = true;
1193b4be Poul Sander 2025-02-03 21:28 22656
    info->DataType = data_type;
1193b4be Poul Sander 2025-02-03 21:28 22657
}
1193b4be Poul Sander 2025-02-03 21:28 22658
1193b4be Poul Sander 2025-02-03 21:28 22659
static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
1193b4be Poul Sander 2025-02-03 21:28 22660
{
1193b4be Poul Sander 2025-02-03 21:28 22661
    ImGuiStackLevelInfo* info = &tool->Results[n];
1193b4be Poul Sander 2025-02-03 21:28 22662
    ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
1193b4be Poul Sander 2025-02-03 21:28 22663
    if (window)                                                                 // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
1193b4be Poul Sander 2025-02-03 21:28 22664
        return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
1193b4be Poul Sander 2025-02-03 21:28 22665
    if (info->QuerySuccess)                                                     // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
1193b4be Poul Sander 2025-02-03 21:28 22666
        return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
1193b4be Poul Sander 2025-02-03 21:28 22667
    if (tool->StackLevel < tool->Results.Size)                                  // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
1193b4be Poul Sander 2025-02-03 21:28 22668
        return (*buf = 0);
1193b4be Poul Sander 2025-02-03 21:28 22669
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 22670
    if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID))   // Source: ImGuiTestEngine's ItemInfo()
1193b4be Poul Sander 2025-02-03 21:28 22671
        return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
1193b4be Poul Sander 2025-02-03 21:28 22672
#endif
1193b4be Poul Sander 2025-02-03 21:28 22673
    return ImFormatString(buf, buf_size, "???");
1193b4be Poul Sander 2025-02-03 21:28 22674
}
1193b4be Poul Sander 2025-02-03 21:28 22675
1193b4be Poul Sander 2025-02-03 21:28 22676
// ID Stack Tool: Display UI
1193b4be Poul Sander 2025-02-03 21:28 22677
void ImGui::ShowIDStackToolWindow(bool* p_open)
1193b4be Poul Sander 2025-02-03 21:28 22678
{
1193b4be Poul Sander 2025-02-03 21:28 22679
    ImGuiContext& g = *GImGui;
1193b4be Poul Sander 2025-02-03 21:28 22680
    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0)
1193b4be Poul Sander 2025-02-03 21:28 22681
        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
1193b4be Poul Sander 2025-02-03 21:28 22682
    if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
1193b4be Poul Sander 2025-02-03 21:28 22683
    {
1193b4be Poul Sander 2025-02-03 21:28 22684
        End();
1193b4be Poul Sander 2025-02-03 21:28 22685
        return;
1193b4be Poul Sander 2025-02-03 21:28 22686
    }
1193b4be Poul Sander 2025-02-03 21:28 22687
1193b4be Poul Sander 2025-02-03 21:28 22688
    // Display hovered/active status
1193b4be Poul Sander 2025-02-03 21:28 22689
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
1193b4be Poul Sander 2025-02-03 21:28 22690
    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
1193b4be Poul Sander 2025-02-03 21:28 22691
    const ImGuiID active_id = g.ActiveId;
1193b4be Poul Sander 2025-02-03 21:28 22692
#ifdef IMGUI_ENABLE_TEST_ENGINE
1193b4be Poul Sander 2025-02-03 21:28 22693
    Text("HoveredId: 0x%08X (\"%s\"), ActiveId:  0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : "");
1193b4be Poul Sander 2025-02-03 21:28 22694
#else
1193b4be Poul Sander 2025-02-03 21:28 22695
    Text("HoveredId: 0x%08X, ActiveId:  0x%08X", hovered_id, active_id);
1193b4be Poul Sander 2025-02-03 21:28 22696
#endif
1193b4be Poul Sander 2025-02-03 21:28 22697
    SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22698
    MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
1193b4be Poul Sander 2025-02-03 21:28 22699
1193b4be Poul Sander 2025-02-03 21:28 22700
    // CTRL+C to copy path
1193b4be Poul Sander 2025-02-03 21:28 22701
    const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
1193b4be Poul Sander 2025-02-03 21:28 22702
    Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
1193b4be Poul Sander 2025-02-03 21:28 22703
    SameLine();
1193b4be Poul Sander 2025-02-03 21:28 22704
    TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
1193b4be Poul Sander 2025-02-03 21:28 22705
    if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
1193b4be Poul Sander 2025-02-03 21:28 22706
    {
1193b4be Poul Sander 2025-02-03 21:28 22707
        tool->CopyToClipboardLastTime = (float)g.Time;
1193b4be Poul Sander 2025-02-03 21:28 22708
        char* p = g.TempBuffer.Data;
1193b4be Poul Sander 2025-02-03 21:28 22709
        char* p_end = p + g.TempBuffer.Size;
1193b4be Poul Sander 2025-02-03 21:28 22710
        for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
1193b4be Poul Sander 2025-02-03 21:28 22711
        {
1193b4be Poul Sander 2025-02-03 21:28 22712
            *p++ = '/';
1193b4be Poul Sander 2025-02-03 21:28 22713
            char level_desc[256];
1193b4be Poul Sander 2025-02-03 21:28 22714
            StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
1193b4be Poul Sander 2025-02-03 21:28 22715
            for (int n = 0; level_desc[n] && p + 2 < p_end; n++)
1193b4be Poul Sander 2025-02-03 21:28 22716
            {
1193b4be Poul Sander 2025-02-03 21:28 22717
                if (level_desc[n] == '/')
1193b4be Poul Sander 2025-02-03 21:28 22718
                    *p++ = '\\';
1193b4be Poul Sander 2025-02-03 21:28 22719
                *p++ = level_desc[n];
1193b4be Poul Sander 2025-02-03 21:28 22720
            }
1193b4be Poul Sander 2025-02-03 21:28 22721
        }
1193b4be Poul Sander 2025-02-03 21:28 22722
        *p = '\0';
1193b4be Poul Sander 2025-02-03 21:28 22723
        SetClipboardText(g.TempBuffer.Data);
1193b4be Poul Sander 2025-02-03 21:28 22724
    }
1193b4be Poul Sander 2025-02-03 21:28 22725
1193b4be Poul Sander 2025-02-03 21:28 22726
    // Display decorated stack
1193b4be Poul Sander 2025-02-03 21:28 22727
    tool->LastActiveFrame = g.FrameCount;
1193b4be Poul Sander 2025-02-03 21:28 22728
    if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
1193b4be Poul Sander 2025-02-03 21:28 22729
    {
1193b4be Poul Sander 2025-02-03 21:28 22730
        const float id_width = CalcTextSize("0xDDDDDDDD").x;
1193b4be Poul Sander 2025-02-03 21:28 22731
        TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
1193b4be Poul Sander 2025-02-03 21:28 22732
        TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
1193b4be Poul Sander 2025-02-03 21:28 22733
        TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
1193b4be Poul Sander 2025-02-03 21:28 22734
        TableHeadersRow();
1193b4be Poul Sander 2025-02-03 21:28 22735
        for (int n = 0; n < tool->Results.Size; n++)
1193b4be Poul Sander 2025-02-03 21:28 22736
        {
1193b4be Poul Sander 2025-02-03 21:28 22737
            ImGuiStackLevelInfo* info = &tool->Results[n];
1193b4be Poul Sander 2025-02-03 21:28 22738
            TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 22739
            Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
1193b4be Poul Sander 2025-02-03 21:28 22740
            TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 22741
            StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
1193b4be Poul Sander 2025-02-03 21:28 22742
            TextUnformatted(g.TempBuffer.Data);
1193b4be Poul Sander 2025-02-03 21:28 22743
            TableNextColumn();
1193b4be Poul Sander 2025-02-03 21:28 22744
            Text("0x%08X", info->ID);
1193b4be Poul Sander 2025-02-03 21:28 22745
            if (n == tool->Results.Size - 1)
1193b4be Poul Sander 2025-02-03 21:28 22746
                TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
1193b4be Poul Sander 2025-02-03 21:28 22747
        }
1193b4be Poul Sander 2025-02-03 21:28 22748
        EndTable();
1193b4be Poul Sander 2025-02-03 21:28 22749
    }
1193b4be Poul Sander 2025-02-03 21:28 22750
    End();
1193b4be Poul Sander 2025-02-03 21:28 22751
}
1193b4be Poul Sander 2025-02-03 21:28 22752
1193b4be Poul Sander 2025-02-03 21:28 22753
#else
1193b4be Poul Sander 2025-02-03 21:28 22754
1193b4be Poul Sander 2025-02-03 21:28 22755
void ImGui::ShowMetricsWindow(bool*) {}
1193b4be Poul Sander 2025-02-03 21:28 22756
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
1193b4be Poul Sander 2025-02-03 21:28 22757
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
1193b4be Poul Sander 2025-02-03 21:28 22758
void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
1193b4be Poul Sander 2025-02-03 21:28 22759
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
1193b4be Poul Sander 2025-02-03 21:28 22760
void ImGui::DebugNodeFont(ImFont*) {}
1193b4be Poul Sander 2025-02-03 21:28 22761
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
1193b4be Poul Sander 2025-02-03 21:28 22762
void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
1193b4be Poul Sander 2025-02-03 21:28 22763
void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
1193b4be Poul Sander 2025-02-03 21:28 22764
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
1193b4be Poul Sander 2025-02-03 21:28 22765
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
1193b4be Poul Sander 2025-02-03 21:28 22766
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
1193b4be Poul Sander 2025-02-03 21:28 22767
1193b4be Poul Sander 2025-02-03 21:28 22768
void ImGui::ShowDebugLogWindow(bool*) {}
1193b4be Poul Sander 2025-02-03 21:28 22769
void ImGui::ShowIDStackToolWindow(bool*) {}
1193b4be Poul Sander 2025-02-03 21:28 22770
void ImGui::DebugStartItemPicker() {}
1193b4be Poul Sander 2025-02-03 21:28 22771
void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
1193b4be Poul Sander 2025-02-03 21:28 22772
1193b4be Poul Sander 2025-02-03 21:28 22773
#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
1193b4be Poul Sander 2025-02-03 21:28 22774
1193b4be Poul Sander 2025-02-03 21:28 22775
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22776
1193b4be Poul Sander 2025-02-03 21:28 22777
// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
1193b4be Poul Sander 2025-02-03 21:28 22778
// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
1193b4be Poul Sander 2025-02-03 21:28 22779
#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
1193b4be Poul Sander 2025-02-03 21:28 22780
#include "imgui_user.inl"
1193b4be Poul Sander 2025-02-03 21:28 22781
#endif
1193b4be Poul Sander 2025-02-03 21:28 22782
1193b4be Poul Sander 2025-02-03 21:28 22783
//-----------------------------------------------------------------------------
1193b4be Poul Sander 2025-02-03 21:28 22784
1193b4be Poul Sander 2025-02-03 21:28 22785
#endif // #ifndef IMGUI_DISABLE
1970-01-01 00:00 22786