Changeset e66fd66 in opengl-game for IMGUI/imgui.cpp
- Timestamp:
- Dec 5, 2020, 7:14:31 PM (4 years ago)
- Branches:
- feature/imgui-sdl, master
- Children:
- 95c657f
- Parents:
- 78c3045
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
IMGUI/imgui.cpp
r78c3045 re66fd66 1 // dear imgui, v1. 61 WIP1 // dear imgui, v1.79 2 2 // (main code and documentation) 3 3 4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. 5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. 6 // Get latest version at https://github.com/ocornut/imgui 7 // Releases change-log at https://github.com/ocornut/imgui/releases 8 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269 4 // Help: 5 // - Read FAQ at http://dearimgui.org/faq 6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. 7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. 8 // Read imgui.cpp for details, links and comments. 9 10 // Resources: 11 // - FAQ http://dearimgui.org/faq 12 // - Homepage & latest https://github.com/ocornut/imgui 13 // - Releases & changelog https://github.com/ocornut/imgui/releases 14 // - Gallery https://github.com/ocornut/imgui/issues/3488 (please post your screenshots/video there!) 15 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary 16 // - Wiki https://github.com/ocornut/imgui/wiki 17 // - Issues & support https://github.com/ocornut/imgui/issues 18 9 19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. 10 // This library is free but I need your support to sustain development and maintenance. 11 // If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui 20 // See LICENSE.txt for copyright and licensing details (standard MIT License). 21 // This library is free but needs your support to sustain development and maintenance. 22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org". 23 // Individuals: you can support continued development via donations. See docs/README or web page. 12 24 13 25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. 14 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without 15 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't 16 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you 26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without 27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't 28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you 17 29 // to a better solution or official support for them. 18 30 19 31 /* 20 32 21 Index 33 Index of this file: 34 35 DOCUMENTATION 36 22 37 - MISSION STATEMENT 23 38 - END-USER GUIDE 24 - PROGRAMMER GUIDE (read me!) 25 - Read first 26 - How to update to a newer version of Dear ImGui 27 - Getting started with integrating Dear ImGui in your code/engine 28 - Using gamepad/keyboard navigation controls [BETA] 39 - PROGRAMMER GUIDE 40 - READ FIRST 41 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 42 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 43 - HOW A SIMPLE APPLICATION MAY LOOK LIKE 44 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 45 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 29 46 - API BREAKING CHANGES (read me when you update!) 30 - ISSUES & TODO LIST 31 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS 32 - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? 33 - How can I display an image? What is ImTextureID, how does it works? 34 - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack. 35 - How can I load a different font than the default? 36 - How can I easily use icons in my application? 37 - How can I load multiple fonts? 38 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? 39 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) 40 - I integrated Dear ImGui in my engine and the text or lines are blurry.. 41 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. 42 - How can I help? 43 - ISSUES & TODO-LIST 44 - CODE 45 46 47 MISSION STATEMENT 48 ================= 49 50 - Easy to use to create code-driven and data-driven tools 51 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools 52 - Easy to hack and improve 53 - Minimize screen real-estate usage 54 - Minimize setup and maintenance 55 - Minimize state storage on user side 56 - Portable, minimize dependencies, run on target (consoles, phones, etc.) 57 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, 58 opening a tree node for the first time, etc. but a typical frame should not allocate anything) 59 60 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: 61 - Doesn't look fancy, doesn't animate 62 - Limited layout features, intricate layouts are typically crafted in code 63 64 65 END-USER GUIDE 66 ============== 67 68 - Double-click on title bar to collapse window. 69 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). 70 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). 71 - Click and drag on any empty space to move window. 72 - TAB/SHIFT+TAB to cycle through keyboard editable fields. 73 - CTRL+Click on a slider or drag box to input value as text. 74 - Use mouse wheel to scroll. 75 - Text editor: 76 - Hold SHIFT or use mouse to select text. 77 - CTRL+Left/Right to word jump. 78 - CTRL+Shift+Left/Right to select words. 79 - CTRL+A our Double-Click to select all. 80 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ 81 - CTRL+Z,CTRL+Y to undo/redo. 82 - ESCAPE to revert text to its original value. 83 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) 84 - Controls are automatically adjusted for OSX to match standard OSX text editing operations. 85 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. 86 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW 87 88 89 PROGRAMMER GUIDE 90 ================ 91 92 READ FIRST 93 94 - Read the FAQ below this section! 95 - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction 96 or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs. 97 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. 98 - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 99 100 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 101 102 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) 103 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. 104 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed 105 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will 106 likely be a comment about it. Please report any issue to the GitHub page! 107 - Try to keep your copy of dear imgui reasonably up to date. 108 109 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 110 111 - Run and study the examples and demo to get acquainted with the library. 112 - Add the Dear ImGui source files to your projects, using your preferred build system. 113 It is recommended you build the .cpp files as part of your project and not as a library. 114 - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types. 115 - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder. 116 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. 117 118 - Init: retrieve the ImGuiIO structure with ImGui::GetIO() and fill the fields marked 'Settings': at minimum you need to set io.DisplaySize 119 (application resolution). Later on you will fill your keyboard mapping, clipboard handlers, and other advanced features but for a basic 120 integration you don't need to worry about it all. 121 - Init: call io.Fonts->GetTexDataAsRGBA32(...), it will build the font atlas texture, then load the texture pixels into graphics memory. 122 - Every frame: 123 - In your main loop as early a possible, fill the IO fields marked 'Input' (e.g. mouse position, buttons, keyboard info, etc.) 124 - Call ImGui::NewFrame() to begin the frame 125 - You can use any ImGui function you want between NewFrame() and Render() 126 - Call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your io.RenderDrawListFn handler. 127 (Even if you don't render, call Render() and ignore the callback, or call EndFrame() instead. Otherwise some features will break) 128 - All rendering information are stored into command-lists until ImGui::Render() is called. 129 - Dear ImGui never touches or knows about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. 130 - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases 131 of your own application. 132 - Refer to the examples applications in the examples/ folder for instruction on how to setup your code. 133 - A minimal application skeleton may be: 134 135 // Application init 136 ImGui::CreateContext(); 137 ImGuiIO& io = ImGui::GetIO(); 138 io.DisplaySize.x = 1920.0f; 139 io.DisplaySize.y = 1280.0f; 140 // TODO: Fill others settings of the io structure later. 141 142 // Load texture atlas (there is a default font so you don't need to care about choosing a font yet) 143 unsigned char* pixels; 144 int width, height; 145 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 146 // TODO: At this points you've got the texture data and you need to upload that your your graphic system: 147 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA) 148 // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID'. This will be passed back to your via the renderer. 149 io.Fonts->TexID = (void*)texture; 150 151 // Application main loop 152 while (true) 153 { 154 // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.) 155 ImGuiIO& io = ImGui::GetIO(); 156 io.DeltaTime = 1.0f/60.0f; 157 io.MousePos = mouse_pos; 158 io.MouseDown[0] = mouse_button_0; 159 io.MouseDown[1] = mouse_button_1; 160 161 // Call NewFrame(), after this point you can use ImGui::* functions anytime 162 ImGui::NewFrame(); 163 164 // Most of your application code here 165 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); 166 MyGameRender(); // may use any ImGui functions as well! 167 168 // Render & swap video buffers 169 ImGui::Render(); 170 MyImGuiRenderFunction(ImGui::GetDrawData()); 171 SwapBuffers(); 172 } 173 174 // Shutdown 175 ImGui::DestroyContext(); 176 177 178 - A minimal render function skeleton may be: 179 180 void void MyRenderFunction(ImDrawData* draw_data) 181 { 182 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled 183 // TODO: Setup viewport, orthographic projection matrix 184 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. 185 for (int n = 0; n < draw_data->CmdListsCount; n++) 186 { 187 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui 188 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui 189 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 190 { 191 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 192 if (pcmd->UserCallback) 193 { 194 pcmd->UserCallback(cmd_list, pcmd); 195 } 196 else 197 { 198 // The texture for the draw call is specified by pcmd->TextureId. 199 // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization. 200 MyEngineBindTexture(pcmd->TextureId); 201 202 // We are using scissoring to clip some objects. All low-level graphics API supports it. 203 // If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches 204 // (some elements visible outside their bounds) but you can fix that once everywhere else works! 205 MyEngineScissor((int)pcmd->ClipRect.x, (int)pcmd->ClipRect.y, (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); 206 207 // Render 'pcmd->ElemCount/3' indexed triangles. 208 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices. 209 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); 210 } 211 idx_buffer += pcmd->ElemCount; 212 } 213 } 214 } 215 216 - The examples/ folders contains many functional implementation of the pseudo-code above. 217 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated. 218 They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application. 219 However, in both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags. 220 - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues! 221 222 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA] 223 224 - The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 225 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. 226 - Gamepad: 227 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. 228 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). 229 Note that io.NavInputs[] is cleared by EndFrame(). 230 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 231 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. 232 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. 233 Your 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.). 234 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW. 235 - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo 236 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. 237 - Keyboard: 238 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. 239 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. 240 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag 241 will be set. For more advanced uses, you may want to read from: 242 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. 243 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). 244 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. 245 Please reach out if you think the game vs navigation input sharing could be improved. 246 - Mouse: 247 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. 248 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. 249 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. 250 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. 251 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. 252 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. 253 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) 254 (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 255 to set a boolean to ignore your other external mouse positions until the external source is moved again.) 256 257 258 API BREAKING CHANGES 259 ==================== 260 261 Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. 262 Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. 263 Also read releases logs https://github.com/ocornut/imgui/releases for more details. 264 265 - 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", consistent with other functions. Kept redirection functions (will obsolete). 266 - 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. 267 - 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 binding ahead of merging the Nav branch). 268 - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. 269 - 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. 270 - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. 271 - 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. 272 - 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. 273 - 2018/02/07 (1.60) - reorganized context handling to be more explicit, 274 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. 275 - removed Shutdown() function, as DestroyContext() serve this purpose. 276 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. 277 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. 278 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. 279 - 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. 280 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). 281 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). 282 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. 283 - 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. 284 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). 285 - 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 286 - 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. 287 - 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. 288 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). 289 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). 290 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). 291 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). 292 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). 293 - 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. 294 - 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. 295 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. 296 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. 297 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. 298 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. 299 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); 300 - 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. 301 - 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. 302 - 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. 303 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. 304 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! 305 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). 306 - 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). 307 - 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 binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". 308 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! 309 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). 310 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). 311 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. 312 - 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. 313 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame. 314 - 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. 315 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete). 316 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). 317 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). 318 - 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. 319 - 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. 320 - 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))' 321 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse 322 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. 323 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. 324 - 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(). 325 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. 326 - 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. 327 - 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. 328 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 329 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. 330 However if your TitleBg/TitleBgActive alpha was <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. 331 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. 332 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) 333 { 334 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; 335 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); 336 } 337 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. 338 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). 339 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. 340 - 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). 341 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::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. 342 - 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). 343 - 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) 344 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). 345 - 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. 346 - 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. 347 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. 348 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. 349 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. 350 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. 351 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! 352 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize 353 - 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. 354 - 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 355 - 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. 356 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. 357 - 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. 358 this necessary change will break your rendering function! the fix should be very easy. sorry for that :( 359 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. 360 - the signature of the io.RenderDrawListsFn handler has changed! 361 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) 362 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). 363 argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' 364 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. 365 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. 366 - 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. 367 - 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! 368 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! 369 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. 370 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). 371 - 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. 372 - 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 373 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! 374 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). 375 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). 376 - 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. 377 - 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. 378 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). 379 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. 380 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API 381 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. 382 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. 383 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. 384 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing 385 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. 386 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) 387 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. 388 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. 389 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. 390 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior 391 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() 392 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) 393 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. 394 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. 395 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. 396 font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..> 397 became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; 398 you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. 399 it is now recommended that you sample the font texture with bilinear interpolation. 400 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. 401 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) 402 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets 403 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) 404 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) 405 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility 406 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() 407 - 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) 408 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) 409 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() 410 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn 411 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) 412 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite 413 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes 414 415 416 ISSUES & TODO-LIST 417 ================== 418 See TODO.txt 419 420 421 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS 422 ====================================== 423 424 Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application? 425 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure. 426 - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application. 427 - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application. 428 - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS). 429 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false. 430 This is because imgui needs to detect that you clicked in the void to unfocus its windows. 431 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!). 432 It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs. 433 Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also 434 perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to NewFrameUpdateHoveredWindowAndCaptureFlags(). 435 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically 436 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs 437 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) 438 439 Q: How can I display an image? What is ImTextureID, how does it works? 440 A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. 441 Dear ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry! 442 It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. 443 At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. 444 Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. 445 (C++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) 446 To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. 447 Dear ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. 448 You may call ImGui::ShowMetricsWindow() to explore active draw lists and visualize/understand how the draw data is generated. 449 It is your responsibility to get textures uploaded to your GPU. 450 451 Q: How can I have multiple widgets with the same label or without a label? 452 A: A primer on labels and the ID Stack... 453 454 - Elements that are typically not clickable, such as Text() items don't need an ID. 455 456 - Interactive widgets require state to be carried over multiple frames (most typically Dear ImGui 457 often needs to remember what is the "active" widget). To do so they need a unique ID. Unique ID 458 are typically derived from a string label, an integer index or a pointer. 459 460 Button("OK"); // Label = "OK", ID = top of id stack + hash of "OK" 461 Button("Cancel"); // Label = "Cancel", ID = top of id stack + hash of "Cancel" 462 463 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having 464 two buttons labeled "OK" in different windows or different tree locations is fine. 465 466 - If you have a same ID twice in the same location, you'll have a conflict: 467 468 Button("OK"); 469 Button("OK"); // ID collision! Interacting with either button will trigger the first one. 470 471 Fear not! this is easy to solve and there are many ways to solve it! 472 473 - Solving ID conflict in a simple/local context: 474 When passing a label you can optionally specify extra ID information within string itself. 475 Use "##" to pass a complement to the ID that won't be visible to the end-user. 476 This helps solving the simple collision cases when you know e.g. at compilation time which items 477 are going to be created: 478 479 Button("Play"); // Label = "Play", ID = top of id stack + hash of "Play" 480 Button("Play##foo1"); // Label = "Play", ID = top of id stack + hash of "Play##foo1" (different from above) 481 Button("Play##foo2"); // Label = "Play", ID = top of id stack + hash of "Play##foo2" (different from above) 482 483 - If you want to completely hide the label, but still need an ID: 484 485 Checkbox("##On", &b); // Label = "", ID = top of id stack + hash of "##On" (no label!) 486 487 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows 488 you to animate labels. For example you may want to include varying information in a window title bar, 489 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: 490 491 Button("Hello###ID"; // Label = "Hello", ID = top of id stack + hash of "ID" 492 Button("World###ID"; // Label = "World", ID = top of id stack + hash of "ID" (same as above) 493 494 sprintf(buf, "My game (%f FPS)###MyGame", fps); 495 Begin(buf); // Variable label, ID = hash of "MyGame" 496 497 - Solving ID conflict in a more general manner: 498 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts 499 within the same window. This is the most convenient way of distinguishing ID when iterating and 500 creating many UI elements programmatically. 501 You can push a pointer, a string or an integer value into the ID stack. 502 Remember that ID are formed from the concatenation of _everything_ in the ID stack! 503 504 for (int i = 0; i < 100; i++) 505 { 506 PushID(i); 507 Button("Click"); // Label = "Click", ID = top of id stack + hash of integer + hash of "Click" 508 PopID(); 509 } 510 511 for (int i = 0; i < 100; i++) 512 { 513 MyObject* obj = Objects[i]; 514 PushID(obj); 515 Button("Click"); // Label = "Click", ID = top of id stack + hash of pointer + hash of "Click" 516 PopID(); 517 } 518 519 for (int i = 0; i < 100; i++) 520 { 521 MyObject* obj = Objects[i]; 522 PushID(obj->Name); 523 Button("Click"); // Label = "Click", ID = top of id stack + hash of string + hash of "Click" 524 PopID(); 525 } 526 527 - More example showing that you can stack multiple prefixes into the ID stack: 528 529 Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" 530 PushID("node"); 531 Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" 532 PushID(my_ptr); 533 Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of ptr + hash of "Click" 534 PopID(); 535 PopID(); 536 537 - Tree nodes implicitly creates a scope for you by calling PushID(). 538 539 Button("Click"); // Label = "Click", ID = top of id stack + hash of "Click" 540 if (TreeNode("node")) 541 { 542 Button("Click"); // Label = "Click", ID = top of id stack + hash of "node" + hash of "Click" 543 TreePop(); 544 } 545 546 - When working with trees, ID are used to preserve the open/close state of each tree node. 547 Depending on your use cases you may want to use strings, indices or pointers as ID. 548 e.g. when following a single pointer that may change over time, using a static string as ID 549 will preserve your node open/closed state when the targeted object change. 550 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the 551 node open/closed state differently. See what makes more sense in your situation! 552 553 Q: How can I load a different font than the default? 554 A: Use the font atlas to load the TTF/OTF file you want: 555 ImGuiIO& io = ImGui::GetIO(); 556 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); 557 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() 558 (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code) 559 560 New programmers: remember that in C/C++ and most programming languages if you want to use a 561 backslash \ within a string literal, you need to write it double backslash "\\": 562 io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!) 563 io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT 564 io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT 565 566 Q: How can I easily use icons in my application? 567 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you 568 main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?' 569 and the file 'misc/fonts/README.txt' for instructions and useful header files. 570 571 Q: How can I load multiple fonts? 572 A: Use the font atlas to pack them into a single texture: 573 (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.) 574 575 ImGuiIO& io = ImGui::GetIO(); 576 ImFont* font0 = io.Fonts->AddFontDefault(); 577 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); 578 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); 579 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() 580 // the first loaded font gets used by default 581 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime 582 583 // Options 584 ImFontConfig config; 585 config.OversampleH = 3; 586 config.OversampleV = 1; 587 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up 588 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters 589 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); 590 591 // Combine multiple fonts into one (e.g. for icon fonts) 592 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; 593 ImFontConfig config; 594 config.MergeMode = true; 595 io.Fonts->AddFontDefault(); 596 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font 597 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs 598 599 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? 600 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. 601 602 // Add default Japanese ranges 603 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); 604 605 // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need) 606 ImVector<ImWchar> ranges; 607 ImFontAtlas::GlyphRangesBuilder builder; 608 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) 609 builder.AddChar(0x7262); // Add a specific character 610 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges 611 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted) 612 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data); 613 614 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 615 by using the u8"hello" syntax. Specifying literal in your source code using a local code page 616 (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! 617 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. 618 619 Text input: it is up to your application to pass the right character code by calling 620 io.AddInputCharacter(). The applications in examples/ are doing that. For languages relying 621 on an Input Method Editor (IME), on Windows you can copy the Hwnd of your application in the 622 io.ImeWindowHandle field. The default implementation of io.ImeSetInputScreenPosFn() will set 623 your Microsoft IME position correctly. 624 625 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) 626 A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. 627 Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. 628 - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. 629 - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData. 630 631 Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. 632 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). 633 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension. 634 635 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. 636 A: You are probably mishandling the clipping rectangles in your render function. 637 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). 638 639 Q: How can I help? 640 A: - If you are experienced with Dear ImGui and C++, look at the github issues, or TODO.txt and see how you want/can help! 641 - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. 642 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. 643 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers. 644 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. 645 - 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). 646 647 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. 648 this is also useful to set yourself in the context of another window (to get/set other settings) 649 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug". 650 - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle 651 of a deep nested inner loop in your code. 652 - tip: you can call Render() multiple times (e.g for VR renders). 653 - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui! 47 - FREQUENTLY ASKED QUESTIONS (FAQ) 48 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) 49 50 CODE 51 (search for "[SECTION]" in the code to find them) 52 53 // [SECTION] INCLUDES 54 // [SECTION] FORWARD DECLARATIONS 55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS 56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 59 // [SECTION] MISC HELPERS/UTILITIES (File functions) 60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 61 // [SECTION] MISC HELPERS/UTILITIES (Color functions) 62 // [SECTION] ImGuiStorage 63 // [SECTION] ImGuiTextFilter 64 // [SECTION] ImGuiTextBuffer 65 // [SECTION] ImGuiListClipper 66 // [SECTION] STYLING 67 // [SECTION] RENDER HELPERS 68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 69 // [SECTION] ERROR CHECKING 70 // [SECTION] LAYOUT 71 // [SECTION] SCROLLING 72 // [SECTION] TOOLTIPS 73 // [SECTION] POPUPS 74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION 75 // [SECTION] DRAG AND DROP 76 // [SECTION] LOGGING/CAPTURING 77 // [SECTION] SETTINGS 78 // [SECTION] PLATFORM DEPENDENT HELPERS 79 // [SECTION] METRICS/DEBUG WINDOW 654 80 655 81 */ 82 83 //----------------------------------------------------------------------------- 84 // DOCUMENTATION 85 //----------------------------------------------------------------------------- 86 87 /* 88 89 MISSION STATEMENT 90 ================= 91 92 - Easy to use to create code-driven and data-driven tools. 93 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. 94 - Easy to hack and improve. 95 - Minimize setup and maintenance. 96 - Minimize state storage on user side. 97 - Portable, minimize dependencies, run on target (consoles, phones, etc.). 98 - Efficient runtime and memory consumption. 99 100 Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes: 101 102 - Doesn't look fancy, doesn't animate. 103 - Limited layout features, intricate layouts are typically crafted in code. 104 105 106 END-USER GUIDE 107 ============== 108 109 - Double-click on title bar to collapse window. 110 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). 111 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). 112 - Click and drag on any empty space to move window. 113 - TAB/SHIFT+TAB to cycle through keyboard editable fields. 114 - CTRL+Click on a slider or drag box to input value as text. 115 - Use mouse wheel to scroll. 116 - Text editor: 117 - Hold SHIFT or use mouse to select text. 118 - CTRL+Left/Right to word jump. 119 - CTRL+Shift+Left/Right to select words. 120 - CTRL+A our Double-Click to select all. 121 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ 122 - CTRL+Z,CTRL+Y to undo/redo. 123 - ESCAPE to revert text to its original value. 124 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) 125 - Controls are automatically adjusted for OSX to match standard OSX text editing operations. 126 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. 127 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW 128 129 130 PROGRAMMER GUIDE 131 ================ 132 133 READ FIRST 134 ---------- 135 - Remember to read the FAQ (https://www.dearimgui.org/faq) 136 - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction 137 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. 138 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. 139 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. 140 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). 141 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ. 142 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. 143 For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, 144 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. 145 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. 146 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. 147 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). 148 If you get an assert, read the messages and comments around the assert. 149 - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. 150 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. 151 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. 152 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. 153 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). 154 155 156 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 157 ---------------------------------------------- 158 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) 159 - Or maintain your own branch where you have imconfig.h modified. 160 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. 161 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed 162 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will 163 likely be a comment about it. Please report any issue to the GitHub page! 164 - Try to keep your copy of dear imgui reasonably up to date. 165 166 167 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 168 --------------------------------------------------------------- 169 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. 170 - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder. 171 - Add the Dear ImGui source files + selected back-end source files to your projects or using your preferred build system. 172 It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL). 173 - 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. 174 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. 175 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. 176 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" 177 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render(). 178 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. 179 - 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. 180 181 182 HOW A SIMPLE APPLICATION MAY LOOK LIKE 183 -------------------------------------- 184 EXHIBIT 1: USING THE EXAMPLE BINDINGS (= imgui_impl_XXX.cpp files from the examples/ folder). 185 The sub-folders in examples/ contains examples applications following this structure. 186 187 // Application init: create a dear imgui context, setup some options, load fonts 188 ImGui::CreateContext(); 189 ImGuiIO& io = ImGui::GetIO(); 190 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 191 // TODO: Fill optional fields of the io structure later. 192 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 193 194 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp) 195 ImGui_ImplWin32_Init(hwnd); 196 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); 197 198 // Application main loop 199 while (true) 200 { 201 // Feed inputs to dear imgui, start new frame 202 ImGui_ImplDX11_NewFrame(); 203 ImGui_ImplWin32_NewFrame(); 204 ImGui::NewFrame(); 205 206 // Any application code here 207 ImGui::Text("Hello, world!"); 208 209 // Render dear imgui into screen 210 ImGui::Render(); 211 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); 212 g_pSwapChain->Present(1, 0); 213 } 214 215 // Shutdown 216 ImGui_ImplDX11_Shutdown(); 217 ImGui_ImplWin32_Shutdown(); 218 ImGui::DestroyContext(); 219 220 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE 221 222 // Application init: create a dear imgui context, setup some options, load fonts 223 ImGui::CreateContext(); 224 ImGuiIO& io = ImGui::GetIO(); 225 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 226 // TODO: Fill optional fields of the io structure later. 227 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 228 229 // Build and load the texture atlas into a texture 230 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) 231 int width, height; 232 unsigned char* pixels = NULL; 233 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 234 235 // At this point you've got the texture data and you need to upload that your your graphic system: 236 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. 237 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. 238 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) 239 io.Fonts->TexID = (void*)texture; 240 241 // Application main loop 242 while (true) 243 { 244 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. 245 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings) 246 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) 247 io.DisplaySize.x = 1920.0f; // set the current display width 248 io.DisplaySize.y = 1280.0f; // set the current display height here 249 io.MousePos = my_mouse_pos; // set the mouse position 250 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states 251 io.MouseDown[1] = my_mouse_buttons[1]; 252 253 // Call NewFrame(), after this point you can use ImGui::* functions anytime 254 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere) 255 ImGui::NewFrame(); 256 257 // Most of your application code here 258 ImGui::Text("Hello, world!"); 259 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); 260 MyGameRender(); // may use any Dear ImGui functions as well! 261 262 // Render dear imgui, swap buffers 263 // (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) 264 ImGui::EndFrame(); 265 ImGui::Render(); 266 ImDrawData* draw_data = ImGui::GetDrawData(); 267 MyImGuiRenderFunction(draw_data); 268 SwapBuffers(); 269 } 270 271 // Shutdown 272 ImGui::DestroyContext(); 273 274 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application, 275 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 276 Please read the FAQ and example applications for details about this! 277 278 279 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 280 --------------------------------------------- 281 The bindings in impl_impl_XXX.cpp files contains many working implementations of a rendering function. 282 283 void void MyImGuiRenderFunction(ImDrawData* draw_data) 284 { 285 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled 286 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 287 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 288 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. 289 for (int n = 0; n < draw_data->CmdListsCount; n++) 290 { 291 const ImDrawList* cmd_list = draw_data->CmdLists[n]; 292 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui 293 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui 294 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 295 { 296 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 297 if (pcmd->UserCallback) 298 { 299 pcmd->UserCallback(cmd_list, pcmd); 300 } 301 else 302 { 303 // The texture for the draw call is specified by pcmd->TextureId. 304 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. 305 MyEngineBindTexture((MyTexture*)pcmd->TextureId); 306 307 // We are using scissoring to clip some objects. All low-level graphics API should supports it. 308 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches 309 // (some elements visible outside their bounds) but you can fix that once everything else works! 310 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) 311 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. 312 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), 313 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. 314 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) 315 ImVec2 pos = draw_data->DisplayPos; 316 MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); 317 318 // Render 'pcmd->ElemCount/3' indexed triangles. 319 // 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. 320 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); 321 } 322 idx_buffer += pcmd->ElemCount; 323 } 324 } 325 } 326 327 328 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 329 ------------------------------------------ 330 - The gamepad/keyboard navigation is fairly functional and keeps being improved. 331 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse! 332 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 333 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. 334 - Keyboard: 335 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. 336 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. 337 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag 338 will be set. For more advanced uses, you may want to read from: 339 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. 340 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). 341 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. 342 Please reach out if you think the game vs navigation input sharing could be improved. 343 - Gamepad: 344 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. 345 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). 346 Note that io.NavInputs[] is cleared by EndFrame(). 347 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 348 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. 349 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. 350 Your 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.). 351 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. 352 - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo 353 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. 354 - Mouse: 355 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. 356 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. 357 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. 358 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. 359 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. 360 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. 361 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) 362 (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 363 to set a boolean to ignore your other external mouse positions until the external source is moved again.) 364 365 366 API BREAKING CHANGES 367 ==================== 368 369 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. 370 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. 371 When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. 372 You can read releases logs https://github.com/ocornut/imgui/releases for more details. 373 374 - 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). 375 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). 376 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. 377 - 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. 378 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on 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. 379 - 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! 380 - 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(). 381 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). 382 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions: 383 - if you omitted the 'power' parameter (likely!), you are not affected. 384 - 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. 385 - 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. 386 see https://github.com/ocornut/imgui/issues/3361 for all details. 387 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used. 388 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. 389 - 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. 390 - 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. 391 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79] 392 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017. 393 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular(). 394 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more. 395 - 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. 396 - 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. 397 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017): 398 - ShowTestWindow() -> use ShowDemoWindow() 399 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) 400 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) 401 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) 402 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() 403 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg 404 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding 405 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap 406 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS 407 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API. 408 - 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). 409 - 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. 410 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. 411 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. 412 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): 413 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed 414 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) 415 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding() 416 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f) 417 - ImFont::Glyph -> use ImFontGlyph 418 - 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. 419 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. 420 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). 421 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you. 422 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). 423 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete). 424 - 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. 425 - 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 426 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. 427 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. 428 Please reach out if you are affected. 429 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). 430 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). 431 - 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. 432 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). 433 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). 434 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). 435 - 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 arbitrary small value! 436 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). 437 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! 438 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). 439 - 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. 440 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. 441 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. 442 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). 443 - 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. 444 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. 445 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) 446 - 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. 447 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. 448 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. 449 - 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). 450 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). 451 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). 452 - 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. 453 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. 454 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. 455 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). 456 - 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.). 457 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports. 458 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call. 459 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. 460 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. 461 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. 462 - 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. 463 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. 464 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. 465 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. 466 - 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", 467 consistent with other functions. Kept redirection functions (will obsolete). 468 - 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. 469 - 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 binding ahead of merging the Nav branch). 470 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. 471 - 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. 472 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. 473 - 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. 474 - 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. 475 - 2018/02/07 (1.60) - reorganized context handling to be more explicit, 476 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. 477 - removed Shutdown() function, as DestroyContext() serve this purpose. 478 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. 479 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. 480 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. 481 - 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. 482 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). 483 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). 484 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. 485 - 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. 486 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). 487 - 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 488 - 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. 489 - 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. 490 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). 491 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). 492 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). 493 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). 494 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). 495 - 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. 496 - 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. 497 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. 498 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. 499 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. 500 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. 501 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); 502 - 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. 503 - 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. 504 - 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. 505 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. 506 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) 507 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) 508 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] 509 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! 510 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). 511 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete). 512 - 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). 513 - 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 binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". 514 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! 515 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). 516 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). 517 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. 518 - 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. 519 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. 520 - 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. 521 - 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). 522 - 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). 523 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). 524 - 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. 525 - 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. 526 - 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))' 527 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse 528 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. 529 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. 530 - 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(). 531 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. 532 - 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. 533 - 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. 534 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 535 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 tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. 536 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: 537 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); } 538 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. 539 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). 540 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. 541 - 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). 542 - 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. 543 - 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). 544 - 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) 545 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). 546 - 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. 547 - 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. 548 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. 549 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. 550 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. 551 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. 552 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! 553 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize 554 - 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. 555 - 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 556 - 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. 557 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. 558 - 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. 559 this necessary change will break your rendering function! the fix should be very easy. sorry for that :( 560 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. 561 - the signature of the io.RenderDrawListsFn handler has changed! 562 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) 563 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). 564 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' 565 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. 566 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. 567 - 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. 568 - 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! 569 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! 570 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. 571 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). 572 - 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. 573 - 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 574 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! 575 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). 576 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). 577 - 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. 578 - 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. 579 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). 580 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. 581 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API 582 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. 583 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. 584 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. 585 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing 586 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. 587 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) 588 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. 589 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. 590 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. 591 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior 592 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() 593 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) 594 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. 595 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. 596 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. 597 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; 598 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier; 599 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. 600 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. 601 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) 602 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets 603 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) 604 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) 605 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility 606 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() 607 - 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) 608 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) 609 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() 610 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn 611 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) 612 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite 613 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes 614 615 616 FREQUENTLY ASKED QUESTIONS (FAQ) 617 ================================ 618 619 Read all answers online: 620 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) 621 Read all answers locally (with a text editor or ideally a Markdown viewer): 622 docs/FAQ.md 623 Some answers are copied down here to facilitate searching in code. 624 625 Q&A: Basics 626 =========== 627 628 Q: Where is the documentation? 629 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. 630 - Run the examples/ and explore them. 631 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. 632 - The demo covers most features of Dear ImGui, so you can read the code and see its output. 633 - See documentation and comments at the top of imgui.cpp + effectively imgui.h. 634 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the 635 examples/ folder to explain how to integrate Dear ImGui with your own engine/application. 636 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. 637 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. 638 - Your programming IDE is your friend, find the type or function declaration to find comments 639 associated to it. 640 641 Q: What is this library called? 642 Q: Which version should I get? 643 >> This library is called "Dear ImGui", please don't call it "ImGui" :) 644 >> See https://www.dearimgui.org/faq for details. 645 646 Q&A: Integration 647 ================ 648 649 Q: How to get started? 650 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. 651 652 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application? 653 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 654 >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this. 655 656 Q. How can I enable keyboard controls? 657 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) 658 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text.. 659 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. 660 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries.. 661 >> See https://www.dearimgui.org/faq 662 663 Q&A: Usage 664 ---------- 665 666 Q: Why is my widget not reacting when I click on it? 667 Q: How can I have widgets with an empty label? 668 Q: How can I have multiple widgets with the same label? 669 Q: How can I display an image? What is ImTextureID, how does it works? 670 Q: How can I use my own math types instead of ImVec2/ImVec4? 671 Q: How can I interact with standard C++ types (such as std::string and std::vector)? 672 Q: How can I display custom shapes? (using low-level ImDrawList API) 673 >> See https://www.dearimgui.org/faq 674 675 Q&A: Fonts, Text 676 ================ 677 678 Q: How should I handle DPI in my application? 679 Q: How can I load a different font than the default? 680 Q: How can I easily use icons in my application? 681 Q: How can I load multiple fonts? 682 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? 683 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md 684 685 Q&A: Concerns 686 ============= 687 688 Q: Who uses Dear ImGui? 689 Q: Can you create elaborate/serious tools with Dear ImGui? 690 Q: Can you reskin the look of Dear ImGui? 691 Q: Why using C++ (as opposed to C)? 692 >> See https://www.dearimgui.org/faq 693 694 Q&A: Community 695 ============== 696 697 Q: How can I help? 698 A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui! 699 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. 700 This is among the most useful thing you can do for Dear ImGui. With increased funding we can hire more people working on this project. 701 - Individuals: you can support continued development via PayPal donations. See README. 702 - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt 703 and see how you want to help and can help! 704 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. 705 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3488). Visuals are ideal as they inspire other programmers. 706 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. 707 - 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). 708 709 */ 710 711 //------------------------------------------------------------------------- 712 // [SECTION] INCLUDES 713 //------------------------------------------------------------------------- 656 714 657 715 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) … … 660 718 661 719 #include "imgui.h" 720 #ifndef IMGUI_DISABLE 721 722 #ifndef IMGUI_DEFINE_MATH_OPERATORS 662 723 #define IMGUI_DEFINE_MATH_OPERATORS 724 #endif 663 725 #include "imgui_internal.h" 664 726 665 #include <ctype.h> // toupper, isprint 666 #include < stdlib.h> // NULL, malloc, free, qsort, atoi727 // System includes 728 #include <ctype.h> // toupper 667 729 #include <stdio.h> // vsnprintf, sscanf, printf 668 730 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier … … 672 734 #endif 673 735 674 #define IMGUI_DEBUG_NAV_SCORING 0 675 #define IMGUI_DEBUG_NAV_RECTS 0 736 // [Windows] OS specific includes (optional) 737 #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_WIN32_FUNCTIONS) 738 #define IMGUI_DISABLE_WIN32_FUNCTIONS 739 #endif 740 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) 741 #ifndef WIN32_LEAN_AND_MEAN 742 #define WIN32_LEAN_AND_MEAN 743 #endif 744 #ifndef NOMINMAX 745 #define NOMINMAX 746 #endif 747 #ifndef __MINGW32__ 748 #include <Windows.h> // _wfopen, OpenClipboard 749 #else 750 #include <windows.h> 751 #endif 752 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions 753 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS 754 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS 755 #endif 756 #endif 757 758 // [Apple] OS specific includes 759 #if defined(__APPLE__) 760 #include <TargetConditionals.h> 761 #endif 676 762 677 763 // Visual Studio warnings 678 764 #ifdef _MSC_VER 679 #pragma warning (disable: 4127) // condition expression is constant 680 #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) 681 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 765 #pragma warning (disable: 4127) // condition expression is constant 766 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 767 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later 768 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types 682 769 #endif 683 684 // Clang warnings with -Weverything 685 #ifdef __clang__ 686 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great! 687 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. 688 #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. 689 #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. 690 #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. 691 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. 692 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // 693 #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. 694 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' 770 #endif 771 772 // Clang/GCC warnings with -Weverything 773 #if defined(__clang__) 774 #if __has_warning("-Wunknown-warning-option") 775 #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! 776 #endif 777 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' 778 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. 779 #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. 780 #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. 781 #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. 782 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is. 783 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness 784 #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. 785 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int' 786 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 787 #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. 788 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision 695 789 #elif defined(__GNUC__) 790 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. 791 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind 696 792 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used 697 793 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size … … 701 797 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked 702 798 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false 799 #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 703 800 #endif 704 801 705 // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall 706 #ifdef _MSC_VER 707 #define IMGUI_CDECL __cdecl 708 #else 709 #define IMGUI_CDECL 710 #endif 802 // Debug options 803 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL 804 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window 805 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) 806 807 // 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. 808 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in 809 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear 810 811 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end) 812 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). 813 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. 814 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. 711 815 712 816 //------------------------------------------------------------------------- 713 // Forward Declarations817 // [SECTION] FORWARD DECLARATIONS 714 818 //------------------------------------------------------------------------- 715 819 716 static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true);717 718 static ImFont* GetDefaultFont();719 820 static void SetCurrentWindow(ImGuiWindow* window); 720 static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); 721 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); 722 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); 723 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); 724 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); 725 static ImGuiWindow* FindHoveredWindow(); 726 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); 727 static void CheckStacksSize(ImGuiWindow* window, bool write); 821 static void FindHoveredWindow(); 822 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); 728 823 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); 729 824 730 825 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list); 731 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window); 732 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window); 733 734 static ImGuiWindowSettings* AddWindowSettings(const char* name); 735 736 static void LoadIniSettingsFromDisk(const char* ini_filename); 737 static void LoadIniSettingsFromMemory(const char* buf); 738 static void SaveIniSettingsToDisk(const char* ini_filename); 739 static void SaveIniSettingsToMemory(ImVector<char>& out_buf); 740 static void MarkIniSettingsDirty(ImGuiWindow* window); 826 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window); 741 827 742 828 static ImRect GetViewportRect(); 743 829 744 static void ClosePopupToLevel(int remaining); 745 746 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); 747 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); 748 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); 749 750 static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); 751 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); 752 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); 753 754 namespace ImGui 755 { 756 static void NavUpdate(); 757 static void NavUpdateWindowing(); 758 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); 759 760 static void UpdateMovingWindow(); 761 static void UpdateMouseInputs(); 762 static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); 763 static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); 764 } 765 766 //----------------------------------------------------------------------------- 767 // Platform dependent default implementations 768 //----------------------------------------------------------------------------- 769 830 // Settings 831 static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); 832 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); 833 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); 834 static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); 835 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); 836 837 // Platform Dependents default implementation for IO functions 770 838 static const char* GetClipboardTextFn_DefaultImpl(void* user_data); 771 839 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); 772 840 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); 773 841 842 namespace ImGui 843 { 844 // Navigation 845 static void NavUpdate(); 846 static void NavUpdateWindowing(); 847 static void NavUpdateWindowingOverlay(); 848 static void NavUpdateMoveResult(); 849 static void NavUpdateInitResult(); 850 static float NavUpdatePageUpPageDown(); 851 static inline void NavUpdateAnyRequestFlag(); 852 static void NavEndFrame(); 853 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); 854 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); 855 static ImVec2 NavCalcPreferredRefPos(); 856 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); 857 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); 858 static int FindWindowFocusIndex(ImGuiWindow* window); 859 860 // Error Checking 861 static void ErrorCheckNewFrameSanityChecks(); 862 static void ErrorCheckEndFrameSanityChecks(); 863 static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write); 864 865 // Misc 866 static void UpdateSettings(); 867 static void UpdateMouseInputs(); 868 static void UpdateMouseWheel(); 869 static void UpdateTabFocus(); 870 static void UpdateDebugToolItemPicker(); 871 static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); 872 static void RenderWindowOuterBorders(ImGuiWindow* window); 873 static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); 874 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); 875 876 } 877 774 878 //----------------------------------------------------------------------------- 775 // Context879 // [SECTION] CONTEXT AND MEMORY ALLOCATORS 776 880 //----------------------------------------------------------------------------- 777 881 778 // Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL. 779 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). 780 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file. 781 // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can: 782 // - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 783 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts) 882 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. 883 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). 884 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call 885 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading. 886 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into. 887 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer. 888 // If you want thread-safety to allow N threads to access N different contexts, you can: 889 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h: 890 // struct ImGuiContext; 891 // extern thread_local ImGuiContext* MyImGuiTLS; 892 // #define GImGui MyImGuiTLS 893 // And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. 894 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 895 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. 784 896 #ifndef GImGui 785 897 ImGuiContext* GImGui = NULL; … … 787 899 788 900 // Memory Allocator functions. Use SetAllocatorFunctions() to change them. 789 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. 901 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. 790 902 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. 791 903 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS 792 static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); }793 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); }904 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } 905 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } 794 906 #else 795 static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }796 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); }907 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } 908 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } 797 909 #endif 798 910 799 911 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; 800 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;912 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; 801 913 static void* GImAllocatorUserData = NULL; 802 static size_t GImAllocatorActiveAllocationsCount = 0;803 914 804 915 //----------------------------------------------------------------------------- 805 // User facing structures916 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 806 917 //----------------------------------------------------------------------------- 807 918 808 919 ImGuiStyle::ImGuiStyle() 809 920 { 810 Alpha = 1.0f; // Global alpha applies to everything in ImGui 811 WindowPadding = ImVec2(8, 8); // Padding within a window 812 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows 813 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. 814 WindowMinSize = ImVec2(32, 32); // Minimum window size 815 WindowTitleAlign = ImVec2(0.0f, 0.5f);// Alignment for title bar text 816 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows 817 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. 818 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows 819 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. 820 FramePadding = ImVec2(4, 3); // Padding within a framed rectangle (used by most widgets) 821 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). 822 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. 823 ItemSpacing = ImVec2(8, 4); // Horizontal and vertical spacing between widgets/lines 824 ItemInnerSpacing = ImVec2(4, 4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) 825 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! 826 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). 827 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns 828 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar 829 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar 830 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar 831 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. 832 ButtonTextAlign = ImVec2(0.5f, 0.5f);// Alignment of button text when button is larger than text. 833 DisplayWindowPadding = ImVec2(20, 20); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. 834 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. 835 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. 836 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. 837 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) 838 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. 839 840 // Default theme 841 ImGui::StyleColorsDark(this); 921 Alpha = 1.0f; // Global alpha applies to everything in ImGui 922 WindowPadding = ImVec2(8,8); // Padding within a window 923 WindowRounding = 7.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. 924 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. 925 WindowMinSize = ImVec2(32,32); // Minimum window size 926 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text 927 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. 928 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows 929 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. 930 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows 931 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. 932 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) 933 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). 934 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. 935 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines 936 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) 937 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! 938 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). 939 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). 940 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar 941 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar 942 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar 943 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. 944 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. 945 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. 946 TabBorderSize = 0.0f; // Thickness of border around tabs. 947 TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears 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. 948 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. 949 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. 950 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. 951 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. 952 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. 953 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. 954 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. 955 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering. 956 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). 957 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. 958 CircleSegmentMaxError = 1.60f; // 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. 959 960 // Default theme 961 ImGui::StyleColorsDark(this); 842 962 } 843 963 … … 846 966 void ImGuiStyle::ScaleAllSizes(float scale_factor) 847 967 { 848 WindowPadding = ImFloor(WindowPadding * scale_factor); 849 WindowRounding = ImFloor(WindowRounding * scale_factor); 850 WindowMinSize = ImFloor(WindowMinSize * scale_factor); 851 ChildRounding = ImFloor(ChildRounding * scale_factor); 852 PopupRounding = ImFloor(PopupRounding * scale_factor); 853 FramePadding = ImFloor(FramePadding * scale_factor); 854 FrameRounding = ImFloor(FrameRounding * scale_factor); 855 ItemSpacing = ImFloor(ItemSpacing * scale_factor); 856 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); 857 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); 858 IndentSpacing = ImFloor(IndentSpacing * scale_factor); 859 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); 860 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); 861 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); 862 GrabMinSize = ImFloor(GrabMinSize * scale_factor); 863 GrabRounding = ImFloor(GrabRounding * scale_factor); 864 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); 865 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); 866 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); 968 WindowPadding = ImFloor(WindowPadding * scale_factor); 969 WindowRounding = ImFloor(WindowRounding * scale_factor); 970 WindowMinSize = ImFloor(WindowMinSize * scale_factor); 971 ChildRounding = ImFloor(ChildRounding * scale_factor); 972 PopupRounding = ImFloor(PopupRounding * scale_factor); 973 FramePadding = ImFloor(FramePadding * scale_factor); 974 FrameRounding = ImFloor(FrameRounding * scale_factor); 975 ItemSpacing = ImFloor(ItemSpacing * scale_factor); 976 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); 977 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); 978 IndentSpacing = ImFloor(IndentSpacing * scale_factor); 979 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); 980 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); 981 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); 982 GrabMinSize = ImFloor(GrabMinSize * scale_factor); 983 GrabRounding = ImFloor(GrabRounding * scale_factor); 984 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor); 985 TabRounding = ImFloor(TabRounding * scale_factor); 986 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; 987 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); 988 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); 989 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); 867 990 } 868 991 869 992 ImGuiIO::ImGuiIO() 870 993 { 871 // Most fields are initialized with zero 872 memset(this, 0, sizeof(*this)); 873 874 // Settings 875 ConfigFlags = 0x00; 876 BackendFlags = 0x00; 877 DisplaySize = ImVec2(-1.0f, -1.0f); 878 DeltaTime = 1.0f / 60.0f; 879 IniSavingRate = 5.0f; 880 IniFilename = "imgui.ini"; 881 LogFilename = "imgui_log.txt"; 882 MouseDoubleClickTime = 0.30f; 883 MouseDoubleClickMaxDist = 6.0f; 884 for (int i = 0; i < ImGuiKey_COUNT; i++) 885 KeyMap[i] = -1; 886 KeyRepeatDelay = 0.250f; 887 KeyRepeatRate = 0.050f; 888 UserData = NULL; 889 890 Fonts = NULL; 891 FontGlobalScale = 1.0f; 892 FontDefault = NULL; 893 FontAllowUserScaling = false; 894 DisplayFramebufferScale = ImVec2(1.0f, 1.0f); 895 DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); 896 897 // Advanced/subtle behaviors 994 // Most fields are initialized with zero 995 memset(this, 0, sizeof(*this)); 996 IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here. 997 998 // Settings 999 ConfigFlags = ImGuiConfigFlags_None; 1000 BackendFlags = ImGuiBackendFlags_None; 1001 DisplaySize = ImVec2(-1.0f, -1.0f); 1002 DeltaTime = 1.0f / 60.0f; 1003 IniSavingRate = 5.0f; 1004 IniFilename = "imgui.ini"; 1005 LogFilename = "imgui_log.txt"; 1006 MouseDoubleClickTime = 0.30f; 1007 MouseDoubleClickMaxDist = 6.0f; 1008 for (int i = 0; i < ImGuiKey_COUNT; i++) 1009 KeyMap[i] = -1; 1010 KeyRepeatDelay = 0.275f; 1011 KeyRepeatRate = 0.050f; 1012 UserData = NULL; 1013 1014 Fonts = NULL; 1015 FontGlobalScale = 1.0f; 1016 FontDefault = NULL; 1017 FontAllowUserScaling = false; 1018 DisplayFramebufferScale = ImVec2(1.0f, 1.0f); 1019 1020 // Miscellaneous options 1021 MouseDrawCursor = false; 898 1022 #ifdef __APPLE__ 899 OptMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag1023 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag 900 1024 #else 901 OptMacOSXBehaviors = false;1025 ConfigMacOSXBehaviors = false; 902 1026 #endif 903 OptCursorBlink = true; 904 905 // Settings (User Functions) 906 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations 907 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; 908 ClipboardUserData = NULL; 909 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; 910 ImeWindowHandle = NULL; 1027 ConfigInputTextCursorBlink = true; 1028 ConfigWindowsResizeFromEdges = true; 1029 ConfigWindowsMoveFromTitleBarOnly = false; 1030 ConfigWindowsMemoryCompactTimer = 60.0f; 1031 1032 // Platform Functions 1033 BackendPlatformName = BackendRendererName = NULL; 1034 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; 1035 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations 1036 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; 1037 ClipboardUserData = NULL; 1038 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; 1039 ImeWindowHandle = NULL; 911 1040 912 1041 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 913 RenderDrawListsFn = NULL;1042 RenderDrawListsFn = NULL; 914 1043 #endif 915 1044 916 // Input (NB: we already have memset zero the entire structure)917 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);918 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);919 MouseDragThreshold = 6.0f;920 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;921 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]= KeysDownDurationPrev[i] = -1.0f;922 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;1045 // Input (NB: we already have memset zero the entire structure!) 1046 MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 1047 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); 1048 MouseDragThreshold = 6.0f; 1049 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; 1050 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; 1051 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; 923 1052 } 924 1053 … … 926 1055 // - with glfw you can get those from the callback set in glfwSetCharCallback() 927 1056 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message 928 void ImGuiIO::AddInputCharacter(ImWchar c) 929 { 930 const int n = ImStrlenW(InputCharacters); 931 if (n + 1 < IM_ARRAYSIZE(InputCharacters)) 932 { 933 InputCharacters[n] = c; 934 InputCharacters[n + 1] = '\0'; 935 } 1057 void ImGuiIO::AddInputCharacter(unsigned int c) 1058 { 1059 if (c != 0) 1060 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); 1061 } 1062 1063 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so 1064 // we should save the high surrogate. 1065 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) 1066 { 1067 if (c == 0 && InputQueueSurrogate == 0) 1068 return; 1069 1070 if ((c & 0xFC00) == 0xD800) // High surrogate, must save 1071 { 1072 if (InputQueueSurrogate != 0) 1073 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); 1074 InputQueueSurrogate = c; 1075 return; 1076 } 1077 1078 ImWchar cp = c; 1079 if (InputQueueSurrogate != 0) 1080 { 1081 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate 1082 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); 1083 else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang) 1084 cp = IM_UNICODE_CODEPOINT_INVALID; 1085 else 1086 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); 1087 InputQueueSurrogate = 0; 1088 } 1089 InputQueueCharacters.push_back(cp); 936 1090 } 937 1091 938 1092 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) 939 1093 { 940 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more 941 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); 942 ImWchar wchars[wchars_buf_len]; 943 ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); 944 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) 945 AddInputCharacter(wchars[i]); 1094 while (*utf8_chars != 0) 1095 { 1096 unsigned int c = 0; 1097 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); 1098 if (c != 0) 1099 InputQueueCharacters.push_back((ImWchar)c); 1100 } 1101 } 1102 1103 void ImGuiIO::ClearInputCharacters() 1104 { 1105 InputQueueCharacters.resize(0); 946 1106 } 947 1107 948 1108 //----------------------------------------------------------------------------- 949 // HELPERS1109 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 950 1110 //----------------------------------------------------------------------------- 951 1111 952 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose 953 #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 1112 ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) 1113 { 1114 IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau() 1115 ImVec2 p_last = p1; 1116 ImVec2 p_closest; 1117 float p_closest_dist2 = FLT_MAX; 1118 float t_step = 1.0f / (float)num_segments; 1119 for (int i_step = 1; i_step <= num_segments; i_step++) 1120 { 1121 ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step); 1122 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1123 float dist2 = ImLengthSqr(p - p_line); 1124 if (dist2 < p_closest_dist2) 1125 { 1126 p_closest = p_line; 1127 p_closest_dist2 = dist2; 1128 } 1129 p_last = p_current; 1130 } 1131 return p_closest; 1132 } 1133 1134 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp 1135 static void BezierClosestPointCasteljauStep(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) 1136 { 1137 float dx = x4 - x1; 1138 float dy = y4 - y1; 1139 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); 1140 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); 1141 d2 = (d2 >= 0) ? d2 : -d2; 1142 d3 = (d3 >= 0) ? d3 : -d3; 1143 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) 1144 { 1145 ImVec2 p_current(x4, y4); 1146 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1147 float dist2 = ImLengthSqr(p - p_line); 1148 if (dist2 < p_closest_dist2) 1149 { 1150 p_closest = p_line; 1151 p_closest_dist2 = dist2; 1152 } 1153 p_last = p_current; 1154 } 1155 else if (level < 10) 1156 { 1157 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; 1158 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; 1159 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; 1160 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; 1161 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; 1162 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; 1163 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); 1164 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); 1165 } 1166 } 1167 1168 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol 1169 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. 1170 ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) 1171 { 1172 IM_ASSERT(tess_tol > 0.0f); 1173 ImVec2 p_last = p1; 1174 ImVec2 p_closest; 1175 float p_closest_dist2 = FLT_MAX; 1176 BezierClosestPointCasteljauStep(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); 1177 return p_closest; 1178 } 954 1179 955 1180 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) 956 1181 { 957 ImVec2 ap = p - a;958 ImVec2 ab_dir = b - a;959 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;960 if (dot < 0.0f)961 return a;962 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;963 if (dot > ab_len_sqr)964 return b;965 return a + ab_dir * dot / ab_len_sqr;1182 ImVec2 ap = p - a; 1183 ImVec2 ab_dir = b - a; 1184 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; 1185 if (dot < 0.0f) 1186 return a; 1187 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; 1188 if (dot > ab_len_sqr) 1189 return b; 1190 return a + ab_dir * dot / ab_len_sqr; 966 1191 } 967 1192 968 1193 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 969 1194 { 970 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;971 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;972 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;973 return ((b1 == b2) && (b2 == b3));1195 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; 1196 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; 1197 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; 1198 return ((b1 == b2) && (b2 == b3)); 974 1199 } 975 1200 976 1201 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) 977 1202 { 978 ImVec2 v0 = b - a;979 ImVec2 v1 = c - a;980 ImVec2 v2 = p - a;981 const float denom = v0.x * v1.y - v1.x * v0.y;982 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;983 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;984 out_u = 1.0f - out_v - out_w;1203 ImVec2 v0 = b - a; 1204 ImVec2 v1 = c - a; 1205 ImVec2 v2 = p - a; 1206 const float denom = v0.x * v1.y - v1.x * v0.y; 1207 out_v = (v2.x * v1.y - v1.x * v2.y) / denom; 1208 out_w = (v0.x * v2.y - v2.x * v0.y) / denom; 1209 out_u = 1.0f - out_v - out_w; 985 1210 } 986 1211 987 1212 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 988 1213 { 989 ImVec2 proj_ab = ImLineClosestPoint(a, b, p); 990 ImVec2 proj_bc = ImLineClosestPoint(b, c, p); 991 ImVec2 proj_ca = ImLineClosestPoint(c, a, p); 992 float dist2_ab = ImLengthSqr(p - proj_ab); 993 float dist2_bc = ImLengthSqr(p - proj_bc); 994 float dist2_ca = ImLengthSqr(p - proj_ca); 995 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); 996 if (m == dist2_ab) 997 return proj_ab; 998 if (m == dist2_bc) 999 return proj_bc; 1000 return proj_ca; 1001 } 1002 1214 ImVec2 proj_ab = ImLineClosestPoint(a, b, p); 1215 ImVec2 proj_bc = ImLineClosestPoint(b, c, p); 1216 ImVec2 proj_ca = ImLineClosestPoint(c, a, p); 1217 float dist2_ab = ImLengthSqr(p - proj_ab); 1218 float dist2_bc = ImLengthSqr(p - proj_bc); 1219 float dist2_ca = ImLengthSqr(p - proj_ca); 1220 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); 1221 if (m == dist2_ab) 1222 return proj_ab; 1223 if (m == dist2_bc) 1224 return proj_bc; 1225 return proj_ca; 1226 } 1227 1228 //----------------------------------------------------------------------------- 1229 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 1230 //----------------------------------------------------------------------------- 1231 1232 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. 1003 1233 int ImStricmp(const char* str1, const char* str2) 1004 1234 { 1005 int d;1006 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }1007 return d;1235 int d; 1236 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } 1237 return d; 1008 1238 } 1009 1239 1010 1240 int ImStrnicmp(const char* str1, const char* str2, size_t count) 1011 1241 { 1012 int d = 0;1013 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }1014 return d;1242 int d = 0; 1243 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } 1244 return d; 1015 1245 } 1016 1246 1017 1247 void ImStrncpy(char* dst, const char* src, size_t count) 1018 1248 { 1019 if (count < 1) return; 1020 strncpy(dst, src, count); 1021 dst[count - 1] = 0; 1022 } 1023 1024 char* ImStrdup(const char *str) 1025 { 1026 size_t len = strlen(str) + 1; 1027 void* buf = ImGui::MemAlloc(len); 1028 return (char*)memcpy(buf, (const void*)str, len); 1249 if (count < 1) 1250 return; 1251 if (count > 1) 1252 strncpy(dst, src, count - 1); 1253 dst[count - 1] = 0; 1254 } 1255 1256 char* ImStrdup(const char* str) 1257 { 1258 size_t len = strlen(str); 1259 void* buf = IM_ALLOC(len + 1); 1260 return (char*)memcpy(buf, (const void*)str, len + 1); 1261 } 1262 1263 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) 1264 { 1265 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; 1266 size_t src_size = strlen(src) + 1; 1267 if (dst_buf_size < src_size) 1268 { 1269 IM_FREE(dst); 1270 dst = (char*)IM_ALLOC(src_size); 1271 if (p_dst_size) 1272 *p_dst_size = src_size; 1273 } 1274 return (char*)memcpy(dst, (const void*)src, src_size); 1029 1275 } 1030 1276 1031 1277 const char* ImStrchrRange(const char* str, const char* str_end, char c) 1032 1278 { 1033 for (; str < str_end; str++) 1034 if (*str == c) 1035 return str; 1036 return NULL; 1279 const char* p = (const char*)memchr(str, (int)c, str_end - str); 1280 return p; 1037 1281 } 1038 1282 1039 1283 int ImStrlenW(const ImWchar* str) 1040 1284 { 1041 int n = 0; 1042 while (*str++) n++; 1043 return n; 1285 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit 1286 int n = 0; 1287 while (*str++) n++; 1288 return n; 1289 } 1290 1291 // Find end-of-line. Return pointer will point to either first \n, either str_end. 1292 const char* ImStreolRange(const char* str, const char* str_end) 1293 { 1294 const char* p = (const char*)memchr(str, '\n', str_end - str); 1295 return p ? p : str_end; 1044 1296 } 1045 1297 1046 1298 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line 1047 1299 { 1048 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')1049 buf_mid_line--;1050 return buf_mid_line;1300 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') 1301 buf_mid_line--; 1302 return buf_mid_line; 1051 1303 } 1052 1304 1053 1305 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) 1054 1306 { 1055 if (!needle_end) 1056 needle_end = needle + strlen(needle); 1057 1058 const char un0 = (char)toupper(*needle); 1059 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) 1060 { 1061 if (toupper(*haystack) == un0) 1062 { 1063 const char* b = needle + 1; 1064 for (const char* a = haystack + 1; b < needle_end; a++, b++) 1065 if (toupper(*a) != toupper(*b)) 1066 break; 1067 if (b == needle_end) 1068 return haystack; 1069 } 1070 haystack++; 1071 } 1072 return NULL; 1073 } 1074 1075 static const char* ImAtoi(const char* src, int* output) 1076 { 1077 int negative = 0; 1078 if (*src == '-') { negative = 1; src++; } 1079 if (*src == '+') { src++; } 1080 int v = 0; 1081 while (*src >= '0' && *src <= '9') 1082 v = (v * 10) + (*src++ - '0'); 1083 *output = negative ? -v : v; 1084 return src; 1085 } 1086 1087 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 1307 if (!needle_end) 1308 needle_end = needle + strlen(needle); 1309 1310 const char un0 = (char)toupper(*needle); 1311 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) 1312 { 1313 if (toupper(*haystack) == un0) 1314 { 1315 const char* b = needle + 1; 1316 for (const char* a = haystack + 1; b < needle_end; a++, b++) 1317 if (toupper(*a) != toupper(*b)) 1318 break; 1319 if (b == needle_end) 1320 return haystack; 1321 } 1322 haystack++; 1323 } 1324 return NULL; 1325 } 1326 1327 // 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. 1328 void ImStrTrimBlanks(char* buf) 1329 { 1330 char* p = buf; 1331 while (p[0] == ' ' || p[0] == '\t') // Leading blanks 1332 p++; 1333 char* p_start = p; 1334 while (*p != 0) // Find end of string 1335 p++; 1336 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks 1337 p--; 1338 if (p_start != buf) // Copy memory if we had leading blanks 1339 memmove(buf, p_start, p - p_start); 1340 buf[p - p_start] = 0; // Zero terminate 1341 } 1342 1343 const char* ImStrSkipBlank(const char* str) 1344 { 1345 while (str[0] == ' ' || str[0] == '\t') 1346 str++; 1347 return str; 1348 } 1349 1350 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 1088 1351 // 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. 1089 1352 // B) When buf==NULL vsnprintf() will return the output size. 1090 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS 1353 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1354 1355 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h) 1356 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1357 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are 1358 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) 1359 #ifdef IMGUI_USE_STB_SPRINTF 1360 #define STB_SPRINTF_IMPLEMENTATION 1361 #include "stb_sprintf.h" 1362 #endif 1363 1364 #if defined(_MSC_VER) && !defined(vsnprintf) 1365 #define vsnprintf _vsnprintf 1366 #endif 1367 1091 1368 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) 1092 1369 { 1093 va_list args; 1094 va_start(args, fmt); 1095 int w = vsnprintf(buf, buf_size, fmt, args); 1096 va_end(args); 1097 if (buf == NULL) 1098 return w; 1099 if (w == -1 || w >= (int)buf_size) 1100 w = (int)buf_size - 1; 1101 buf[w] = 0; 1102 return w; 1370 va_list args; 1371 va_start(args, fmt); 1372 #ifdef IMGUI_USE_STB_SPRINTF 1373 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1374 #else 1375 int w = vsnprintf(buf, buf_size, fmt, args); 1376 #endif 1377 va_end(args); 1378 if (buf == NULL) 1379 return w; 1380 if (w == -1 || w >= (int)buf_size) 1381 w = (int)buf_size - 1; 1382 buf[w] = 0; 1383 return w; 1103 1384 } 1104 1385 1105 1386 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) 1106 1387 { 1107 int w = vsnprintf(buf, buf_size, fmt, args); 1108 if (buf == NULL) 1109 return w; 1110 if (w == -1 || w >= (int)buf_size) 1111 w = (int)buf_size - 1; 1112 buf[w] = 0; 1113 return w; 1114 } 1115 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS 1116 1117 // Pass data_size==0 for zero-terminated strings 1388 #ifdef IMGUI_USE_STB_SPRINTF 1389 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1390 #else 1391 int w = vsnprintf(buf, buf_size, fmt, args); 1392 #endif 1393 if (buf == NULL) 1394 return w; 1395 if (w == -1 || w >= (int)buf_size) 1396 w = (int)buf_size - 1; 1397 buf[w] = 0; 1398 return w; 1399 } 1400 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1401 1402 // CRC32 needs a 1KB lookup table (not cache friendly) 1403 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: 1404 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. 1405 static const ImU32 GCrc32LookupTable[256] = 1406 { 1407 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 1408 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, 1409 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, 1410 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, 1411 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, 1412 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, 1413 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, 1414 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, 1415 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, 1416 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, 1417 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, 1418 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, 1419 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, 1420 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, 1421 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, 1422 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, 1423 }; 1424 1425 // Known size hash 1426 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported. 1118 1427 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1119 ImU32 ImHash(const void* data, int data_size, ImU32 seed) 1120 { 1121 static ImU32 crc32_lut[256] = { 0 }; 1122 if (!crc32_lut[1]) 1123 { 1124 const ImU32 polynomial = 0xEDB88320; 1125 for (ImU32 i = 0; i < 256; i++) 1126 { 1127 ImU32 crc = i; 1128 for (ImU32 j = 0; j < 8; j++) 1129 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); 1130 crc32_lut[i] = crc; 1131 } 1132 } 1133 1134 seed = ~seed; 1135 ImU32 crc = seed; 1136 const unsigned char* current = (const unsigned char*)data; 1137 1138 if (data_size > 0) 1139 { 1140 // Known size 1141 while (data_size--) 1142 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; 1143 } 1144 else 1145 { 1146 // Zero-terminated string 1147 while (unsigned char c = *current++) 1148 { 1149 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. 1150 // Because this syntax is rarely used we are optimizing for the common case. 1151 // - If we reach ### in the string we discard the hash so far and reset to the seed. 1152 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. 1153 if (c == '#' && current[0] == '#' && current[1] == '#') 1154 crc = seed; 1155 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1156 } 1157 } 1158 return ~crc; 1428 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed) 1429 { 1430 ImU32 crc = ~seed; 1431 const unsigned char* data = (const unsigned char*)data_p; 1432 const ImU32* crc32_lut = GCrc32LookupTable; 1433 while (data_size-- != 0) 1434 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; 1435 return ~crc; 1436 } 1437 1438 // Zero-terminated string hash, with support for ### to reset back to seed value 1439 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. 1440 // Because this syntax is rarely used we are optimizing for the common case. 1441 // - If we reach ### in the string we discard the hash so far and reset to the seed. 1442 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) 1443 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1444 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed) 1445 { 1446 seed = ~seed; 1447 ImU32 crc = seed; 1448 const unsigned char* data = (const unsigned char*)data_p; 1449 const ImU32* crc32_lut = GCrc32LookupTable; 1450 if (data_size != 0) 1451 { 1452 while (data_size-- != 0) 1453 { 1454 unsigned char c = *data++; 1455 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') 1456 crc = seed; 1457 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1458 } 1459 } 1460 else 1461 { 1462 while (unsigned char c = *data++) 1463 { 1464 if (c == '#' && data[0] == '#' && data[1] == '#') 1465 crc = seed; 1466 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1467 } 1468 } 1469 return ~crc; 1159 1470 } 1160 1471 1161 1472 //----------------------------------------------------------------------------- 1162 // ImText* helpers1473 // [SECTION] MISC HELPERS/UTILITIES (File functions) 1163 1474 //----------------------------------------------------------------------------- 1164 1475 1165 // Convert UTF-8 to 32-bits character, process single character input. 1476 // Default file functions 1477 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1478 1479 ImFileHandle ImFileOpen(const char* filename, const char* mode) 1480 { 1481 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) 1482 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. 1483 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! 1484 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); 1485 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); 1486 ImVector<ImWchar> buf; 1487 buf.resize(filename_wsize + mode_wsize); 1488 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize); 1489 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize); 1490 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]); 1491 #else 1492 return fopen(filename, mode); 1493 #endif 1494 } 1495 1496 // 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. 1497 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; } 1498 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; } 1499 ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); } 1500 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); } 1501 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1502 1503 // Helper: Load file content into memory 1504 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() 1505 // This can't really be used with "rt" because fseek size won't match read size. 1506 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes) 1507 { 1508 IM_ASSERT(filename && mode); 1509 if (out_file_size) 1510 *out_file_size = 0; 1511 1512 ImFileHandle f; 1513 if ((f = ImFileOpen(filename, mode)) == NULL) 1514 return NULL; 1515 1516 size_t file_size = (size_t)ImFileGetSize(f); 1517 if (file_size == (size_t)-1) 1518 { 1519 ImFileClose(f); 1520 return NULL; 1521 } 1522 1523 void* file_data = IM_ALLOC(file_size + padding_bytes); 1524 if (file_data == NULL) 1525 { 1526 ImFileClose(f); 1527 return NULL; 1528 } 1529 if (ImFileRead(file_data, 1, file_size, f) != file_size) 1530 { 1531 ImFileClose(f); 1532 IM_FREE(file_data); 1533 return NULL; 1534 } 1535 if (padding_bytes > 0) 1536 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); 1537 1538 ImFileClose(f); 1539 if (out_file_size) 1540 *out_file_size = file_size; 1541 1542 return file_data; 1543 } 1544 1545 //----------------------------------------------------------------------------- 1546 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 1547 //----------------------------------------------------------------------------- 1548 1549 // Convert UTF-8 to 32-bit character, process single character input. 1166 1550 // Based on stb_from_utf8() from github.com/nothings/stb/ 1167 1551 // We handle UTF-8 decoding error by skipping forward. 1168 1552 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) 1169 1553 { 1170 unsigned int c = (unsigned int)-1; 1171 const unsigned char* str = (const unsigned char*)in_text; 1172 if (!(*str & 0x80)) 1173 { 1174 c = (unsigned int)(*str++); 1175 *out_char = c; 1176 return 1; 1177 } 1178 if ((*str & 0xe0) == 0xc0) 1179 { 1180 *out_char = 0xFFFD; // will be invalid but not end of string 1181 if (in_text_end && in_text_end - (const char*)str < 2) return 1; 1182 if (*str < 0xc2) return 2; 1183 c = (unsigned int)((*str++ & 0x1f) << 6); 1184 if ((*str & 0xc0) != 0x80) return 2; 1185 c += (*str++ & 0x3f); 1186 *out_char = c; 1187 return 2; 1188 } 1189 if ((*str & 0xf0) == 0xe0) 1190 { 1191 *out_char = 0xFFFD; // will be invalid but not end of string 1192 if (in_text_end && in_text_end - (const char*)str < 3) return 1; 1193 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; 1194 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below 1195 c = (unsigned int)((*str++ & 0x0f) << 12); 1196 if ((*str & 0xc0) != 0x80) return 3; 1197 c += (unsigned int)((*str++ & 0x3f) << 6); 1198 if ((*str & 0xc0) != 0x80) return 3; 1199 c += (*str++ & 0x3f); 1200 *out_char = c; 1201 return 3; 1202 } 1203 if ((*str & 0xf8) == 0xf0) 1204 { 1205 *out_char = 0xFFFD; // will be invalid but not end of string 1206 if (in_text_end && in_text_end - (const char*)str < 4) return 1; 1207 if (*str > 0xf4) return 4; 1208 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; 1209 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below 1210 c = (unsigned int)((*str++ & 0x07) << 18); 1211 if ((*str & 0xc0) != 0x80) return 4; 1212 c += (unsigned int)((*str++ & 0x3f) << 12); 1213 if ((*str & 0xc0) != 0x80) return 4; 1214 c += (unsigned int)((*str++ & 0x3f) << 6); 1215 if ((*str & 0xc0) != 0x80) return 4; 1216 c += (*str++ & 0x3f); 1217 // utf-8 encodings of values used in surrogate pairs are invalid 1218 if ((c & 0xFFFFF800) == 0xD800) return 4; 1219 *out_char = c; 1220 return 4; 1221 } 1222 *out_char = 0; 1223 return 0; 1554 unsigned int c = (unsigned int)-1; 1555 const unsigned char* str = (const unsigned char*)in_text; 1556 if (!(*str & 0x80)) 1557 { 1558 c = (unsigned int)(*str++); 1559 *out_char = c; 1560 return 1; 1561 } 1562 if ((*str & 0xe0) == 0xc0) 1563 { 1564 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1565 if (in_text_end && in_text_end - (const char*)str < 2) return 1; 1566 if (*str < 0xc2) return 2; 1567 c = (unsigned int)((*str++ & 0x1f) << 6); 1568 if ((*str & 0xc0) != 0x80) return 2; 1569 c += (*str++ & 0x3f); 1570 *out_char = c; 1571 return 2; 1572 } 1573 if ((*str & 0xf0) == 0xe0) 1574 { 1575 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1576 if (in_text_end && in_text_end - (const char*)str < 3) return 1; 1577 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; 1578 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below 1579 c = (unsigned int)((*str++ & 0x0f) << 12); 1580 if ((*str & 0xc0) != 0x80) return 3; 1581 c += (unsigned int)((*str++ & 0x3f) << 6); 1582 if ((*str & 0xc0) != 0x80) return 3; 1583 c += (*str++ & 0x3f); 1584 *out_char = c; 1585 return 3; 1586 } 1587 if ((*str & 0xf8) == 0xf0) 1588 { 1589 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1590 if (in_text_end && in_text_end - (const char*)str < 4) return 1; 1591 if (*str > 0xf4) return 4; 1592 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; 1593 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below 1594 c = (unsigned int)((*str++ & 0x07) << 18); 1595 if ((*str & 0xc0) != 0x80) return 4; 1596 c += (unsigned int)((*str++ & 0x3f) << 12); 1597 if ((*str & 0xc0) != 0x80) return 4; 1598 c += (unsigned int)((*str++ & 0x3f) << 6); 1599 if ((*str & 0xc0) != 0x80) return 4; 1600 c += (*str++ & 0x3f); 1601 // utf-8 encodings of values used in surrogate pairs are invalid 1602 if ((c & 0xFFFFF800) == 0xD800) return 4; 1603 // If codepoint does not fit in ImWchar, use replacement character U+FFFD instead 1604 if (c > IM_UNICODE_CODEPOINT_MAX) c = IM_UNICODE_CODEPOINT_INVALID; 1605 *out_char = c; 1606 return 4; 1607 } 1608 *out_char = 0; 1609 return 0; 1224 1610 } 1225 1611 1226 1612 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) 1227 1613 { 1228 ImWchar* buf_out = buf; 1229 ImWchar* buf_end = buf + buf_size; 1230 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) 1231 { 1232 unsigned int c; 1233 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1234 if (c == 0) 1235 break; 1236 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes 1237 *buf_out++ = (ImWchar)c; 1238 } 1239 *buf_out = 0; 1240 if (in_text_remaining) 1241 *in_text_remaining = in_text; 1242 return (int)(buf_out - buf); 1614 ImWchar* buf_out = buf; 1615 ImWchar* buf_end = buf + buf_size; 1616 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) 1617 { 1618 unsigned int c; 1619 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1620 if (c == 0) 1621 break; 1622 *buf_out++ = (ImWchar)c; 1623 } 1624 *buf_out = 0; 1625 if (in_text_remaining) 1626 *in_text_remaining = in_text; 1627 return (int)(buf_out - buf); 1243 1628 } 1244 1629 1245 1630 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) 1246 1631 { 1247 int char_count = 0; 1248 while ((!in_text_end || in_text < in_text_end) && *in_text) 1249 { 1250 unsigned int c; 1251 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1252 if (c == 0) 1253 break; 1254 if (c < 0x10000) 1255 char_count++; 1256 } 1257 return char_count; 1632 int char_count = 0; 1633 while ((!in_text_end || in_text < in_text_end) && *in_text) 1634 { 1635 unsigned int c; 1636 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1637 if (c == 0) 1638 break; 1639 char_count++; 1640 } 1641 return char_count; 1258 1642 } 1259 1643 … … 1261 1645 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) 1262 1646 { 1263 if (c < 0x80) 1264 { 1265 buf[0] = (char)c; 1266 return 1; 1267 } 1268 if (c < 0x800) 1269 { 1270 if (buf_size < 2) return 0; 1271 buf[0] = (char)(0xc0 + (c >> 6)); 1272 buf[1] = (char)(0x80 + (c & 0x3f)); 1273 return 2; 1274 } 1275 if (c >= 0xdc00 && c < 0xe000) 1276 { 1277 return 0; 1278 } 1279 if (c >= 0xd800 && c < 0xdc00) 1280 { 1281 if (buf_size < 4) return 0; 1282 buf[0] = (char)(0xf0 + (c >> 18)); 1283 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); 1284 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); 1285 buf[3] = (char)(0x80 + ((c) & 0x3f)); 1286 return 4; 1287 } 1288 //else if (c < 0x10000) 1289 { 1290 if (buf_size < 3) return 0; 1291 buf[0] = (char)(0xe0 + (c >> 12)); 1292 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); 1293 buf[2] = (char)(0x80 + ((c) & 0x3f)); 1294 return 3; 1295 } 1647 if (c < 0x80) 1648 { 1649 buf[0] = (char)c; 1650 return 1; 1651 } 1652 if (c < 0x800) 1653 { 1654 if (buf_size < 2) return 0; 1655 buf[0] = (char)(0xc0 + (c >> 6)); 1656 buf[1] = (char)(0x80 + (c & 0x3f)); 1657 return 2; 1658 } 1659 if (c < 0x10000) 1660 { 1661 if (buf_size < 3) return 0; 1662 buf[0] = (char)(0xe0 + (c >> 12)); 1663 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); 1664 buf[2] = (char)(0x80 + ((c ) & 0x3f)); 1665 return 3; 1666 } 1667 if (c <= 0x10FFFF) 1668 { 1669 if (buf_size < 4) return 0; 1670 buf[0] = (char)(0xf0 + (c >> 18)); 1671 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); 1672 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); 1673 buf[3] = (char)(0x80 + ((c ) & 0x3f)); 1674 return 4; 1675 } 1676 // Invalid code point, the max unicode is 0x10FFFF 1677 return 0; 1678 } 1679 1680 // Not optimal but we very rarely use this function. 1681 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) 1682 { 1683 unsigned int unused = 0; 1684 return ImTextCharFromUtf8(&unused, in_text, in_text_end); 1296 1685 } 1297 1686 1298 1687 static inline int ImTextCountUtf8BytesFromChar(unsigned int c) 1299 1688 { 1300 if (c < 0x80) return 1;1301 if (c < 0x800) return 2;1302 if (c >= 0xdc00 && c < 0xe000) return 0;1303 if (c >= 0xd800 && c < 0xdc00) return 4;1304 return 3;1689 if (c < 0x80) return 1; 1690 if (c < 0x800) return 2; 1691 if (c < 0x10000) return 3; 1692 if (c <= 0x10FFFF) return 4; 1693 return 3; 1305 1694 } 1306 1695 1307 1696 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) 1308 1697 { 1309 char* buf_out = buf;1310 const char* buf_end = buf + buf_size;1311 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)1312 {1313 unsigned int c = (unsigned int)(*in_text++);1314 if (c < 0x80)1315 *buf_out++ = (char)c;1316 else1317 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);1318 }1319 *buf_out = 0;1320 return (int)(buf_out - buf);1698 char* buf_out = buf; 1699 const char* buf_end = buf + buf_size; 1700 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) 1701 { 1702 unsigned int c = (unsigned int)(*in_text++); 1703 if (c < 0x80) 1704 *buf_out++ = (char)c; 1705 else 1706 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); 1707 } 1708 *buf_out = 0; 1709 return (int)(buf_out - buf); 1321 1710 } 1322 1711 1323 1712 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) 1324 1713 { 1325 int bytes_count = 0; 1326 while ((!in_text_end || in_text < in_text_end) && *in_text) 1327 { 1328 unsigned int c = (unsigned int)(*in_text++); 1329 if (c < 0x80) 1330 bytes_count++; 1331 else 1332 bytes_count += ImTextCountUtf8BytesFromChar(c); 1333 } 1334 return bytes_count; 1714 int bytes_count = 0; 1715 while ((!in_text_end || in_text < in_text_end) && *in_text) 1716 { 1717 unsigned int c = (unsigned int)(*in_text++); 1718 if (c < 0x80) 1719 bytes_count++; 1720 else 1721 bytes_count += ImTextCountUtf8BytesFromChar(c); 1722 } 1723 return bytes_count; 1724 } 1725 1726 //----------------------------------------------------------------------------- 1727 // [SECTION] MISC HELPERS/UTILITIES (Color functions) 1728 // Note: The Convert functions are early design which are not consistent with other API. 1729 //----------------------------------------------------------------------------- 1730 1731 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) 1732 { 1733 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; 1734 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); 1735 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); 1736 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); 1737 return IM_COL32(r, g, b, 0xFF); 1335 1738 } 1336 1739 1337 1740 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) 1338 1741 { 1339 float s = 1.0f / 255.0f;1340 return ImVec4(1341 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,1342 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,1343 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,1344 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);1742 float s = 1.0f / 255.0f; 1743 return ImVec4( 1744 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, 1745 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, 1746 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, 1747 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); 1345 1748 } 1346 1749 1347 1750 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) 1348 1751 { 1349 ImU32 out; 1350 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; 1351 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; 1352 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; 1353 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; 1354 return out; 1355 } 1356 1357 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) 1358 { 1359 ImGuiStyle& style = GImGui->Style; 1360 ImVec4 c = style.Colors[idx]; 1361 c.w *= style.Alpha * alpha_mul; 1362 return ColorConvertFloat4ToU32(c); 1363 } 1364 1365 ImU32 ImGui::GetColorU32(const ImVec4& col) 1366 { 1367 ImGuiStyle& style = GImGui->Style; 1368 ImVec4 c = col; 1369 c.w *= style.Alpha; 1370 return ColorConvertFloat4ToU32(c); 1371 } 1372 1373 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) 1374 { 1375 ImGuiStyle& style = GImGui->Style; 1376 return style.Colors[idx]; 1377 } 1378 1379 ImU32 ImGui::GetColorU32(ImU32 col) 1380 { 1381 float style_alpha = GImGui->Style.Alpha; 1382 if (style_alpha >= 1.0f) 1383 return col; 1384 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; 1385 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. 1386 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); 1752 ImU32 out; 1753 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; 1754 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; 1755 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; 1756 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; 1757 return out; 1387 1758 } 1388 1759 … … 1391 1762 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) 1392 1763 { 1393 float K = 0.f;1394 if (g < b)1395 {1396 ImSwap(g, b);1397 K = -1.f;1398 }1399 if (r < g)1400 {1401 ImSwap(r, g);1402 K = -2.f / 6.f - K;1403 }1404 1405 const float chroma = r - (g < b ? g : b);1406 out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f));1407 out_s = chroma / (r + 1e-20f);1408 out_v = r;1764 float K = 0.f; 1765 if (g < b) 1766 { 1767 ImSwap(g, b); 1768 K = -1.f; 1769 } 1770 if (r < g) 1771 { 1772 ImSwap(r, g); 1773 K = -2.f / 6.f - K; 1774 } 1775 1776 const float chroma = r - (g < b ? g : b); 1777 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); 1778 out_s = chroma / (r + 1e-20f); 1779 out_v = r; 1409 1780 } 1410 1781 … … 1413 1784 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) 1414 1785 { 1415 if (s == 0.0f) 1416 { 1417 // gray 1418 out_r = out_g = out_b = v; 1419 return; 1420 } 1421 1422 h = fmodf(h, 1.0f) / (60.0f / 360.0f); 1423 int i = (int)h; 1424 float f = h - (float)i; 1425 float p = v * (1.0f - s); 1426 float q = v * (1.0f - s * f); 1427 float t = v * (1.0f - s * (1.0f - f)); 1428 1429 switch (i) 1430 { 1431 case 0: out_r = v; out_g = t; out_b = p; break; 1432 case 1: out_r = q; out_g = v; out_b = p; break; 1433 case 2: out_r = p; out_g = v; out_b = t; break; 1434 case 3: out_r = p; out_g = q; out_b = v; break; 1435 case 4: out_r = t; out_g = p; out_b = v; break; 1436 case 5: default: out_r = v; out_g = p; out_b = q; break; 1437 } 1438 } 1439 1440 FILE* ImFileOpen(const char* filename, const char* mode) 1441 { 1442 #if defined(_WIN32) && !defined(__CYGWIN__) 1443 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) 1444 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; 1445 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; 1446 ImVector<ImWchar> buf; 1447 buf.resize(filename_wsize + mode_wsize); 1448 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); 1449 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); 1450 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); 1451 #else 1452 return fopen(filename, mode); 1453 #endif 1454 } 1455 1456 // Load file content into memory 1457 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() 1458 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes) 1459 { 1460 IM_ASSERT(filename && file_open_mode); 1461 if (out_file_size) 1462 *out_file_size = 0; 1463 1464 FILE* f; 1465 if ((f = ImFileOpen(filename, file_open_mode)) == NULL) 1466 return NULL; 1467 1468 long file_size_signed; 1469 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) 1470 { 1471 fclose(f); 1472 return NULL; 1473 } 1474 1475 int file_size = (int)file_size_signed; 1476 void* file_data = ImGui::MemAlloc((size_t)(file_size + padding_bytes)); 1477 if (file_data == NULL) 1478 { 1479 fclose(f); 1480 return NULL; 1481 } 1482 if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size) 1483 { 1484 fclose(f); 1485 ImGui::MemFree(file_data); 1486 return NULL; 1487 } 1488 if (padding_bytes > 0) 1489 memset((void *)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); 1490 1491 fclose(f); 1492 if (out_file_size) 1493 *out_file_size = file_size; 1494 1495 return file_data; 1786 if (s == 0.0f) 1787 { 1788 // gray 1789 out_r = out_g = out_b = v; 1790 return; 1791 } 1792 1793 h = ImFmod(h, 1.0f) / (60.0f / 360.0f); 1794 int i = (int)h; 1795 float f = h - (float)i; 1796 float p = v * (1.0f - s); 1797 float q = v * (1.0f - s * f); 1798 float t = v * (1.0f - s * (1.0f - f)); 1799 1800 switch (i) 1801 { 1802 case 0: out_r = v; out_g = t; out_b = p; break; 1803 case 1: out_r = q; out_g = v; out_b = p; break; 1804 case 2: out_r = p; out_g = v; out_b = t; break; 1805 case 3: out_r = p; out_g = q; out_b = v; break; 1806 case 4: out_r = t; out_g = p; out_b = v; break; 1807 case 5: default: out_r = v; out_g = p; out_b = q; break; 1808 } 1496 1809 } 1497 1810 1498 1811 //----------------------------------------------------------------------------- 1499 // ImGuiStorage1812 // [SECTION] ImGuiStorage 1500 1813 // Helper: Key->value storage 1501 1814 //----------------------------------------------------------------------------- 1502 1815 1503 1816 // std::lower_bound but without the bullshit 1504 static Im Vector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)1505 { 1506 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();1507 ImVector<ImGuiStorage::Pair>::iterator last = data.end();1508 size_t count = (size_t)(last - first);1509 while (count > 0)1510 {1511 size_t count2 = count >> 1;1512 ImVector<ImGuiStorage::Pair>::iteratormid = first + count2;1513 if (mid->key < key)1514 {1515 first = ++mid;1516 count -= count2 + 1;1517 }1518 else1519 {1520 count = count2;1521 }1522 }1523 return first;1817 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key) 1818 { 1819 ImGuiStorage::ImGuiStoragePair* first = data.Data; 1820 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size; 1821 size_t count = (size_t)(last - first); 1822 while (count > 0) 1823 { 1824 size_t count2 = count >> 1; 1825 ImGuiStorage::ImGuiStoragePair* mid = first + count2; 1826 if (mid->key < key) 1827 { 1828 first = ++mid; 1829 count -= count2 + 1; 1830 } 1831 else 1832 { 1833 count = count2; 1834 } 1835 } 1836 return first; 1524 1837 } 1525 1838 … … 1527 1840 void ImGuiStorage::BuildSortByKey() 1528 1841 { 1529 struct StaticFunc1530 {1531 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)1532 {1533 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.1534 if (((const Pair*)lhs)->key > ((constPair*)rhs)->key) return +1;1535 if (((const Pair*)lhs)->key < ((constPair*)rhs)->key) return -1;1536 return 0;1537 }1538 };1539 if (Data.Size > 1)1540 qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);1842 struct StaticFunc 1843 { 1844 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) 1845 { 1846 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. 1847 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; 1848 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1; 1849 return 0; 1850 } 1851 }; 1852 if (Data.Size > 1) 1853 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); 1541 1854 } 1542 1855 1543 1856 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const 1544 1857 { 1545 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);1546 if (it == Data.end() || it->key != key)1547 return default_val;1548 return it->val_i;1858 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1859 if (it == Data.end() || it->key != key) 1860 return default_val; 1861 return it->val_i; 1549 1862 } 1550 1863 1551 1864 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const 1552 1865 { 1553 return GetInt(key, default_val ? 1 : 0) != 0;1866 return GetInt(key, default_val ? 1 : 0) != 0; 1554 1867 } 1555 1868 1556 1869 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const 1557 1870 { 1558 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);1559 if (it == Data.end() || it->key != key)1560 return default_val;1561 return it->val_f;1871 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1872 if (it == Data.end() || it->key != key) 1873 return default_val; 1874 return it->val_f; 1562 1875 } 1563 1876 1564 1877 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const 1565 1878 { 1566 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);1567 if (it == Data.end() || it->key != key)1568 return NULL;1569 return it->val_p;1879 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1880 if (it == Data.end() || it->key != key) 1881 return NULL; 1882 return it->val_p; 1570 1883 } 1571 1884 … … 1573 1886 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) 1574 1887 { 1575 ImVector<Pair>::iteratorit = LowerBound(Data, key);1576 if (it == Data.end() || it->key != key)1577 it = Data.insert(it,Pair(key, default_val));1578 return &it->val_i;1888 ImGuiStoragePair* it = LowerBound(Data, key); 1889 if (it == Data.end() || it->key != key) 1890 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1891 return &it->val_i; 1579 1892 } 1580 1893 1581 1894 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) 1582 1895 { 1583 return (bool*)GetIntRef(key, default_val ? 1 : 0);1896 return (bool*)GetIntRef(key, default_val ? 1 : 0); 1584 1897 } 1585 1898 1586 1899 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) 1587 1900 { 1588 ImVector<Pair>::iteratorit = LowerBound(Data, key);1589 if (it == Data.end() || it->key != key)1590 it = Data.insert(it,Pair(key, default_val));1591 return &it->val_f;1901 ImGuiStoragePair* it = LowerBound(Data, key); 1902 if (it == Data.end() || it->key != key) 1903 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1904 return &it->val_f; 1592 1905 } 1593 1906 1594 1907 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) 1595 1908 { 1596 ImVector<Pair>::iteratorit = LowerBound(Data, key);1597 if (it == Data.end() || it->key != key)1598 it = Data.insert(it,Pair(key, default_val));1599 return &it->val_p;1909 ImGuiStoragePair* it = LowerBound(Data, key); 1910 if (it == Data.end() || it->key != key) 1911 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1912 return &it->val_p; 1600 1913 } 1601 1914 … … 1603 1916 void ImGuiStorage::SetInt(ImGuiID key, int val) 1604 1917 { 1605 ImVector<Pair>::iteratorit = LowerBound(Data, key);1606 if (it == Data.end() || it->key != key)1607 {1608 Data.insert(it,Pair(key, val));1609 return;1610 }1611 it->val_i = val;1918 ImGuiStoragePair* it = LowerBound(Data, key); 1919 if (it == Data.end() || it->key != key) 1920 { 1921 Data.insert(it, ImGuiStoragePair(key, val)); 1922 return; 1923 } 1924 it->val_i = val; 1612 1925 } 1613 1926 1614 1927 void ImGuiStorage::SetBool(ImGuiID key, bool val) 1615 1928 { 1616 SetInt(key, val ? 1 : 0);1929 SetInt(key, val ? 1 : 0); 1617 1930 } 1618 1931 1619 1932 void ImGuiStorage::SetFloat(ImGuiID key, float val) 1620 1933 { 1621 ImVector<Pair>::iteratorit = LowerBound(Data, key);1622 if (it == Data.end() || it->key != key)1623 {1624 Data.insert(it,Pair(key, val));1625 return;1626 }1627 it->val_f = val;1934 ImGuiStoragePair* it = LowerBound(Data, key); 1935 if (it == Data.end() || it->key != key) 1936 { 1937 Data.insert(it, ImGuiStoragePair(key, val)); 1938 return; 1939 } 1940 it->val_f = val; 1628 1941 } 1629 1942 1630 1943 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) 1631 1944 { 1632 ImVector<Pair>::iteratorit = LowerBound(Data, key);1633 if (it == Data.end() || it->key != key)1634 {1635 Data.insert(it,Pair(key, val));1636 return;1637 }1638 it->val_p = val;1945 ImGuiStoragePair* it = LowerBound(Data, key); 1946 if (it == Data.end() || it->key != key) 1947 { 1948 Data.insert(it, ImGuiStoragePair(key, val)); 1949 return; 1950 } 1951 it->val_p = val; 1639 1952 } 1640 1953 1641 1954 void ImGuiStorage::SetAllInt(int v) 1642 1955 { 1643 for (int i = 0; i < Data.Size; i++)1644 Data[i].val_i = v;1956 for (int i = 0; i < Data.Size; i++) 1957 Data[i].val_i = v; 1645 1958 } 1646 1959 1647 1960 //----------------------------------------------------------------------------- 1648 // ImGuiTextFilter1961 // [SECTION] ImGuiTextFilter 1649 1962 //----------------------------------------------------------------------------- 1650 1963 … … 1652 1965 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) 1653 1966 { 1654 if (default_filter)1655 {1656 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));1657 Build();1658 }1659 else1660 {1661 InputBuf[0] = 0;1662 CountGrep = 0;1663 }1967 if (default_filter) 1968 { 1969 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); 1970 Build(); 1971 } 1972 else 1973 { 1974 InputBuf[0] = 0; 1975 CountGrep = 0; 1976 } 1664 1977 } 1665 1978 1666 1979 bool ImGuiTextFilter::Draw(const char* label, float width) 1667 1980 { 1668 if (width != 0.0f) 1669 ImGui::PushItemWidth(width); 1670 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); 1671 if (width != 0.0f) 1672 ImGui::PopItemWidth(); 1673 if (value_changed) 1674 Build(); 1675 return value_changed; 1676 } 1677 1678 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>& out) 1679 { 1680 out.resize(0); 1681 const char* wb = b; 1682 const char* we = wb; 1683 while (we < e) 1684 { 1685 if (*we == separator) 1686 { 1687 out.push_back(TextRange(wb, we)); 1688 wb = we + 1; 1689 } 1690 we++; 1691 } 1692 if (wb != we) 1693 out.push_back(TextRange(wb, we)); 1981 if (width != 0.0f) 1982 ImGui::SetNextItemWidth(width); 1983 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); 1984 if (value_changed) 1985 Build(); 1986 return value_changed; 1987 } 1988 1989 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const 1990 { 1991 out->resize(0); 1992 const char* wb = b; 1993 const char* we = wb; 1994 while (we < e) 1995 { 1996 if (*we == separator) 1997 { 1998 out->push_back(ImGuiTextRange(wb, we)); 1999 wb = we + 1; 2000 } 2001 we++; 2002 } 2003 if (wb != we) 2004 out->push_back(ImGuiTextRange(wb, we)); 1694 2005 } 1695 2006 1696 2007 void ImGuiTextFilter::Build() 1697 2008 { 1698 Filters.resize(0); 1699 TextRange input_range(InputBuf, InputBuf + strlen(InputBuf)); 1700 input_range.split(',', Filters); 1701 1702 CountGrep = 0; 1703 for (int i = 0; i != Filters.Size; i++) 1704 { 1705 Filters[i].trim_blanks(); 1706 if (Filters[i].empty()) 1707 continue; 1708 if (Filters[i].front() != '-') 1709 CountGrep += 1; 1710 } 2009 Filters.resize(0); 2010 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf)); 2011 input_range.split(',', &Filters); 2012 2013 CountGrep = 0; 2014 for (int i = 0; i != Filters.Size; i++) 2015 { 2016 ImGuiTextRange& f = Filters[i]; 2017 while (f.b < f.e && ImCharIsBlankA(f.b[0])) 2018 f.b++; 2019 while (f.e > f.b && ImCharIsBlankA(f.e[-1])) 2020 f.e--; 2021 if (f.empty()) 2022 continue; 2023 if (Filters[i].b[0] != '-') 2024 CountGrep += 1; 2025 } 1711 2026 } 1712 2027 1713 2028 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const 1714 2029 { 1715 if (Filters.empty())1716 return true;1717 1718 if (text == NULL)1719 text = "";1720 1721 for (int i = 0; i != Filters.Size; i++)1722 {1723 constTextRange& f = Filters[i];1724 if (f.empty())1725 continue;1726 if (f.front()== '-')1727 {1728 // Subtract1729 if (ImStristr(text, text_end, f.begin() + 1, f.end()) != NULL)1730 return false;1731 }1732 else1733 {1734 // Grep1735 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)1736 return true;1737 }1738 }1739 1740 // Implicit * grep1741 if (CountGrep == 0)1742 return true;1743 1744 return false;2030 if (Filters.empty()) 2031 return true; 2032 2033 if (text == NULL) 2034 text = ""; 2035 2036 for (int i = 0; i != Filters.Size; i++) 2037 { 2038 const ImGuiTextRange& f = Filters[i]; 2039 if (f.empty()) 2040 continue; 2041 if (f.b[0] == '-') 2042 { 2043 // Subtract 2044 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) 2045 return false; 2046 } 2047 else 2048 { 2049 // Grep 2050 if (ImStristr(text, text_end, f.b, f.e) != NULL) 2051 return true; 2052 } 2053 } 2054 2055 // Implicit * grep 2056 if (CountGrep == 0) 2057 return true; 2058 2059 return false; 1745 2060 } 1746 2061 1747 2062 //----------------------------------------------------------------------------- 1748 // ImGuiTextBuffer2063 // [SECTION] ImGuiTextBuffer 1749 2064 //----------------------------------------------------------------------------- 1750 2065 … … 1752 2067 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. 1753 2068 #ifndef va_copy 2069 #if defined(__GNUC__) || defined(__clang__) 2070 #define va_copy(dest, src) __builtin_va_copy(dest, src) 2071 #else 1754 2072 #define va_copy(dest, src) (dest = src) 1755 2073 #endif 2074 #endif 2075 2076 char ImGuiTextBuffer::EmptyString[1] = { 0 }; 2077 2078 void ImGuiTextBuffer::append(const char* str, const char* str_end) 2079 { 2080 int len = str_end ? (int)(str_end - str) : (int)strlen(str); 2081 2082 // Add zero-terminator the first time 2083 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2084 const int needed_sz = write_off + len; 2085 if (write_off + len >= Buf.Capacity) 2086 { 2087 int new_capacity = Buf.Capacity * 2; 2088 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2089 } 2090 2091 Buf.resize(needed_sz); 2092 memcpy(&Buf[write_off - 1], str, (size_t)len); 2093 Buf[write_off - 1 + len] = 0; 2094 } 2095 2096 void ImGuiTextBuffer::appendf(const char* fmt, ...) 2097 { 2098 va_list args; 2099 va_start(args, fmt); 2100 appendfv(fmt, args); 2101 va_end(args); 2102 } 1756 2103 1757 2104 // Helper: Text buffer for logging/accumulating text 1758 2105 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) 1759 2106 { 1760 va_list args_copy; 1761 va_copy(args_copy, args); 1762 1763 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. 1764 if (len <= 0) 1765 { 1766 va_end(args_copy); 1767 return; 1768 } 1769 1770 const int write_off = Buf.Size; 1771 const int needed_sz = write_off + len; 1772 if (write_off + len >= Buf.Capacity) 1773 { 1774 int double_capacity = Buf.Capacity * 2; 1775 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); 1776 } 1777 1778 Buf.resize(needed_sz); 1779 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); 1780 va_end(args_copy); 1781 } 1782 1783 void ImGuiTextBuffer::appendf(const char* fmt, ...) 1784 { 1785 va_list args; 1786 va_start(args, fmt); 1787 appendfv(fmt, args); 1788 va_end(args); 2107 va_list args_copy; 2108 va_copy(args_copy, args); 2109 2110 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. 2111 if (len <= 0) 2112 { 2113 va_end(args_copy); 2114 return; 2115 } 2116 2117 // Add zero-terminator the first time 2118 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2119 const int needed_sz = write_off + len; 2120 if (write_off + len >= Buf.Capacity) 2121 { 2122 int new_capacity = Buf.Capacity * 2; 2123 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2124 } 2125 2126 Buf.resize(needed_sz); 2127 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); 2128 va_end(args_copy); 1789 2129 } 1790 2130 1791 2131 //----------------------------------------------------------------------------- 1792 // ImGuiSimpleColumns (internal use only) 2132 // [SECTION] ImGuiListClipper 2133 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed 2134 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) 1793 2135 //----------------------------------------------------------------------------- 1794 2136 1795 ImGuiMenuColumns::ImGuiMenuColumns() 1796 { 1797 Count = 0; 1798 Spacing = Width = NextWidth = 0.0f; 1799 memset(Pos, 0, sizeof(Pos)); 1800 memset(NextWidths, 0, sizeof(NextWidths)); 1801 } 1802 1803 void ImGuiMenuColumns::Update(int count, float spacing, bool clear) 1804 { 1805 IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); 1806 Count = count; 1807 Width = NextWidth = 0.0f; 1808 Spacing = spacing; 1809 if (clear) memset(NextWidths, 0, sizeof(NextWidths)); 1810 for (int i = 0; i < Count; i++) 1811 { 1812 if (i > 0 && NextWidths[i] > 0.0f) 1813 Width += Spacing; 1814 Pos[i] = (float)(int)Width; 1815 Width += NextWidths[i]; 1816 NextWidths[i] = 0.0f; 1817 } 1818 } 1819 1820 float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double 1821 { 1822 NextWidth = 0.0f; 1823 NextWidths[0] = ImMax(NextWidths[0], w0); 1824 NextWidths[1] = ImMax(NextWidths[1], w1); 1825 NextWidths[2] = ImMax(NextWidths[2], w2); 1826 for (int i = 0; i < 3; i++) 1827 NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); 1828 return ImMax(Width, NextWidth); 1829 } 1830 1831 float ImGuiMenuColumns::CalcExtraSpace(float avail_w) 1832 { 1833 return ImMax(0.0f, avail_w - Width); 1834 } 1835 1836 //----------------------------------------------------------------------------- 1837 // ImGuiListClipper 1838 //----------------------------------------------------------------------------- 1839 1840 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) 1841 { 1842 // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. 1843 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. 1844 // The clipper should probably have a 4th step to display the last item in a regular manner. 1845 ImGui::SetCursorPosY(pos_y); 1846 ImGuiWindow* window = ImGui::GetCurrentWindow(); 1847 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. 1848 window->DC.PrevLineHeight = (line_height - GImGui->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. 1849 if (window->DC.ColumnsSet) 1850 window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly 1851 } 1852 1853 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 2137 // Helper to calculate coarse clipping of large list of evenly sized items. 2138 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. 2139 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX 2140 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) 2141 { 2142 ImGuiContext& g = *GImGui; 2143 ImGuiWindow* window = g.CurrentWindow; 2144 if (g.LogEnabled) 2145 { 2146 // If logging is active, do not perform any clipping 2147 *out_items_display_start = 0; 2148 *out_items_display_end = items_count; 2149 return; 2150 } 2151 if (window->SkipItems) 2152 { 2153 *out_items_display_start = *out_items_display_end = 0; 2154 return; 2155 } 2156 2157 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect 2158 ImRect unclipped_rect = window->ClipRect; 2159 if (g.NavMoveRequest) 2160 unclipped_rect.Add(g.NavScoringRect); 2161 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) 2162 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); 2163 2164 const ImVec2 pos = window->DC.CursorPos; 2165 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); 2166 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); 2167 2168 // When performing a navigation request, ensure we have one item extra in the direction we are moving to 2169 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) 2170 start--; 2171 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) 2172 end++; 2173 2174 start = ImClamp(start, 0, items_count); 2175 end = ImClamp(end + 1, start, items_count); 2176 *out_items_display_start = start; 2177 *out_items_display_end = end; 2178 } 2179 2180 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) 2181 { 2182 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. 2183 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. 2184 // The clipper should probably have a 4th step to display the last item in a regular manner. 2185 ImGuiContext& g = *GImGui; 2186 ImGuiWindow* window = g.CurrentWindow; 2187 window->DC.CursorPos.y = pos_y; 2188 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); 2189 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. 2190 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. 2191 if (ImGuiColumns* columns = window->DC.CurrentColumns) 2192 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly 2193 } 2194 2195 ImGuiListClipper::ImGuiListClipper() 2196 { 2197 memset(this, 0, sizeof(*this)); 2198 ItemsCount = -1; 2199 } 2200 2201 ImGuiListClipper::~ImGuiListClipper() 2202 { 2203 IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); 2204 } 2205 2206 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 1854 2207 // Use case B: Begin() called from constructor with items_height>0 1855 2208 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. 1856 void ImGuiListClipper::Begin(int count, float items_height) 1857 { 1858 StartPosY = ImGui::GetCursorPosY(); 1859 ItemsHeight = items_height; 1860 ItemsCount = count; 1861 StepNo = 0; 1862 DisplayEnd = DisplayStart = -1; 1863 if (ItemsHeight > 0.0f) 1864 { 1865 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display 1866 if (DisplayStart > 0) 1867 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor 1868 StepNo = 2; 1869 } 2209 void ImGuiListClipper::Begin(int items_count, float items_height) 2210 { 2211 ImGuiContext& g = *GImGui; 2212 ImGuiWindow* window = g.CurrentWindow; 2213 2214 StartPosY = window->DC.CursorPos.y; 2215 ItemsHeight = items_height; 2216 ItemsCount = items_count; 2217 StepNo = 0; 2218 DisplayStart = -1; 2219 DisplayEnd = 0; 1870 2220 } 1871 2221 1872 2222 void ImGuiListClipper::End() 1873 2223 { 1874 if (ItemsCount < 0) 1875 return; 1876 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. 1877 if (ItemsCount < INT_MAX) 1878 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor 1879 ItemsCount = -1; 1880 StepNo = 3; 2224 if (ItemsCount < 0) // Already ended 2225 return; 2226 2227 // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. 2228 if (ItemsCount < INT_MAX && DisplayStart >= 0) 2229 SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); 2230 ItemsCount = -1; 2231 StepNo = 3; 1881 2232 } 1882 2233 1883 2234 bool ImGuiListClipper::Step() 1884 2235 { 1885 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems) 1886 { 1887 ItemsCount = -1; 1888 return false; 1889 } 1890 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. 1891 { 1892 DisplayStart = 0; 1893 DisplayEnd = 1; 1894 StartPosY = ImGui::GetCursorPosY(); 1895 StepNo = 1; 1896 return true; 1897 } 1898 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. 1899 { 1900 if (ItemsCount == 1) { ItemsCount = -1; return false; } 1901 float items_height = ImGui::GetCursorPosY() - StartPosY; 1902 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically 1903 Begin(ItemsCount - 1, items_height); 1904 DisplayStart++; 1905 DisplayEnd++; 1906 StepNo = 3; 1907 return true; 1908 } 1909 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3. 1910 { 1911 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0); 1912 StepNo = 3; 1913 return true; 1914 } 1915 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. 1916 End(); 1917 return false; 2236 ImGuiContext& g = *GImGui; 2237 ImGuiWindow* window = g.CurrentWindow; 2238 2239 // Reached end of list 2240 if (DisplayEnd >= ItemsCount || window->SkipItems) 2241 { 2242 End(); 2243 return false; 2244 } 2245 2246 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) 2247 if (StepNo == 0) 2248 { 2249 StartPosY = window->DC.CursorPos.y; 2250 if (ItemsHeight <= 0.0f) 2251 { 2252 // Submit the first item so we can measure its height (generally it is 0..1) 2253 DisplayStart = 0; 2254 DisplayEnd = 1; 2255 StepNo = 1; 2256 return true; 2257 } 2258 2259 // Already has item height (given by user in Begin): skip to calculating step 2260 DisplayStart = DisplayEnd; 2261 StepNo = 2; 2262 } 2263 2264 // Step 1: the clipper infer height from first element 2265 if (StepNo == 1) 2266 { 2267 IM_ASSERT(ItemsHeight <= 0.0f); 2268 ItemsHeight = window->DC.CursorPos.y - StartPosY; 2269 IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); 2270 StepNo = 2; 2271 } 2272 2273 // Step 2: calculate the actual range of elements to display, and position the cursor before the first element 2274 if (StepNo == 2) 2275 { 2276 IM_ASSERT(ItemsHeight > 0.0f); 2277 2278 int already_submitted = DisplayEnd; 2279 ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); 2280 DisplayStart += already_submitted; 2281 DisplayEnd += already_submitted; 2282 2283 // Seek cursor 2284 if (DisplayStart > already_submitted) 2285 SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); 2286 2287 StepNo = 3; 2288 return true; 2289 } 2290 2291 // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), 2292 // Advance the cursor to the end of the list and then returns 'false' to end the loop. 2293 if (StepNo == 3) 2294 { 2295 // Seek cursor 2296 if (ItemsCount < INT_MAX) 2297 SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor 2298 ItemsCount = -1; 2299 return false; 2300 } 2301 2302 IM_ASSERT(0); 2303 return false; 1918 2304 } 1919 2305 1920 2306 //----------------------------------------------------------------------------- 1921 // ImGuiWindow2307 // [SECTION] STYLING 1922 2308 //----------------------------------------------------------------------------- 1923 2309 2310 ImGuiStyle& ImGui::GetStyle() 2311 { 2312 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 2313 return GImGui->Style; 2314 } 2315 2316 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) 2317 { 2318 ImGuiStyle& style = GImGui->Style; 2319 ImVec4 c = style.Colors[idx]; 2320 c.w *= style.Alpha * alpha_mul; 2321 return ColorConvertFloat4ToU32(c); 2322 } 2323 2324 ImU32 ImGui::GetColorU32(const ImVec4& col) 2325 { 2326 ImGuiStyle& style = GImGui->Style; 2327 ImVec4 c = col; 2328 c.w *= style.Alpha; 2329 return ColorConvertFloat4ToU32(c); 2330 } 2331 2332 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) 2333 { 2334 ImGuiStyle& style = GImGui->Style; 2335 return style.Colors[idx]; 2336 } 2337 2338 ImU32 ImGui::GetColorU32(ImU32 col) 2339 { 2340 ImGuiStyle& style = GImGui->Style; 2341 if (style.Alpha >= 1.0f) 2342 return col; 2343 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; 2344 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. 2345 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); 2346 } 2347 2348 // 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 2349 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) 2350 { 2351 ImGuiContext& g = *GImGui; 2352 ImGuiColorMod backup; 2353 backup.Col = idx; 2354 backup.BackupValue = g.Style.Colors[idx]; 2355 g.ColorModifiers.push_back(backup); 2356 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); 2357 } 2358 2359 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) 2360 { 2361 ImGuiContext& g = *GImGui; 2362 ImGuiColorMod backup; 2363 backup.Col = idx; 2364 backup.BackupValue = g.Style.Colors[idx]; 2365 g.ColorModifiers.push_back(backup); 2366 g.Style.Colors[idx] = col; 2367 } 2368 2369 void ImGui::PopStyleColor(int count) 2370 { 2371 ImGuiContext& g = *GImGui; 2372 while (count > 0) 2373 { 2374 ImGuiColorMod& backup = g.ColorModifiers.back(); 2375 g.Style.Colors[backup.Col] = backup.BackupValue; 2376 g.ColorModifiers.pop_back(); 2377 count--; 2378 } 2379 } 2380 2381 struct ImGuiStyleVarInfo 2382 { 2383 ImGuiDataType Type; 2384 ImU32 Count; 2385 ImU32 Offset; 2386 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } 2387 }; 2388 2389 static const ImGuiStyleVarInfo GStyleVarInfo[] = 2390 { 2391 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha 2392 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding 2393 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding 2394 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize 2395 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize 2396 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign 2397 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding 2398 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize 2399 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding 2400 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize 2401 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding 2402 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding 2403 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize 2404 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing 2405 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing 2406 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing 2407 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize 2408 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding 2409 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize 2410 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding 2411 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding 2412 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign 2413 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign 2414 }; 2415 2416 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) 2417 { 2418 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); 2419 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); 2420 return &GStyleVarInfo[idx]; 2421 } 2422 2423 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) 2424 { 2425 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2426 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) 2427 { 2428 ImGuiContext& g = *GImGui; 2429 float* pvar = (float*)var_info->GetVarPtr(&g.Style); 2430 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 2431 *pvar = val; 2432 return; 2433 } 2434 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); 2435 } 2436 2437 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) 2438 { 2439 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2440 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) 2441 { 2442 ImGuiContext& g = *GImGui; 2443 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); 2444 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 2445 *pvar = val; 2446 return; 2447 } 2448 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); 2449 } 2450 2451 void ImGui::PopStyleVar(int count) 2452 { 2453 ImGuiContext& g = *GImGui; 2454 while (count > 0) 2455 { 2456 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. 2457 ImGuiStyleMod& backup = g.StyleModifiers.back(); 2458 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); 2459 void* data = info->GetVarPtr(&g.Style); 2460 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } 2461 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } 2462 g.StyleModifiers.pop_back(); 2463 count--; 2464 } 2465 } 2466 2467 const char* ImGui::GetStyleColorName(ImGuiCol idx) 2468 { 2469 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; 2470 switch (idx) 2471 { 2472 case ImGuiCol_Text: return "Text"; 2473 case ImGuiCol_TextDisabled: return "TextDisabled"; 2474 case ImGuiCol_WindowBg: return "WindowBg"; 2475 case ImGuiCol_ChildBg: return "ChildBg"; 2476 case ImGuiCol_PopupBg: return "PopupBg"; 2477 case ImGuiCol_Border: return "Border"; 2478 case ImGuiCol_BorderShadow: return "BorderShadow"; 2479 case ImGuiCol_FrameBg: return "FrameBg"; 2480 case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; 2481 case ImGuiCol_FrameBgActive: return "FrameBgActive"; 2482 case ImGuiCol_TitleBg: return "TitleBg"; 2483 case ImGuiCol_TitleBgActive: return "TitleBgActive"; 2484 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; 2485 case ImGuiCol_MenuBarBg: return "MenuBarBg"; 2486 case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; 2487 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; 2488 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; 2489 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; 2490 case ImGuiCol_CheckMark: return "CheckMark"; 2491 case ImGuiCol_SliderGrab: return "SliderGrab"; 2492 case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; 2493 case ImGuiCol_Button: return "Button"; 2494 case ImGuiCol_ButtonHovered: return "ButtonHovered"; 2495 case ImGuiCol_ButtonActive: return "ButtonActive"; 2496 case ImGuiCol_Header: return "Header"; 2497 case ImGuiCol_HeaderHovered: return "HeaderHovered"; 2498 case ImGuiCol_HeaderActive: return "HeaderActive"; 2499 case ImGuiCol_Separator: return "Separator"; 2500 case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; 2501 case ImGuiCol_SeparatorActive: return "SeparatorActive"; 2502 case ImGuiCol_ResizeGrip: return "ResizeGrip"; 2503 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; 2504 case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; 2505 case ImGuiCol_Tab: return "Tab"; 2506 case ImGuiCol_TabHovered: return "TabHovered"; 2507 case ImGuiCol_TabActive: return "TabActive"; 2508 case ImGuiCol_TabUnfocused: return "TabUnfocused"; 2509 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; 2510 case ImGuiCol_PlotLines: return "PlotLines"; 2511 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; 2512 case ImGuiCol_PlotHistogram: return "PlotHistogram"; 2513 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; 2514 case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; 2515 case ImGuiCol_DragDropTarget: return "DragDropTarget"; 2516 case ImGuiCol_NavHighlight: return "NavHighlight"; 2517 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; 2518 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; 2519 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; 2520 } 2521 IM_ASSERT(0); 2522 return "Unknown"; 2523 } 2524 2525 2526 //----------------------------------------------------------------------------- 2527 // [SECTION] RENDER HELPERS 2528 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, 2529 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context. 2530 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context. 2531 //----------------------------------------------------------------------------- 2532 2533 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) 2534 { 2535 const char* text_display_end = text; 2536 if (!text_end) 2537 text_end = (const char*)-1; 2538 2539 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) 2540 text_display_end++; 2541 return text_display_end; 2542 } 2543 2544 // Internal ImGui functions to render text 2545 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() 2546 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) 2547 { 2548 ImGuiContext& g = *GImGui; 2549 ImGuiWindow* window = g.CurrentWindow; 2550 2551 // Hide anything after a '##' string 2552 const char* text_display_end; 2553 if (hide_text_after_hash) 2554 { 2555 text_display_end = FindRenderedTextEnd(text, text_end); 2556 } 2557 else 2558 { 2559 if (!text_end) 2560 text_end = text + strlen(text); // FIXME-OPT 2561 text_display_end = text_end; 2562 } 2563 2564 if (text != text_display_end) 2565 { 2566 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); 2567 if (g.LogEnabled) 2568 LogRenderedText(&pos, text, text_display_end); 2569 } 2570 } 2571 2572 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) 2573 { 2574 ImGuiContext& g = *GImGui; 2575 ImGuiWindow* window = g.CurrentWindow; 2576 2577 if (!text_end) 2578 text_end = text + strlen(text); // FIXME-OPT 2579 2580 if (text != text_end) 2581 { 2582 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); 2583 if (g.LogEnabled) 2584 LogRenderedText(&pos, text, text_end); 2585 } 2586 } 2587 2588 // Default clip_rect uses (pos_min,pos_max) 2589 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) 2590 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) 2591 { 2592 // Perform CPU side clipping for single clipped element to avoid using scissor state 2593 ImVec2 pos = pos_min; 2594 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); 2595 2596 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; 2597 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; 2598 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); 2599 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min 2600 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); 2601 2602 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. 2603 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); 2604 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); 2605 2606 // Render 2607 if (need_clipping) 2608 { 2609 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); 2610 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); 2611 } 2612 else 2613 { 2614 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); 2615 } 2616 } 2617 2618 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) 2619 { 2620 // Hide anything after a '##' string 2621 const char* text_display_end = FindRenderedTextEnd(text, text_end); 2622 const int text_len = (int)(text_display_end - text); 2623 if (text_len == 0) 2624 return; 2625 2626 ImGuiContext& g = *GImGui; 2627 ImGuiWindow* window = g.CurrentWindow; 2628 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); 2629 if (g.LogEnabled) 2630 LogRenderedText(&pos_min, text, text_display_end); 2631 } 2632 2633 2634 // Another overly complex function until we reorganize everything into a nice all-in-one helper. 2635 // 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. 2636 // 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. 2637 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) 2638 { 2639 ImGuiContext& g = *GImGui; 2640 if (text_end_full == NULL) 2641 text_end_full = FindRenderedTextEnd(text); 2642 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); 2643 2644 //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)); 2645 //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)); 2646 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255)); 2647 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. 2648 if (text_size.x > pos_max.x - pos_min.x) 2649 { 2650 // Hello wo... 2651 // | | | 2652 // min max ellipsis_max 2653 // <-> this is generally some padding value 2654 2655 const ImFont* font = draw_list->_Data->Font; 2656 const float font_size = draw_list->_Data->FontSize; 2657 const char* text_end_ellipsis = NULL; 2658 2659 ImWchar ellipsis_char = font->EllipsisChar; 2660 int ellipsis_char_count = 1; 2661 if (ellipsis_char == (ImWchar)-1) 2662 { 2663 ellipsis_char = (ImWchar)'.'; 2664 ellipsis_char_count = 3; 2665 } 2666 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); 2667 2668 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side 2669 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis 2670 2671 if (ellipsis_char_count > 1) 2672 { 2673 // Full ellipsis size without free spacing after it. 2674 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize); 2675 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots; 2676 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots; 2677 } 2678 2679 // We can now claim the space between pos_max.x and ellipsis_max.x 2680 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f); 2681 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; 2682 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) 2683 { 2684 // Always display at least 1 character if there's no room for character + ellipsis 2685 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); 2686 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; 2687 } 2688 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) 2689 { 2690 // 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) 2691 text_end_ellipsis--; 2692 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 2693 } 2694 2695 // Render text, render ellipsis 2696 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); 2697 float ellipsis_x = pos_min.x + text_size_clipped_x; 2698 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x) 2699 for (int i = 0; i < ellipsis_char_count; i++) 2700 { 2701 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char); 2702 ellipsis_x += ellipsis_glyph_width; 2703 } 2704 } 2705 else 2706 { 2707 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); 2708 } 2709 2710 if (g.LogEnabled) 2711 LogRenderedText(&pos_min, text, text_end_full); 2712 } 2713 2714 // Render a rectangle shaped with optional rounding and borders 2715 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) 2716 { 2717 ImGuiContext& g = *GImGui; 2718 ImGuiWindow* window = g.CurrentWindow; 2719 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); 2720 const float border_size = g.Style.FrameBorderSize; 2721 if (border && border_size > 0.0f) 2722 { 2723 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 2724 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 2725 } 2726 } 2727 2728 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) 2729 { 2730 ImGuiContext& g = *GImGui; 2731 ImGuiWindow* window = g.CurrentWindow; 2732 const float border_size = g.Style.FrameBorderSize; 2733 if (border_size > 0.0f) 2734 { 2735 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 2736 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 2737 } 2738 } 2739 2740 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) 2741 { 2742 ImGuiContext& g = *GImGui; 2743 if (id != g.NavId) 2744 return; 2745 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) 2746 return; 2747 ImGuiWindow* window = g.CurrentWindow; 2748 if (window->DC.NavHideHighlightOneFrame) 2749 return; 2750 2751 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; 2752 ImRect display_rect = bb; 2753 display_rect.ClipWith(window->ClipRect); 2754 if (flags & ImGuiNavHighlightFlags_TypeDefault) 2755 { 2756 const float THICKNESS = 2.0f; 2757 const float DISTANCE = 3.0f + THICKNESS * 0.5f; 2758 display_rect.Expand(ImVec2(DISTANCE, DISTANCE)); 2759 bool fully_visible = window->ClipRect.Contains(display_rect); 2760 if (!fully_visible) 2761 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); 2762 window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); 2763 if (!fully_visible) 2764 window->DrawList->PopClipRect(); 2765 } 2766 if (flags & ImGuiNavHighlightFlags_TypeThin) 2767 { 2768 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); 2769 } 2770 } 2771 2772 //----------------------------------------------------------------------------- 2773 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 2774 //----------------------------------------------------------------------------- 2775 2776 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods 1924 2777 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) 1925 : DrawListInst(&context->DrawListSharedData) 1926 { 1927 Name = ImStrdup(name); 1928 ID = ImHash(name, 0); 1929 IDStack.push_back(ID); 1930 Flags = 0; 1931 Pos = ImVec2(0.0f, 0.0f); 1932 Size = SizeFull = ImVec2(0.0f, 0.0f); 1933 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); 1934 WindowPadding = ImVec2(0.0f, 0.0f); 1935 WindowRounding = 0.0f; 1936 WindowBorderSize = 0.0f; 1937 MoveId = GetID("#MOVE"); 1938 ChildId = 0; 1939 Scroll = ImVec2(0.0f, 0.0f); 1940 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 1941 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); 1942 ScrollbarSizes = ImVec2(0.0f, 0.0f); 1943 ScrollbarX = ScrollbarY = false; 1944 Active = WasActive = false; 1945 WriteAccessed = false; 1946 Collapsed = false; 1947 CollapseToggleWanted = false; 1948 SkipItems = false; 1949 Appearing = false; 1950 CloseButton = false; 1951 BeginOrderWithinParent = -1; 1952 BeginOrderWithinContext = -1; 1953 BeginCount = 0; 1954 PopupId = 0; 1955 AutoFitFramesX = AutoFitFramesY = -1; 1956 AutoFitOnlyGrows = false; 1957 AutoFitChildAxises = 0x00; 1958 AutoPosLastDirection = ImGuiDir_None; 1959 HiddenFrames = 0; 1960 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; 1961 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); 1962 1963 LastFrameActive = -1; 1964 ItemWidthDefault = 0.0f; 1965 FontWindowScale = 1.0f; 1966 1967 DrawList = &DrawListInst; 1968 DrawList->_OwnerName = Name; 1969 ParentWindow = NULL; 1970 RootWindow = NULL; 1971 RootWindowForTitleBarHighlight = NULL; 1972 RootWindowForTabbing = NULL; 1973 RootWindowForNav = NULL; 1974 1975 NavLastIds[0] = NavLastIds[1] = 0; 1976 NavRectRel[0] = NavRectRel[1] = ImRect(); 1977 NavLastChildNavWindow = NULL; 1978 1979 FocusIdxAllCounter = FocusIdxTabCounter = -1; 1980 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; 1981 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; 2778 : DrawListInst(&context->DrawListSharedData) 2779 { 2780 Name = ImStrdup(name); 2781 ID = ImHashStr(name); 2782 IDStack.push_back(ID); 2783 Flags = ImGuiWindowFlags_None; 2784 Pos = ImVec2(0.0f, 0.0f); 2785 Size = SizeFull = ImVec2(0.0f, 0.0f); 2786 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f); 2787 WindowPadding = ImVec2(0.0f, 0.0f); 2788 WindowRounding = 0.0f; 2789 WindowBorderSize = 0.0f; 2790 NameBufLen = (int)strlen(name) + 1; 2791 MoveId = GetID("#MOVE"); 2792 ChildId = 0; 2793 Scroll = ImVec2(0.0f, 0.0f); 2794 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 2795 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); 2796 ScrollbarSizes = ImVec2(0.0f, 0.0f); 2797 ScrollbarX = ScrollbarY = false; 2798 Active = WasActive = false; 2799 WriteAccessed = false; 2800 Collapsed = false; 2801 WantCollapseToggle = false; 2802 SkipItems = false; 2803 Appearing = false; 2804 Hidden = false; 2805 IsFallbackWindow = false; 2806 HasCloseButton = false; 2807 ResizeBorderHeld = -1; 2808 BeginCount = 0; 2809 BeginOrderWithinParent = -1; 2810 BeginOrderWithinContext = -1; 2811 PopupId = 0; 2812 AutoFitFramesX = AutoFitFramesY = -1; 2813 AutoFitChildAxises = 0x00; 2814 AutoFitOnlyGrows = false; 2815 AutoPosLastDirection = ImGuiDir_None; 2816 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0; 2817 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; 2818 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); 2819 2820 InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used. 2821 2822 LastFrameActive = -1; 2823 LastTimeActive = -1.0f; 2824 ItemWidthDefault = 0.0f; 2825 FontWindowScale = 1.0f; 2826 SettingsOffset = -1; 2827 2828 DrawList = &DrawListInst; 2829 DrawList->_OwnerName = Name; 2830 ParentWindow = NULL; 2831 RootWindow = NULL; 2832 RootWindowForTitleBarHighlight = NULL; 2833 RootWindowForNav = NULL; 2834 2835 NavLastIds[0] = NavLastIds[1] = 0; 2836 NavRectRel[0] = NavRectRel[1] = ImRect(); 2837 NavLastChildNavWindow = NULL; 2838 2839 MemoryCompacted = false; 2840 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0; 1982 2841 } 1983 2842 1984 2843 ImGuiWindow::~ImGuiWindow() 1985 2844 { 1986 IM_ASSERT(DrawList == &DrawListInst);1987 IM_DELETE(Name);1988 for (int i = 0; i != ColumnsStorage.Size; i++)1989 ColumnsStorage[i].~ImGuiColumnsSet();2845 IM_ASSERT(DrawList == &DrawListInst); 2846 IM_DELETE(Name); 2847 for (int i = 0; i != ColumnsStorage.Size; i++) 2848 ColumnsStorage[i].~ImGuiColumns(); 1990 2849 } 1991 2850 1992 2851 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) 1993 2852 { 1994 ImGuiID seed = IDStack.back(); 1995 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); 1996 ImGui::KeepAliveID(id); 1997 return id; 2853 ImGuiID seed = IDStack.back(); 2854 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2855 ImGui::KeepAliveID(id); 2856 #ifdef IMGUI_ENABLE_TEST_ENGINE 2857 ImGuiContext& g = *GImGui; 2858 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 2859 #endif 2860 return id; 1998 2861 } 1999 2862 2000 2863 ImGuiID ImGuiWindow::GetID(const void* ptr) 2001 2864 { 2002 ImGuiID seed = IDStack.back(); 2003 ImGuiID id = ImHash(&ptr, sizeof(void*), seed); 2004 ImGui::KeepAliveID(id); 2005 return id; 2865 ImGuiID seed = IDStack.back(); 2866 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); 2867 ImGui::KeepAliveID(id); 2868 #ifdef IMGUI_ENABLE_TEST_ENGINE 2869 ImGuiContext& g = *GImGui; 2870 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); 2871 #endif 2872 return id; 2873 } 2874 2875 ImGuiID ImGuiWindow::GetID(int n) 2876 { 2877 ImGuiID seed = IDStack.back(); 2878 ImGuiID id = ImHashData(&n, sizeof(n), seed); 2879 ImGui::KeepAliveID(id); 2880 #ifdef IMGUI_ENABLE_TEST_ENGINE 2881 ImGuiContext& g = *GImGui; 2882 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); 2883 #endif 2884 return id; 2006 2885 } 2007 2886 2008 2887 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) 2009 2888 { 2010 ImGuiID seed = IDStack.back(); 2011 return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); 2889 ImGuiID seed = IDStack.back(); 2890 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2891 #ifdef IMGUI_ENABLE_TEST_ENGINE 2892 ImGuiContext& g = *GImGui; 2893 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 2894 #endif 2895 return id; 2896 } 2897 2898 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) 2899 { 2900 ImGuiID seed = IDStack.back(); 2901 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); 2902 #ifdef IMGUI_ENABLE_TEST_ENGINE 2903 ImGuiContext& g = *GImGui; 2904 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); 2905 #endif 2906 return id; 2907 } 2908 2909 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) 2910 { 2911 ImGuiID seed = IDStack.back(); 2912 ImGuiID id = ImHashData(&n, sizeof(n), seed); 2913 #ifdef IMGUI_ENABLE_TEST_ENGINE 2914 ImGuiContext& g = *GImGui; 2915 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); 2916 #endif 2917 return id; 2012 2918 } 2013 2919 … … 2015 2921 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) 2016 2922 { 2017 ImGuiID seed = IDStack.back(); 2018 const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; 2019 ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); 2020 ImGui::KeepAliveID(id); 2021 return id; 2022 } 2023 2024 //----------------------------------------------------------------------------- 2025 // Internal API exposed in imgui_internal.h 2026 //----------------------------------------------------------------------------- 2923 ImGuiID seed = IDStack.back(); 2924 const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; 2925 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); 2926 ImGui::KeepAliveID(id); 2927 return id; 2928 } 2027 2929 2028 2930 static void SetCurrentWindow(ImGuiWindow* window) 2029 2931 { 2030 ImGuiContext& g = *GImGui; 2031 g.CurrentWindow = window; 2032 if (window) 2033 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 2034 } 2035 2036 static void SetNavID(ImGuiID id, int nav_layer) 2037 { 2038 ImGuiContext& g = *GImGui; 2039 IM_ASSERT(g.NavWindow); 2040 IM_ASSERT(nav_layer == 0 || nav_layer == 1); 2041 g.NavId = id; 2042 g.NavWindow->NavLastIds[nav_layer] = id; 2043 } 2044 2045 static void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel) 2046 { 2047 ImGuiContext& g = *GImGui; 2048 SetNavID(id, nav_layer); 2049 g.NavWindow->NavRectRel[nav_layer] = rect_rel; 2050 g.NavMousePosDirty = true; 2051 g.NavDisableHighlight = false; 2052 g.NavDisableMouseHover = true; 2932 ImGuiContext& g = *GImGui; 2933 g.CurrentWindow = window; 2934 if (window) 2935 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 2936 } 2937 2938 // Free up/compact internal window buffers, we can use this when a window becomes unused. 2939 // This is currently unused by the library, but you may call this yourself for easy GC. 2940 // Not freed: 2941 // - ImGuiWindow, ImGuiWindowSettings, Name 2942 // - StateStorage, ColumnsStorage (may hold useful data) 2943 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. 2944 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) 2945 { 2946 window->MemoryCompacted = true; 2947 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; 2948 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; 2949 window->IDStack.clear(); 2950 window->DrawList->_ClearFreeMemory(); 2951 window->DC.ChildWindows.clear(); 2952 window->DC.ItemFlagsStack.clear(); 2953 window->DC.ItemWidthStack.clear(); 2954 window->DC.TextWrapPosStack.clear(); 2955 window->DC.GroupStack.clear(); 2956 } 2957 2958 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) 2959 { 2960 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening. 2961 // The other buffers tends to amortize much faster. 2962 window->MemoryCompacted = false; 2963 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity); 2964 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity); 2965 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0; 2053 2966 } 2054 2967 2055 2968 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) 2056 2969 { 2057 ImGuiContext& g = *GImGui; 2058 g.ActiveIdIsJustActivated = (g.ActiveId != id); 2059 if (g.ActiveIdIsJustActivated) 2060 g.ActiveIdTimer = 0.0f; 2061 g.ActiveId = id; 2062 g.ActiveIdAllowNavDirFlags = 0; 2063 g.ActiveIdAllowOverlap = false; 2064 g.ActiveIdWindow = window; 2065 if (id) 2066 { 2067 g.ActiveIdIsAlive = true; 2068 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; 2069 } 2070 } 2071 2072 ImGuiID ImGui::GetActiveID() 2073 { 2074 ImGuiContext& g = *GImGui; 2075 return g.ActiveId; 2076 } 2077 2078 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) 2079 { 2080 ImGuiContext& g = *GImGui; 2081 IM_ASSERT(id != 0); 2082 2083 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. 2084 const int nav_layer = window->DC.NavLayerCurrent; 2085 if (g.NavWindow != window) 2086 g.NavInitRequest = false; 2087 g.NavId = id; 2088 g.NavWindow = window; 2089 g.NavLayer = nav_layer; 2090 window->NavLastIds[nav_layer] = id; 2091 if (window->DC.LastItemId == id) 2092 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); 2093 2094 if (g.ActiveIdSource == ImGuiInputSource_Nav) 2095 g.NavDisableMouseHover = true; 2096 else 2097 g.NavDisableHighlight = true; 2970 ImGuiContext& g = *GImGui; 2971 g.ActiveIdIsJustActivated = (g.ActiveId != id); 2972 if (g.ActiveIdIsJustActivated) 2973 { 2974 g.ActiveIdTimer = 0.0f; 2975 g.ActiveIdHasBeenPressedBefore = false; 2976 g.ActiveIdHasBeenEditedBefore = false; 2977 if (id != 0) 2978 { 2979 g.LastActiveId = id; 2980 g.LastActiveIdTimer = 0.0f; 2981 } 2982 } 2983 g.ActiveId = id; 2984 g.ActiveIdAllowOverlap = false; 2985 g.ActiveIdNoClearOnFocusLoss = false; 2986 g.ActiveIdWindow = window; 2987 g.ActiveIdHasBeenEditedThisFrame = false; 2988 if (id) 2989 { 2990 g.ActiveIdIsAlive = id; 2991 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; 2992 } 2993 2994 // Clear declaration of inputs claimed by the widget 2995 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) 2996 g.ActiveIdUsingNavDirMask = 0x00; 2997 g.ActiveIdUsingNavInputMask = 0x00; 2998 g.ActiveIdUsingKeyInputMask = 0x00; 2098 2999 } 2099 3000 2100 3001 void ImGui::ClearActiveID() 2101 3002 { 2102 SetActiveID(0, NULL);3003 SetActiveID(0, NULL); // g.ActiveId = 0; 2103 3004 } 2104 3005 2105 3006 void ImGui::SetHoveredID(ImGuiID id) 2106 3007 { 2107 ImGuiContext& g = *GImGui; 2108 g.HoveredId = id; 2109 g.HoveredIdAllowOverlap = false; 2110 g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f; 3008 ImGuiContext& g = *GImGui; 3009 g.HoveredId = id; 3010 g.HoveredIdAllowOverlap = false; 3011 if (id != 0 && g.HoveredIdPreviousFrame != id) 3012 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; 2111 3013 } 2112 3014 2113 3015 ImGuiID ImGui::GetHoveredID() 2114 3016 { 2115 ImGuiContext& g = *GImGui;2116 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;3017 ImGuiContext& g = *GImGui; 3018 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; 2117 3019 } 2118 3020 2119 3021 void ImGui::KeepAliveID(ImGuiID id) 2120 3022 { 2121 ImGuiContext& g = *GImGui; 2122 if (g.ActiveId == id) 2123 g.ActiveIdIsAlive = true; 3023 ImGuiContext& g = *GImGui; 3024 if (g.ActiveId == id) 3025 g.ActiveIdIsAlive = id; 3026 if (g.ActiveIdPreviousFrame == id) 3027 g.ActiveIdPreviousFrameIsAlive = true; 3028 } 3029 3030 void ImGui::MarkItemEdited(ImGuiID id) 3031 { 3032 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). 3033 // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. 3034 ImGuiContext& g = *GImGui; 3035 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); 3036 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. 3037 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); 3038 g.ActiveIdHasBeenEditedThisFrame = true; 3039 g.ActiveIdHasBeenEditedBefore = true; 3040 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 2124 3041 } 2125 3042 2126 3043 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) 2127 3044 { 2128 // An active popup disable hovering on other windows (apart from its own children) 2129 // FIXME-OPT: This could be cached/stored within the window. 2130 ImGuiContext& g = *GImGui; 2131 if (g.NavWindow) 2132 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) 2133 if (focused_root_window->WasActive && focused_root_window != window->RootWindow) 2134 { 2135 // For the purpose of those flags we differentiate "standard popup" from "modal popup" 2136 // NB: The order of those two tests is important because Modal windows are also Popups. 2137 if (focused_root_window->Flags & ImGuiWindowFlags_Modal) 2138 return false; 2139 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 2140 return false; 2141 } 2142 2143 return true; 2144 } 2145 2146 // Advance cursor given item size for layout. 2147 void ImGui::ItemSize(const ImVec2& size, float text_offset_y) 2148 { 2149 ImGuiContext& g = *GImGui; 2150 ImGuiWindow* window = g.CurrentWindow; 2151 if (window->SkipItems) 2152 return; 2153 2154 // Always align ourselves on pixel boundaries 2155 const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); 2156 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); 2157 //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] 2158 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); 2159 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); 2160 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); 2161 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); 2162 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] 2163 2164 window->DC.PrevLineHeight = line_height; 2165 window->DC.PrevLineTextBaseOffset = text_base_offset; 2166 window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; 2167 2168 // Horizontal layout mode 2169 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 2170 SameLine(); 2171 } 2172 2173 void ImGui::ItemSize(const ImRect& bb, float text_offset_y) 2174 { 2175 ItemSize(bb.GetSize(), text_offset_y); 2176 } 2177 2178 static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy) 2179 { 2180 if (fabsf(dx) > fabsf(dy)) 2181 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; 2182 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; 2183 } 2184 2185 static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) 2186 { 2187 if (a1 < b0) 2188 return a1 - b0; 2189 if (b1 < a0) 2190 return a0 - b1; 2191 return 0.0f; 2192 } 2193 2194 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 2195 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) 2196 { 2197 ImGuiContext& g = *GImGui; 2198 ImGuiWindow* window = g.CurrentWindow; 2199 if (g.NavLayer != window->DC.NavLayerCurrent) 2200 return false; 2201 2202 const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) 2203 g.NavScoringCount++; 2204 2205 // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) 2206 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) 2207 { 2208 cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y); 2209 cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y); 2210 } 2211 else 2212 { 2213 cand.Min.x = ImClamp(cand.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); 2214 cand.Max.x = ImClamp(cand.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); 2215 } 2216 2217 // Compute distance between boxes 2218 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. 2219 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); 2220 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 2221 if (dby != 0.0f && dbx != 0.0f) 2222 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); 2223 float dist_box = fabsf(dbx) + fabsf(dby); 2224 2225 // 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) 2226 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); 2227 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); 2228 float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee) 2229 2230 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance 2231 ImGuiDir quadrant; 2232 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; 2233 if (dbx != 0.0f || dby != 0.0f) 2234 { 2235 // For non-overlapping boxes, use distance between boxes 2236 dax = dbx; 2237 day = dby; 2238 dist_axial = dist_box; 2239 quadrant = NavScoreItemGetQuadrant(dbx, dby); 2240 } 2241 else if (dcx != 0.0f || dcy != 0.0f) 2242 { 2243 // For overlapping boxes with different centers, use distance between centers 2244 dax = dcx; 2245 day = dcy; 2246 dist_axial = dist_center; 2247 quadrant = NavScoreItemGetQuadrant(dcx, dcy); 2248 } 2249 else 2250 { 2251 // 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) 2252 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; 2253 } 2254 2255 #if IMGUI_DEBUG_NAV_SCORING 2256 char buf[128]; 2257 if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) 2258 { 2259 ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); 2260 ImDrawList* draw_list = ImGui::GetOverlayDrawList(); 2261 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100)); 2262 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200)); 2263 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + ImGui::CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 150)); 2264 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); 2265 } 2266 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. 2267 { 2268 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } 2269 if (quadrant == g.NavMoveDir) 2270 { 2271 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); 2272 ImDrawList* draw_list = ImGui::GetOverlayDrawList(); 2273 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); 2274 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); 2275 } 2276 } 2277 #endif 2278 2279 // Is it in the quadrant we're interesting in moving to? 2280 bool new_best = false; 2281 if (quadrant == g.NavMoveDir) 2282 { 2283 // Does it beat the current best candidate? 2284 if (dist_box < result->DistBox) 2285 { 2286 result->DistBox = dist_box; 2287 result->DistCenter = dist_center; 2288 return true; 2289 } 2290 if (dist_box == result->DistBox) 2291 { 2292 // Try using distance between center points to break ties 2293 if (dist_center < result->DistCenter) 2294 { 2295 result->DistCenter = dist_center; 2296 new_best = true; 2297 } 2298 else if (dist_center == result->DistCenter) 2299 { 2300 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items 2301 // (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), 2302 // 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. 2303 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance 2304 new_best = true; 2305 } 2306 } 2307 } 2308 2309 // 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 2310 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) 2311 // 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. 2312 // 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. 2313 // Disabling it may however lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? 2314 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match 2315 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) 2316 if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) 2317 { 2318 result->DistAxial = dist_axial; 2319 new_best = true; 2320 } 2321 2322 return new_best; 2323 } 2324 2325 static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) 2326 { 2327 ImGuiWindow* parent_window = child_window; 2328 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 2329 parent_window = parent_window->ParentWindow; 2330 if (parent_window && parent_window != child_window) 2331 parent_window->NavLastChildNavWindow = child_window; 2332 } 2333 2334 // Call when we are expected to land on Layer 0 after FocusWindow() 2335 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) 2336 { 2337 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; 2338 } 2339 2340 static void NavRestoreLayer(int layer) 2341 { 2342 ImGuiContext& g = *GImGui; 2343 g.NavLayer = layer; 2344 if (layer == 0) 2345 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); 2346 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) 2347 SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); 2348 else 2349 ImGui::NavInitWindow(g.NavWindow, true); 2350 } 2351 2352 static inline void NavUpdateAnyRequestFlag() 2353 { 2354 ImGuiContext& g = *GImGui; 2355 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); 2356 if (g.NavAnyRequest) 2357 IM_ASSERT(g.NavWindow != NULL); 2358 } 2359 2360 static bool NavMoveRequestButNoResultYet() 2361 { 2362 ImGuiContext& g = *GImGui; 2363 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; 2364 } 2365 2366 void ImGui::NavMoveRequestCancel() 2367 { 2368 ImGuiContext& g = *GImGui; 2369 g.NavMoveRequest = false; 2370 NavUpdateAnyRequestFlag(); 2371 } 2372 2373 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) 2374 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) 2375 { 2376 ImGuiContext& g = *GImGui; 2377 //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. 2378 // return; 2379 2380 const ImGuiItemFlags item_flags = window->DC.ItemFlags; 2381 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); 2382 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) 2383 { 2384 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback 2385 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) 2386 { 2387 g.NavInitResultId = id; 2388 g.NavInitResultRectRel = nav_bb_rel; 2389 } 2390 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) 2391 { 2392 g.NavInitRequest = false; // Found a match, clear request 2393 NavUpdateAnyRequestFlag(); 2394 } 2395 } 2396 2397 // Scoring for navigation 2398 if (g.NavId != id && !(item_flags & ImGuiItemFlags_NoNav)) 2399 { 2400 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 2401 #if IMGUI_DEBUG_NAV_SCORING 2402 // [DEBUG] Score all items in NavWindow at all times 2403 if (!g.NavMoveRequest) 2404 g.NavMoveDir = g.NavMoveDirLast; 2405 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; 2406 #else 2407 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); 2408 #endif 2409 if (new_best) 2410 { 2411 result->ID = id; 2412 result->ParentID = window->IDStack.back(); 2413 result->Window = window; 2414 result->RectRel = nav_bb_rel; 2415 } 2416 } 2417 2418 // Update window-relative bounding box of navigated item 2419 if (g.NavId == id) 2420 { 2421 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. 2422 g.NavLayer = window->DC.NavLayerCurrent; 2423 g.NavIdIsAlive = true; 2424 g.NavIdTabCounter = window->FocusIdxTabCounter; 2425 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) 2426 } 2427 } 2428 2429 // Declare item bounding box for clipping and interaction. 2430 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface 2431 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). 2432 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) 2433 { 2434 ImGuiContext& g = *GImGui; 2435 ImGuiWindow* window = g.CurrentWindow; 2436 2437 if (id != 0) 2438 { 2439 // Navigation processing runs prior to clipping early-out 2440 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget 2441 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. 2442 // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. 2443 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) 2444 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; 2445 if (g.NavId == id || g.NavAnyRequest) 2446 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) 2447 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) 2448 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); 2449 } 2450 2451 window->DC.LastItemId = id; 2452 window->DC.LastItemRect = bb; 2453 window->DC.LastItemStatusFlags = 0; 2454 2455 // Clipping test 2456 const bool is_clipped = IsClippedEx(bb, id, false); 2457 if (is_clipped) 2458 return false; 2459 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] 2460 2461 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) 2462 if (IsMouseHoveringRect(bb.Min, bb.Max)) 2463 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; 2464 return true; 3045 // An active popup disable hovering on other windows (apart from its own children) 3046 // FIXME-OPT: This could be cached/stored within the window. 3047 ImGuiContext& g = *GImGui; 3048 if (g.NavWindow) 3049 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) 3050 if (focused_root_window->WasActive && focused_root_window != window->RootWindow) 3051 { 3052 // For the purpose of those flags we differentiate "standard popup" from "modal popup" 3053 // NB: The order of those two tests is important because Modal windows are also Popups. 3054 if (focused_root_window->Flags & ImGuiWindowFlags_Modal) 3055 return false; 3056 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 3057 return false; 3058 } 3059 return true; 2465 3060 } 2466 3061 … … 2470 3065 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) 2471 3066 { 2472 ImGuiContext& g = *GImGui; 2473 ImGuiWindow* window = g.CurrentWindow; 2474 if (g.NavDisableMouseHover && !g.NavDisableHighlight) 2475 return IsItemFocused(); 2476 2477 // Test for bounding box overlap, as updated as ItemAdd() 2478 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 2479 return false; 2480 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function 2481 2482 // Test if we are hovering the right window (our window could be behind another window) 2483 // [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 to use IsItemHovered() after EndChild() itself. 2484 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. 2485 //if (g.HoveredWindow != window) 2486 // return false; 2487 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) 2488 return false; 2489 2490 // Test if another item is active (e.g. being dragged) 2491 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 2492 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) 2493 return false; 2494 2495 // Test if interactions on this window are blocked by an active popup or modal 2496 if (!IsWindowContentHoverable(window, flags)) 2497 return false; 2498 2499 // Test if the item is disabled 2500 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) 2501 return false; 2502 2503 // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case. 2504 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) 2505 return false; 2506 return true; 3067 ImGuiContext& g = *GImGui; 3068 ImGuiWindow* window = g.CurrentWindow; 3069 if (g.NavDisableMouseHover && !g.NavDisableHighlight) 3070 return IsItemFocused(); 3071 3072 // Test for bounding box overlap, as updated as ItemAdd() 3073 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 3074 return false; 3075 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function 3076 3077 // Test if we are hovering the right window (our window could be behind another window) 3078 // [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 to use IsItemHovered() after EndChild() itself. 3079 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. 3080 //if (g.HoveredWindow != window) 3081 // return false; 3082 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) 3083 return false; 3084 3085 // Test if another item is active (e.g. being dragged) 3086 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 3087 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) 3088 return false; 3089 3090 // Test if interactions on this window are blocked by an active popup or modal. 3091 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. 3092 if (!IsWindowContentHoverable(window, flags)) 3093 return false; 3094 3095 // Test if the item is disabled 3096 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) 3097 return false; 3098 3099 // Special handling for calling after Begin() which represent the title bar or tab. 3100 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. 3101 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) 3102 return false; 3103 return true; 2507 3104 } 2508 3105 … … 2510 3107 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) 2511 3108 { 2512 ImGuiContext& g = *GImGui; 2513 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) 2514 return false; 2515 2516 ImGuiWindow* window = g.CurrentWindow; 2517 if (g.HoveredWindow != window) 2518 return false; 2519 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) 2520 return false; 2521 if (!IsMouseHoveringRect(bb.Min, bb.Max)) 2522 return false; 2523 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_Default)) 2524 return false; 2525 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) 2526 return false; 2527 2528 SetHoveredID(id); 2529 return true; 3109 ImGuiContext& g = *GImGui; 3110 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) 3111 return false; 3112 3113 ImGuiWindow* window = g.CurrentWindow; 3114 if (g.HoveredWindow != window) 3115 return false; 3116 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) 3117 return false; 3118 if (!IsMouseHoveringRect(bb.Min, bb.Max)) 3119 return false; 3120 if (g.NavDisableMouseHover) 3121 return false; 3122 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled)) 3123 { 3124 g.HoveredIdDisabled = true; 3125 return false; 3126 } 3127 3128 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level 3129 // hover test in widgets code. We could also decide to split this function is two. 3130 if (id != 0) 3131 { 3132 SetHoveredID(id); 3133 3134 // [DEBUG] Item Picker tool! 3135 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making 3136 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered 3137 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost. 3138 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd(). 3139 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) 3140 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); 3141 if (g.DebugItemPickerBreakId == id) 3142 IM_DEBUG_BREAK(); 3143 } 3144 3145 return true; 2530 3146 } 2531 3147 2532 3148 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) 2533 3149 { 2534 ImGuiContext& g = *GImGui; 2535 ImGuiWindow* window = g.CurrentWindow; 2536 if (!bb.Overlaps(window->ClipRect)) 2537 if (id == 0 || id != g.ActiveId) 2538 if (clip_even_when_logged || !g.LogEnabled) 3150 ImGuiContext& g = *GImGui; 3151 ImGuiWindow* window = g.CurrentWindow; 3152 if (!bb.Overlaps(window->ClipRect)) 3153 if (id == 0 || (id != g.ActiveId && id != g.NavId)) 3154 if (clip_even_when_logged || !g.LogEnabled) 3155 return true; 3156 return false; 3157 } 3158 3159 // This is also inlined in ItemAdd() 3160 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! 3161 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) 3162 { 3163 window->DC.LastItemId = item_id; 3164 window->DC.LastItemStatusFlags = item_flags; 3165 window->DC.LastItemRect = item_rect; 3166 } 3167 3168 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. 3169 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) 3170 { 3171 ImGuiContext& g = *GImGui; 3172 3173 // Increment counters 3174 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; 3175 window->DC.FocusCounterRegular++; 3176 if (is_tab_stop) 3177 window->DC.FocusCounterTabStop++; 3178 3179 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. 3180 // (Note that we can always TAB out of a widget that doesn't allow tabbing in) 3181 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL) 3182 { 3183 g.FocusRequestNextWindow = window; 3184 g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. 3185 } 3186 3187 // Handle focus requests 3188 if (g.FocusRequestCurrWindow == window) 3189 { 3190 if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular) 2539 3191 return true; 2540 return false; 2541 } 2542 2543 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) 2544 { 2545 ImGuiContext& g = *GImGui; 2546 2547 const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus; 2548 window->FocusIdxAllCounter++; 2549 if (allow_keyboard_focus) 2550 window->FocusIdxTabCounter++; 2551 2552 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. 2553 // Note that we can always TAB out of a widget that doesn't allow tabbing in. 2554 if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) 2555 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. 2556 2557 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) 2558 return true; 2559 if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) 2560 { 2561 g.NavJustTabbedId = id; 2562 return true; 2563 } 2564 2565 return false; 3192 if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop) 3193 { 3194 g.NavJustTabbedId = id; 3195 return true; 3196 } 3197 3198 // If another item is about to be focused, we clear our own active id 3199 if (g.ActiveId == id) 3200 ClearActiveID(); 3201 } 3202 3203 return false; 2566 3204 } 2567 3205 2568 3206 void ImGui::FocusableItemUnregister(ImGuiWindow* window) 2569 3207 { 2570 window->FocusIdxAllCounter--; 2571 window->FocusIdxTabCounter--; 2572 } 2573 2574 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) 2575 { 2576 ImGuiContext& g = *GImGui; 2577 ImVec2 content_max; 2578 if (size.x < 0.0f || size.y < 0.0f) 2579 content_max = g.CurrentWindow->Pos + GetContentRegionMax(); 2580 if (size.x <= 0.0f) 2581 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x; 2582 if (size.y <= 0.0f) 2583 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y; 2584 return size; 3208 window->DC.FocusCounterRegular--; 3209 window->DC.FocusCounterTabStop--; 2585 3210 } 2586 3211 2587 3212 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) 2588 3213 { 2589 if (wrap_pos_x < 0.0f) 2590 return 0.0f; 2591 2592 ImGuiWindow* window = GetCurrentWindowRead(); 2593 if (wrap_pos_x == 0.0f) 2594 wrap_pos_x = GetContentRegionMax().x + window->Pos.x; 2595 else if (wrap_pos_x > 0.0f) 2596 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space 2597 2598 return ImMax(wrap_pos_x - pos.x, 1.0f); 2599 } 2600 2601 //----------------------------------------------------------------------------- 2602 2603 void* ImGui::MemAlloc(size_t sz) 2604 { 2605 GImAllocatorActiveAllocationsCount++; 2606 return GImAllocatorAllocFunc(sz, GImAllocatorUserData); 2607 } 2608 3214 if (wrap_pos_x < 0.0f) 3215 return 0.0f; 3216 3217 ImGuiContext& g = *GImGui; 3218 ImGuiWindow* window = g.CurrentWindow; 3219 if (wrap_pos_x == 0.0f) 3220 { 3221 // We could decide to setup a default wrapping max point for auto-resizing windows, 3222 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function? 3223 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) 3224 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x); 3225 //else 3226 wrap_pos_x = window->WorkRect.Max.x; 3227 } 3228 else if (wrap_pos_x > 0.0f) 3229 { 3230 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space 3231 } 3232 3233 return ImMax(wrap_pos_x - pos.x, 1.0f); 3234 } 3235 3236 // IM_ALLOC() == ImGui::MemAlloc() 3237 void* ImGui::MemAlloc(size_t size) 3238 { 3239 if (ImGuiContext* ctx = GImGui) 3240 ctx->IO.MetricsActiveAllocations++; 3241 return GImAllocatorAllocFunc(size, GImAllocatorUserData); 3242 } 3243 3244 // IM_FREE() == ImGui::MemFree() 2609 3245 void ImGui::MemFree(void* ptr) 2610 3246 { 2611 if (ptr) GImAllocatorActiveAllocationsCount--; 2612 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); 3247 if (ptr) 3248 if (ImGuiContext* ctx = GImGui) 3249 ctx->IO.MetricsActiveAllocations--; 3250 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); 2613 3251 } 2614 3252 2615 3253 const char* ImGui::GetClipboardText() 2616 3254 { 2617 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : ""; 3255 ImGuiContext& g = *GImGui; 3256 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : ""; 2618 3257 } 2619 3258 2620 3259 void ImGui::SetClipboardText(const char* text) 2621 3260 { 2622 if (GImGui->IO.SetClipboardTextFn) 2623 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text); 3261 ImGuiContext& g = *GImGui; 3262 if (g.IO.SetClipboardTextFn) 3263 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text); 2624 3264 } 2625 3265 2626 3266 const char* ImGui::GetVersion() 2627 3267 { 2628 return IMGUI_VERSION;2629 } 2630 2631 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself3268 return IMGUI_VERSION; 3269 } 3270 3271 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself 2632 3272 // 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 2633 3273 ImGuiContext* ImGui::GetCurrentContext() 2634 3274 { 2635 return GImGui;3275 return GImGui; 2636 3276 } 2637 3277 … … 2639 3279 { 2640 3280 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC 2641 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.3281 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. 2642 3282 #else 2643 GImGui = ctx;3283 GImGui = ctx; 2644 3284 #endif 2645 3285 } 2646 3286 2647 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit 2648 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic. 2649 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) 2650 { 2651 bool error = false; 2652 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatch version string!"); } 2653 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } 2654 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } 2655 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } 2656 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } 2657 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } 2658 return !error; 2659 } 2660 2661 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data) 2662 { 2663 GImAllocatorAllocFunc = alloc_func; 2664 GImAllocatorFreeFunc = free_func; 2665 GImAllocatorUserData = user_data; 3287 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) 3288 { 3289 GImAllocatorAllocFunc = alloc_func; 3290 GImAllocatorFreeFunc = free_func; 3291 GImAllocatorUserData = user_data; 2666 3292 } 2667 3293 2668 3294 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) 2669 3295 { 2670 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);2671 if (GImGui == NULL)2672 SetCurrentContext(ctx);2673 Initialize(ctx);2674 return ctx;3296 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); 3297 if (GImGui == NULL) 3298 SetCurrentContext(ctx); 3299 Initialize(ctx); 3300 return ctx; 2675 3301 } 2676 3302 2677 3303 void ImGui::DestroyContext(ImGuiContext* ctx) 2678 3304 { 2679 if (ctx == NULL)2680 ctx = GImGui;2681 Shutdown(ctx);2682 if (GImGui == ctx)2683 SetCurrentContext(NULL);2684 IM_DELETE(ctx);3305 if (ctx == NULL) 3306 ctx = GImGui; 3307 Shutdown(ctx); 3308 if (GImGui == ctx) 3309 SetCurrentContext(NULL); 3310 IM_DELETE(ctx); 2685 3311 } 2686 3312 2687 3313 ImGuiIO& ImGui::GetIO() 2688 3314 { 2689 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); 2690 return GImGui->IO; 2691 } 2692 2693 ImGuiStyle& ImGui::GetStyle() 2694 { 2695 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); 2696 return GImGui->Style; 3315 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3316 return GImGui->IO; 2697 3317 } 2698 3318 … … 2700 3320 ImDrawData* ImGui::GetDrawData() 2701 3321 { 2702 ImGuiContext& g = *GImGui;2703 return g.DrawData.Valid ? &g.DrawData : NULL;2704 } 2705 2706 floatImGui::GetTime()2707 { 2708 return GImGui->Time;3322 ImGuiContext& g = *GImGui; 3323 return g.DrawData.Valid ? &g.DrawData : NULL; 3324 } 3325 3326 double ImGui::GetTime() 3327 { 3328 return GImGui->Time; 2709 3329 } 2710 3330 2711 3331 int ImGui::GetFrameCount() 2712 3332 { 2713 return GImGui->FrameCount; 2714 } 2715 2716 ImDrawList* ImGui::GetOverlayDrawList() 2717 { 2718 return &GImGui->OverlayDrawList; 3333 return GImGui->FrameCount; 3334 } 3335 3336 ImDrawList* ImGui::GetBackgroundDrawList() 3337 { 3338 return &GImGui->BackgroundDrawList; 3339 } 3340 3341 ImDrawList* ImGui::GetForegroundDrawList() 3342 { 3343 return &GImGui->ForegroundDrawList; 2719 3344 } 2720 3345 2721 3346 ImDrawListSharedData* ImGui::GetDrawListSharedData() 2722 3347 { 2723 return &GImGui->DrawListSharedData; 2724 } 2725 2726 // This needs to be called before we submit any widget (aka in or before Begin) 2727 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) 2728 { 2729 ImGuiContext& g = *GImGui; 2730 IM_ASSERT(window == g.NavWindow); 2731 bool init_for_nav = false; 2732 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) 2733 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) 2734 init_for_nav = true; 2735 if (init_for_nav) 2736 { 2737 SetNavID(0, g.NavLayer); 2738 g.NavInitRequest = true; 2739 g.NavInitRequestFromMove = false; 2740 g.NavInitResultId = 0; 2741 g.NavInitResultRectRel = ImRect(); 2742 NavUpdateAnyRequestFlag(); 2743 } 2744 else 2745 { 2746 g.NavId = window->NavLastIds[0]; 2747 } 2748 } 2749 2750 static ImVec2 NavCalcPreferredRefPos() 2751 { 2752 ImGuiContext& g = *GImGui; 2753 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) 2754 return ImFloor(g.IO.MousePos); 2755 2756 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item 2757 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; 2758 ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); 2759 ImRect visible_rect = GetViewportRect(); 2760 return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. 2761 } 2762 2763 static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) 2764 { 2765 ImGuiContext& g = *GImGui; 2766 for (int i = g.Windows.Size - 1; i >= 0; i--) 2767 if (g.Windows[i] == window) 2768 return i; 2769 return -1; 2770 } 2771 2772 static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) 2773 { 2774 ImGuiContext& g = *GImGui; 2775 for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) 2776 if (ImGui::IsWindowNavFocusable(g.Windows[i])) 2777 return g.Windows[i]; 2778 return NULL; 2779 } 2780 2781 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) 2782 { 2783 ImGuiContext& g = *GImGui; 2784 if (mode == ImGuiInputReadMode_Down) 2785 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) 2786 2787 const float t = g.IO.NavInputsDownDuration[n]; 2788 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. 2789 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); 2790 if (t < 0.0f) 2791 return 0.0f; 2792 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. 2793 return (t == 0.0f) ? 1.0f : 0.0f; 2794 if (mode == ImGuiInputReadMode_Repeat) 2795 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); 2796 if (mode == ImGuiInputReadMode_RepeatSlow) 2797 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); 2798 if (mode == ImGuiInputReadMode_RepeatFast) 2799 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); 2800 return 0.0f; 2801 } 2802 2803 // Equivalent of IsKeyDown() for NavInputs[] 2804 static bool IsNavInputDown(ImGuiNavInput n) 2805 { 2806 return GImGui->IO.NavInputs[n] > 0.0f; 2807 } 2808 2809 // Equivalent of IsKeyPressed() for NavInputs[] 2810 static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) 2811 { 2812 return ImGui::GetNavInputAmount(n, mode) > 0.0f; 2813 } 2814 2815 static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) 2816 { 2817 return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f; 2818 } 2819 2820 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) 2821 { 2822 ImVec2 delta(0.0f, 0.0f); 2823 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) 2824 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); 2825 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) 2826 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); 2827 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) 2828 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); 2829 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) 2830 delta *= slow_factor; 2831 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) 2832 delta *= fast_factor; 2833 return delta; 2834 } 2835 2836 static void NavUpdateWindowingHighlightWindow(int focus_change_dir) 2837 { 2838 ImGuiContext& g = *GImGui; 2839 IM_ASSERT(g.NavWindowingTarget); 2840 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) 2841 return; 2842 2843 const int i_current = FindWindowIndex(g.NavWindowingTarget); 2844 ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); 2845 if (!window_target) 2846 window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); 2847 g.NavWindowingTarget = window_target; 2848 g.NavWindowingToggleLayer = false; 2849 } 2850 2851 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) 2852 static void ImGui::NavUpdateWindowing() 2853 { 2854 ImGuiContext& g = *GImGui; 2855 ImGuiWindow* apply_focus_window = NULL; 2856 bool apply_toggle_layer = false; 2857 2858 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); 2859 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); 2860 if (start_windowing_with_gamepad || start_windowing_with_keyboard) 2861 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) 2862 { 2863 g.NavWindowingTarget = window->RootWindowForTabbing; 2864 g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; 2865 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; 2866 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; 2867 } 2868 2869 // Gamepad update 2870 g.NavWindowingHighlightTimer += g.IO.DeltaTime; 2871 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) 2872 { 2873 // 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 2874 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); 2875 2876 // Select window to focus 2877 const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); 2878 if (focus_change_dir != 0) 2879 { 2880 NavUpdateWindowingHighlightWindow(focus_change_dir); 2881 g.NavWindowingHighlightAlpha = 1.0f; 2882 } 2883 2884 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) 2885 if (!IsNavInputDown(ImGuiNavInput_Menu)) 2886 { 2887 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. 2888 if (g.NavWindowingToggleLayer && g.NavWindow) 2889 apply_toggle_layer = true; 2890 else if (!g.NavWindowingToggleLayer) 2891 apply_focus_window = g.NavWindowingTarget; 2892 g.NavWindowingTarget = NULL; 2893 } 2894 } 2895 2896 // Keyboard: Focus 2897 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) 2898 { 2899 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise 2900 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f 2901 if (IsKeyPressedMap(ImGuiKey_Tab, true)) 2902 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); 2903 if (!g.IO.KeyCtrl) 2904 apply_focus_window = g.NavWindowingTarget; 2905 } 2906 2907 // Keyboard: Press and Release ALT to toggle menu layer 2908 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB 2909 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) 2910 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) 2911 apply_toggle_layer = true; 2912 2913 // Move window 2914 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) 2915 { 2916 ImVec2 move_delta; 2917 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) 2918 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 2919 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 2920 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); 2921 if (move_delta.x != 0.0f || move_delta.y != 0.0f) 2922 { 2923 const float NAV_MOVE_SPEED = 800.0f; 2924 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well 2925 g.NavWindowingTarget->Pos += move_delta * move_speed; 2926 g.NavDisableMouseHover = true; 2927 MarkIniSettingsDirty(g.NavWindowingTarget); 2928 } 2929 } 2930 2931 // Apply final focus 2932 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowForTabbing)) 2933 { 2934 g.NavDisableHighlight = false; 2935 g.NavDisableMouseHover = true; 2936 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); 2937 ClosePopupsOverWindow(apply_focus_window); 2938 FocusWindow(apply_focus_window); 2939 if (apply_focus_window->NavLastIds[0] == 0) 2940 NavInitWindow(apply_focus_window, false); 2941 2942 // If the window only has a menu layer, select it directly 2943 if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) 2944 g.NavLayer = 1; 2945 } 2946 if (apply_focus_window) 2947 g.NavWindowingTarget = NULL; 2948 2949 // Apply menu/layer toggle 2950 if (apply_toggle_layer && g.NavWindow) 2951 { 2952 ImGuiWindow* new_nav_window = g.NavWindow; 2953 while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 2954 new_nav_window = new_nav_window->ParentWindow; 2955 if (new_nav_window != g.NavWindow) 2956 { 2957 ImGuiWindow* old_nav_window = g.NavWindow; 2958 FocusWindow(new_nav_window); 2959 new_nav_window->NavLastChildNavWindow = old_nav_window; 2960 } 2961 g.NavDisableHighlight = false; 2962 g.NavDisableMouseHover = true; 2963 NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); 2964 } 2965 } 2966 2967 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. 2968 static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_rel) 2969 { 2970 // Scroll to keep newly navigated item fully into view 2971 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); 2972 //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] 2973 if (window_rect_rel.Contains(item_rect_rel)) 2974 return; 2975 2976 ImGuiContext& g = *GImGui; 2977 if (window->ScrollbarX && item_rect_rel.Min.x < window_rect_rel.Min.x) 2978 { 2979 window->ScrollTarget.x = item_rect_rel.Min.x + window->Scroll.x - g.Style.ItemSpacing.x; 2980 window->ScrollTargetCenterRatio.x = 0.0f; 2981 } 2982 else if (window->ScrollbarX && item_rect_rel.Max.x >= window_rect_rel.Max.x) 2983 { 2984 window->ScrollTarget.x = item_rect_rel.Max.x + window->Scroll.x + g.Style.ItemSpacing.x; 2985 window->ScrollTargetCenterRatio.x = 1.0f; 2986 } 2987 if (item_rect_rel.Min.y < window_rect_rel.Min.y) 2988 { 2989 window->ScrollTarget.y = item_rect_rel.Min.y + window->Scroll.y - g.Style.ItemSpacing.y; 2990 window->ScrollTargetCenterRatio.y = 0.0f; 2991 } 2992 else if (item_rect_rel.Max.y >= window_rect_rel.Max.y) 2993 { 2994 window->ScrollTarget.y = item_rect_rel.Max.y + window->Scroll.y + g.Style.ItemSpacing.y; 2995 window->ScrollTargetCenterRatio.y = 1.0f; 2996 } 2997 2998 // Estimate upcoming scroll so we can offset our relative mouse position so mouse position can be applied immediately (under this block) 2999 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); 3000 item_rect_rel.Translate(window->Scroll - next_scroll); 3001 } 3002 3003 static void ImGui::NavUpdate() 3004 { 3005 ImGuiContext& g = *GImGui; 3006 g.IO.WantSetMousePos = false; 3007 3008 #if 0 3009 if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); 3348 return &GImGui->DrawListSharedData; 3349 } 3350 3351 void ImGui::StartMouseMovingWindow(ImGuiWindow* window) 3352 { 3353 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. 3354 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. 3355 // This is because we want ActiveId to be set even when the window is not permitted to move. 3356 ImGuiContext& g = *GImGui; 3357 FocusWindow(window); 3358 SetActiveID(window->MoveId, window); 3359 g.NavDisableHighlight = true; 3360 g.ActiveIdNoClearOnFocusLoss = true; 3361 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; 3362 3363 bool can_move_window = true; 3364 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) 3365 can_move_window = false; 3366 if (can_move_window) 3367 g.MovingWindow = window; 3368 } 3369 3370 // Handle mouse moving window 3371 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() 3372 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. 3373 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, 3374 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. 3375 void ImGui::UpdateMouseMovingWindowNewFrame() 3376 { 3377 ImGuiContext& g = *GImGui; 3378 if (g.MovingWindow != NULL) 3379 { 3380 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). 3381 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. 3382 KeepAliveID(g.ActiveId); 3383 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); 3384 ImGuiWindow* moving_window = g.MovingWindow->RootWindow; 3385 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) 3386 { 3387 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; 3388 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) 3389 { 3390 MarkIniSettingsDirty(moving_window); 3391 SetWindowPos(moving_window, pos, ImGuiCond_Always); 3392 } 3393 FocusWindow(g.MovingWindow); 3394 } 3395 else 3396 { 3397 ClearActiveID(); 3398 g.MovingWindow = NULL; 3399 } 3400 } 3401 else 3402 { 3403 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. 3404 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) 3405 { 3406 KeepAliveID(g.ActiveId); 3407 if (!g.IO.MouseDown[0]) 3408 ClearActiveID(); 3409 } 3410 } 3411 } 3412 3413 // Initiate moving window when clicking on empty space or title bar. 3414 // Handle left-click and right-click focus. 3415 void ImGui::UpdateMouseMovingWindowEndFrame() 3416 { 3417 ImGuiContext& g = *GImGui; 3418 if (g.ActiveId != 0 || g.HoveredId != 0) 3419 return; 3420 3421 // Unless we just made a window/popup appear 3422 if (g.NavWindow && g.NavWindow->Appearing) 3423 return; 3424 3425 // Click on empty space to focus window and start moving (after we're done with all our widgets) 3426 if (g.IO.MouseClicked[0]) 3427 { 3428 // Handle the edge case of a popup being closed while clicking in its empty space. 3429 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. 3430 ImGuiWindow* root_window = g.HoveredRootWindow; 3431 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); 3432 3433 if (root_window != NULL && !is_closed_popup) 3434 { 3435 StartMouseMovingWindow(g.HoveredWindow); 3436 3437 // Cancel moving if clicked outside of title bar 3438 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) 3439 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) 3440 g.MovingWindow = NULL; 3441 3442 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already) 3443 if (g.HoveredIdDisabled) 3444 g.MovingWindow = NULL; 3445 } 3446 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) 3447 { 3448 // Clicking on void disable focus 3449 FocusWindow(NULL); 3450 } 3451 } 3452 3453 // With right mouse button we close popups without changing focus based on where the mouse is aimed 3454 // Instead, focus will be restored to the window under the bottom-most closed popup. 3455 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) 3456 if (g.IO.MouseClicked[1]) 3457 { 3458 // Find the top-most window between HoveredWindow and the top-most Modal Window. 3459 // This is where we can trim the popup stack. 3460 ImGuiWindow* modal = GetTopMostPopupModal(); 3461 bool hovered_window_above_modal = false; 3462 if (modal == NULL) 3463 hovered_window_above_modal = true; 3464 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) 3465 { 3466 ImGuiWindow* window = g.Windows[i]; 3467 if (window == modal) 3468 break; 3469 if (window == g.HoveredWindow) 3470 hovered_window_above_modal = true; 3471 } 3472 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); 3473 } 3474 } 3475 3476 static bool IsWindowActiveAndVisible(ImGuiWindow* window) 3477 { 3478 return (window->Active) && (!window->Hidden); 3479 } 3480 3481 static void ImGui::UpdateMouseInputs() 3482 { 3483 ImGuiContext& g = *GImGui; 3484 3485 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) 3486 if (IsMousePosValid(&g.IO.MousePos)) 3487 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); 3488 3489 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta 3490 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) 3491 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; 3492 else 3493 g.IO.MouseDelta = ImVec2(0.0f, 0.0f); 3494 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) 3495 g.NavDisableMouseHover = false; 3496 3497 g.IO.MousePosPrev = g.IO.MousePos; 3498 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3499 { 3500 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; 3501 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; 3502 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; 3503 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; 3504 g.IO.MouseDoubleClicked[i] = false; 3505 if (g.IO.MouseClicked[i]) 3506 { 3507 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) 3508 { 3509 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3510 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) 3511 g.IO.MouseDoubleClicked[i] = true; 3512 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click 3513 } 3514 else 3515 { 3516 g.IO.MouseClickedTime[i] = g.Time; 3517 } 3518 g.IO.MouseClickedPos[i] = g.IO.MousePos; 3519 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; 3520 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); 3521 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; 3522 } 3523 else if (g.IO.MouseDown[i]) 3524 { 3525 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold 3526 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3527 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); 3528 g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); 3529 g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); 3530 } 3531 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) 3532 g.IO.MouseDownWasDoubleClick[i] = false; 3533 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation 3534 g.NavDisableMouseHover = false; 3535 } 3536 } 3537 3538 static void StartLockWheelingWindow(ImGuiWindow* window) 3539 { 3540 ImGuiContext& g = *GImGui; 3541 if (g.WheelingWindow == window) 3542 return; 3543 g.WheelingWindow = window; 3544 g.WheelingWindowRefMousePos = g.IO.MousePos; 3545 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; 3546 } 3547 3548 void ImGui::UpdateMouseWheel() 3549 { 3550 ImGuiContext& g = *GImGui; 3551 3552 // Reset the locked window if we move the mouse or after the timer elapses 3553 if (g.WheelingWindow != NULL) 3554 { 3555 g.WheelingWindowTimer -= g.IO.DeltaTime; 3556 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) 3557 g.WheelingWindowTimer = 0.0f; 3558 if (g.WheelingWindowTimer <= 0.0f) 3559 { 3560 g.WheelingWindow = NULL; 3561 g.WheelingWindowTimer = 0.0f; 3562 } 3563 } 3564 3565 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) 3566 return; 3567 3568 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; 3569 if (!window || window->Collapsed) 3570 return; 3571 3572 // Zoom / Scale window 3573 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. 3574 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) 3575 { 3576 StartLockWheelingWindow(window); 3577 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); 3578 const float scale = new_font_scale / window->FontWindowScale; 3579 window->FontWindowScale = new_font_scale; 3580 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) 3581 { 3582 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; 3583 SetWindowPos(window, window->Pos + offset, 0); 3584 window->Size = ImFloor(window->Size * scale); 3585 window->SizeFull = ImFloor(window->SizeFull * scale); 3586 } 3587 return; 3588 } 3589 3590 // Mouse wheel scrolling 3591 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent 3592 3593 // Vertical Mouse Wheel scrolling 3594 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; 3595 if (wheel_y != 0.0f && !g.IO.KeyCtrl) 3596 { 3597 StartLockWheelingWindow(window); 3598 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3599 window = window->ParentWindow; 3600 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3601 { 3602 float max_step = window->InnerRect.GetHeight() * 0.67f; 3603 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); 3604 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step); 3605 } 3606 } 3607 3608 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held 3609 const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; 3610 if (wheel_x != 0.0f && !g.IO.KeyCtrl) 3611 { 3612 StartLockWheelingWindow(window); 3613 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3614 window = window->ParentWindow; 3615 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3616 { 3617 float max_step = window->InnerRect.GetWidth() * 0.67f; 3618 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); 3619 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step); 3620 } 3621 } 3622 } 3623 3624 void ImGui::UpdateTabFocus() 3625 { 3626 ImGuiContext& g = *GImGui; 3627 3628 // Pressing TAB activate widget focus 3629 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); 3630 if (g.ActiveId == 0 && g.FocusTabPressed) 3631 { 3632 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also 3633 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. 3634 g.FocusRequestNextWindow = g.NavWindow; 3635 g.FocusRequestNextCounterRegular = INT_MAX; 3636 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) 3637 g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); 3638 else 3639 g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; 3640 } 3641 3642 // Turn queued focus request into current one 3643 g.FocusRequestCurrWindow = NULL; 3644 g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX; 3645 if (g.FocusRequestNextWindow != NULL) 3646 { 3647 ImGuiWindow* window = g.FocusRequestNextWindow; 3648 g.FocusRequestCurrWindow = window; 3649 if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) 3650 g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); 3651 if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) 3652 g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); 3653 g.FocusRequestNextWindow = NULL; 3654 g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX; 3655 } 3656 3657 g.NavIdTabCounter = INT_MAX; 3658 } 3659 3660 // 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) 3661 void ImGui::UpdateHoveredWindowAndCaptureFlags() 3662 { 3663 ImGuiContext& g = *GImGui; 3664 3665 // Find the window hovered by mouse: 3666 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. 3667 // - 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. 3668 // - 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. 3669 bool clear_hovered_windows = false; 3670 FindHoveredWindow(); 3671 3672 // Modal windows prevents mouse from hovering behind them. 3673 ImGuiWindow* modal_window = GetTopMostPopupModal(); 3674 if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) 3675 clear_hovered_windows = true; 3676 3677 // Disabled mouse? 3678 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) 3679 clear_hovered_windows = true; 3680 3681 // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. 3682 int mouse_earliest_button_down = -1; 3683 bool mouse_any_down = false; 3684 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3685 { 3686 if (g.IO.MouseClicked[i]) 3687 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); 3688 mouse_any_down |= g.IO.MouseDown[i]; 3689 if (g.IO.MouseDown[i]) 3690 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) 3691 mouse_earliest_button_down = i; 3692 } 3693 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; 3694 3695 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. 3696 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) 3697 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; 3698 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) 3699 clear_hovered_windows = true; 3700 3701 if (clear_hovered_windows) 3702 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; 3703 3704 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) 3705 if (g.WantCaptureMouseNextFrame != -1) 3706 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); 3707 else 3708 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); 3709 3710 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) 3711 if (g.WantCaptureKeyboardNextFrame != -1) 3712 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); 3713 else 3714 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); 3715 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) 3716 g.IO.WantCaptureKeyboard = true; 3717 3718 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible 3719 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; 3720 } 3721 3722 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() 3723 { 3724 ImGuiContext& g = *GImGui; 3725 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None; 3726 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; } 3727 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; } 3728 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; } 3729 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; } 3730 return key_mod_flags; 3731 } 3732 3733 void ImGui::NewFrame() 3734 { 3735 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3736 ImGuiContext& g = *GImGui; 3737 3738 #ifdef IMGUI_ENABLE_TEST_ENGINE 3739 ImGuiTestEngineHook_PreNewFrame(&g); 3010 3740 #endif 3011 3741 3012 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad)) 3013 if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) 3014 g.NavInputSource = ImGuiInputSource_NavGamepad; 3015 3016 // Update Keyboard->Nav inputs mapping 3017 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) 3018 { 3019 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } 3020 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate); 3021 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input); 3022 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel); 3023 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_); 3024 NAV_MAP_KEY(ImGuiKey_RightArrow, ImGuiNavInput_KeyRight_); 3025 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_); 3026 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_); 3027 if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; 3028 if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; 3029 if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; 3030 #undef NAV_MAP_KEY 3031 } 3032 3033 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); 3034 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) 3035 g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; 3036 3037 // Process navigation init request (select first/default focus) 3038 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) 3039 { 3040 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) 3041 IM_ASSERT(g.NavWindow); 3042 if (g.NavInitRequestFromMove) 3043 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); 3044 else 3045 SetNavID(g.NavInitResultId, g.NavLayer); 3046 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; 3047 } 3048 g.NavInitRequest = false; 3049 g.NavInitRequestFromMove = false; 3050 g.NavInitResultId = 0; 3051 g.NavJustMovedToId = 0; 3052 3053 // Process navigation move request 3054 if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) 3055 { 3056 // Select which result to use 3057 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 3058 if (g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) // Maybe entering a flattened child? In this case solve the tie using the regular scoring rules 3059 if ((g.NavMoveResultOther.DistBox < g.NavMoveResultLocal.DistBox) || (g.NavMoveResultOther.DistBox == g.NavMoveResultLocal.DistBox && g.NavMoveResultOther.DistCenter < g.NavMoveResultLocal.DistCenter)) 3060 result = &g.NavMoveResultOther; 3061 3062 IM_ASSERT(g.NavWindow && result->Window); 3063 3064 // Scroll to keep newly navigated item fully into view 3065 if (g.NavLayer == 0) 3066 NavScrollToBringItemIntoView(result->Window, result->RectRel); 3067 3068 // Apply result from previous frame navigation directional move request 3069 ClearActiveID(); 3070 g.NavWindow = result->Window; 3071 SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); 3072 g.NavJustMovedToId = result->ID; 3073 g.NavMoveFromClampedRefRect = false; 3074 } 3075 3076 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame 3077 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) 3078 { 3079 IM_ASSERT(g.NavMoveRequest); 3080 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 3081 g.NavDisableHighlight = false; 3082 g.NavMoveRequestForward = ImGuiNavForward_None; 3083 } 3084 3085 // Apply application mouse position movement, after we had a chance to process move request result. 3086 if (g.NavMousePosDirty && g.NavIdIsAlive) 3087 { 3088 // Set mouse position given our knowledge of the navigated item position from last frame 3089 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) 3090 { 3091 IM_ASSERT(!g.NavDisableHighlight && g.NavDisableMouseHover); 3092 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); 3093 g.IO.WantSetMousePos = true; 3094 } 3095 g.NavMousePosDirty = false; 3096 } 3097 g.NavIdIsAlive = false; 3098 g.NavJustTabbedId = 0; 3099 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); 3100 3101 // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 3102 if (g.NavWindow) 3103 NavSaveLastChildNavWindow(g.NavWindow); 3104 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) 3105 g.NavWindow->NavLastChildNavWindow = NULL; 3106 3107 NavUpdateWindowing(); 3108 3109 // Set output flags for user application 3110 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; 3111 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; 3112 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); 3113 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; 3114 3115 // Process NavCancel input (to close a popup, get back to parent, clear focus) 3116 if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) 3117 { 3118 if (g.ActiveId != 0) 3119 { 3120 ClearActiveID(); 3121 } 3122 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) 3123 { 3124 // Exit child window 3125 ImGuiWindow* child_window = g.NavWindow; 3126 ImGuiWindow* parent_window = g.NavWindow->ParentWindow; 3127 IM_ASSERT(child_window->ChildId != 0); 3128 FocusWindow(parent_window); 3129 SetNavID(child_window->ChildId, 0); 3130 g.NavIdIsAlive = false; 3131 if (g.NavDisableMouseHover) 3132 g.NavMousePosDirty = true; 3133 } 3134 else if (g.OpenPopupStack.Size > 0) 3135 { 3136 // Close open popup/menu 3137 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) 3138 ClosePopupToLevel(g.OpenPopupStack.Size - 1); 3139 } 3140 else if (g.NavLayer != 0) 3141 { 3142 // Leave the "menu" layer 3143 NavRestoreLayer(0); 3144 } 3145 else 3146 { 3147 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were 3148 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) 3149 g.NavWindow->NavLastIds[0] = 0; 3150 g.NavId = 0; 3151 } 3152 } 3153 3154 // Process manual activation request 3155 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; 3156 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 3157 { 3158 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); 3159 bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); 3160 if (g.ActiveId == 0 && activate_pressed) 3161 g.NavActivateId = g.NavId; 3162 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) 3163 g.NavActivateDownId = g.NavId; 3164 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) 3165 g.NavActivatePressedId = g.NavId; 3166 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) 3167 g.NavInputId = g.NavId; 3168 } 3169 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 3170 g.NavDisableHighlight = true; 3171 if (g.NavActivateId != 0) 3172 IM_ASSERT(g.NavActivateDownId == g.NavActivateId); 3173 g.NavMoveRequest = false; 3174 3175 // Process programmatic activation request 3176 if (g.NavNextActivateId != 0) 3177 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; 3178 g.NavNextActivateId = 0; 3179 3180 // Initiate directional inputs request 3181 const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; 3182 if (g.NavMoveRequestForward == ImGuiNavForward_None) 3183 { 3184 g.NavMoveDir = ImGuiDir_None; 3185 if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 3186 { 3187 if ((allowed_dir_flags & (1 << ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left; 3188 if ((allowed_dir_flags & (1 << ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight, ImGuiNavInput_KeyRight_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right; 3189 if ((allowed_dir_flags & (1 << ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up; 3190 if ((allowed_dir_flags & (1 << ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down; 3191 } 3192 } 3193 else 3194 { 3195 // 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) 3196 IM_ASSERT(g.NavMoveDir != ImGuiDir_None); 3197 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); 3198 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; 3199 } 3200 3201 if (g.NavMoveDir != ImGuiDir_None) 3202 { 3203 g.NavMoveRequest = true; 3204 g.NavMoveDirLast = g.NavMoveDir; 3205 } 3206 3207 // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match 3208 if (g.NavMoveRequest && g.NavId == 0) 3209 { 3210 g.NavInitRequest = g.NavInitRequestFromMove = true; 3211 g.NavInitResultId = 0; 3212 g.NavDisableHighlight = false; 3213 } 3214 3215 NavUpdateAnyRequestFlag(); 3216 3217 // Scrolling 3218 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) 3219 { 3220 // *Fallback* manual-scroll with NavUp/NavDown when window has no navigable item 3221 ImGuiWindow* window = g.NavWindow; 3222 const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. 3223 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) 3224 { 3225 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) 3226 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); 3227 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) 3228 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); 3229 } 3230 3231 // *Normal* Manual scroll with NavScrollXXX keys 3232 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. 3233 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); 3234 if (scroll_dir.x != 0.0f && window->ScrollbarX) 3235 { 3236 SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); 3237 g.NavMoveFromClampedRefRect = true; 3238 } 3239 if (scroll_dir.y != 0.0f) 3240 { 3241 SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); 3242 g.NavMoveFromClampedRefRect = true; 3243 } 3244 } 3245 3246 // Reset search results 3247 g.NavMoveResultLocal.Clear(); 3248 g.NavMoveResultOther.Clear(); 3249 3250 // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items 3251 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) 3252 { 3253 ImGuiWindow* window = g.NavWindow; 3254 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); 3255 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) 3256 { 3257 float pad = window->CalcFontSize() * 0.5f; 3258 window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item 3259 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); 3260 g.NavId = 0; 3261 } 3262 g.NavMoveFromClampedRefRect = false; 3263 } 3264 3265 // 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) 3266 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); 3267 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); 3268 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); 3269 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; 3270 IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem(). 3271 //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] 3272 g.NavScoringCount = 0; 3273 #if IMGUI_DEBUG_NAV_RECTS 3274 if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] 3275 if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255, 0, 255, 255) : IM_COL32(255, 0, 0, 255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8, -4), col, buf); } 3742 // Check and assert for various common IO and Configuration mistakes 3743 ErrorCheckNewFrameSanityChecks(); 3744 3745 // Load settings on first frame, save settings when modified (after a delay) 3746 UpdateSettings(); 3747 3748 g.Time += g.IO.DeltaTime; 3749 g.WithinFrameScope = true; 3750 g.FrameCount += 1; 3751 g.TooltipOverrideCount = 0; 3752 g.WindowsActiveCount = 0; 3753 g.MenusIdSubmittedThisFrame.resize(0); 3754 3755 // Calculate frame-rate for the user, as a purely luxurious feature 3756 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; 3757 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; 3758 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); 3759 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; 3760 3761 // Setup current font and draw list shared data 3762 g.IO.Fonts->Locked = true; 3763 SetCurrentFont(GetDefaultFont()); 3764 IM_ASSERT(g.Font->IsLoaded()); 3765 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 3766 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; 3767 g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); 3768 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; 3769 if (g.Style.AntiAliasedLines) 3770 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; 3771 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)) 3772 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; 3773 if (g.Style.AntiAliasedFill) 3774 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; 3775 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) 3776 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; 3777 3778 g.BackgroundDrawList._ResetForNewFrame(); 3779 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); 3780 g.BackgroundDrawList.PushClipRectFullScreen(); 3781 3782 g.ForegroundDrawList._ResetForNewFrame(); 3783 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); 3784 g.ForegroundDrawList.PushClipRectFullScreen(); 3785 3786 // Mark rendering data as invalid to prevent user who may have a handle on it to use it. 3787 g.DrawData.Clear(); 3788 3789 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent 3790 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) 3791 KeepAliveID(g.DragDropPayload.SourceId); 3792 3793 // Update HoveredId data 3794 if (!g.HoveredIdPreviousFrame) 3795 g.HoveredIdTimer = 0.0f; 3796 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) 3797 g.HoveredIdNotActiveTimer = 0.0f; 3798 if (g.HoveredId) 3799 g.HoveredIdTimer += g.IO.DeltaTime; 3800 if (g.HoveredId && g.ActiveId != g.HoveredId) 3801 g.HoveredIdNotActiveTimer += g.IO.DeltaTime; 3802 g.HoveredIdPreviousFrame = g.HoveredId; 3803 g.HoveredId = 0; 3804 g.HoveredIdAllowOverlap = false; 3805 g.HoveredIdDisabled = false; 3806 3807 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) 3808 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) 3809 ClearActiveID(); 3810 if (g.ActiveId) 3811 g.ActiveIdTimer += g.IO.DeltaTime; 3812 g.LastActiveIdTimer += g.IO.DeltaTime; 3813 g.ActiveIdPreviousFrame = g.ActiveId; 3814 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; 3815 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; 3816 g.ActiveIdIsAlive = 0; 3817 g.ActiveIdHasBeenEditedThisFrame = false; 3818 g.ActiveIdPreviousFrameIsAlive = false; 3819 g.ActiveIdIsJustActivated = false; 3820 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) 3821 g.TempInputId = 0; 3822 if (g.ActiveId == 0) 3823 { 3824 g.ActiveIdUsingNavDirMask = 0x00; 3825 g.ActiveIdUsingNavInputMask = 0x00; 3826 g.ActiveIdUsingKeyInputMask = 0x00; 3827 } 3828 3829 // Drag and drop 3830 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; 3831 g.DragDropAcceptIdCurr = 0; 3832 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 3833 g.DragDropWithinSource = false; 3834 g.DragDropWithinTarget = false; 3835 g.DragDropHoldJustPressedId = 0; 3836 3837 // Update keyboard input state 3838 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools 3839 g.IO.KeyMods = GetMergedKeyModFlags(); 3840 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); 3841 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) 3842 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; 3843 3844 // Update gamepad/keyboard navigation 3845 NavUpdate(); 3846 3847 // Update mouse input state 3848 UpdateMouseInputs(); 3849 3850 // Find hovered window 3851 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) 3852 UpdateHoveredWindowAndCaptureFlags(); 3853 3854 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) 3855 UpdateMouseMovingWindowNewFrame(); 3856 3857 // Background darkening/whitening 3858 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) 3859 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); 3860 else 3861 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); 3862 3863 g.MouseCursor = ImGuiMouseCursor_Arrow; 3864 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; 3865 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default 3866 3867 // Mouse wheel scrolling, scale 3868 UpdateMouseWheel(); 3869 3870 // Update legacy TAB focus 3871 UpdateTabFocus(); 3872 3873 // Mark all windows as not visible and compact unused memory. 3874 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); 3875 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX; 3876 for (int i = 0; i != g.Windows.Size; i++) 3877 { 3878 ImGuiWindow* window = g.Windows[i]; 3879 window->WasActive = window->Active; 3880 window->BeginCount = 0; 3881 window->Active = false; 3882 window->WriteAccessed = false; 3883 3884 // Garbage collect transient buffers of recently unused windows 3885 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) 3886 GcCompactTransientWindowBuffers(window); 3887 } 3888 3889 // Closing the focused window restore focus to the first active root window in descending z-order 3890 if (g.NavWindow && !g.NavWindow->WasActive) 3891 FocusTopMostWindowUnderOne(NULL, NULL); 3892 3893 // No window should be open at the beginning of the frame. 3894 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. 3895 g.CurrentWindowStack.resize(0); 3896 g.BeginPopupStack.resize(0); 3897 ClosePopupsOverWindow(g.NavWindow, false); 3898 3899 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 3900 UpdateDebugToolItemPicker(); 3901 3902 // Create implicit/fallback window - which we will only render it if the user has added something to it. 3903 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. 3904 // This fallback is particularly important as it avoid ImGui:: calls from crashing. 3905 g.WithinFrameScopeWithImplicitWindow = true; 3906 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); 3907 Begin("Debug##Default"); 3908 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); 3909 3910 #ifdef IMGUI_ENABLE_TEST_ENGINE 3911 ImGuiTestEngineHook_PostNewFrame(&g); 3276 3912 #endif 3277 3913 } 3278 3914 3279 static void ImGui::UpdateMovingWindow() 3280 { 3281 ImGuiContext& g = *GImGui; 3282 if (g.MovingWindow != NULL) 3283 { 3284 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). 3285 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. 3286 KeepAliveID(g.ActiveId); 3287 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); 3288 ImGuiWindow* moving_window = g.MovingWindow->RootWindow; 3289 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) 3290 { 3291 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; 3292 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) 3293 { 3294 MarkIniSettingsDirty(moving_window); 3295 SetWindowPos(moving_window, pos, ImGuiCond_Always); 3296 } 3297 FocusWindow(g.MovingWindow); 3298 } 3299 else 3300 { 3301 ClearActiveID(); 3302 g.MovingWindow = NULL; 3303 } 3304 } 3305 else 3306 { 3307 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. 3308 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) 3309 { 3310 KeepAliveID(g.ActiveId); 3311 if (!g.IO.MouseDown[0]) 3312 ClearActiveID(); 3313 } 3314 } 3315 } 3316 3317 static void ImGui::UpdateMouseInputs() 3318 { 3319 ImGuiContext& g = *GImGui; 3320 3321 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta 3322 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) 3323 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; 3324 else 3325 g.IO.MouseDelta = ImVec2(0.0f, 0.0f); 3326 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) 3327 g.NavDisableMouseHover = false; 3328 3329 g.IO.MousePosPrev = g.IO.MousePos; 3330 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3331 { 3332 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; 3333 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; 3334 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; 3335 g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; 3336 g.IO.MouseDoubleClicked[i] = false; 3337 if (g.IO.MouseClicked[i]) 3338 { 3339 if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) 3340 { 3341 if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) 3342 g.IO.MouseDoubleClicked[i] = true; 3343 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click 3344 } 3345 else 3346 { 3347 g.IO.MouseClickedTime[i] = g.Time; 3348 } 3349 g.IO.MouseClickedPos[i] = g.IO.MousePos; 3350 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); 3351 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; 3352 } 3353 else if (g.IO.MouseDown[i]) 3354 { 3355 ImVec2 mouse_delta = g.IO.MousePos - g.IO.MouseClickedPos[i]; 3356 g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, mouse_delta.x < 0.0f ? -mouse_delta.x : mouse_delta.x); 3357 g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, mouse_delta.y < 0.0f ? -mouse_delta.y : mouse_delta.y); 3358 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(mouse_delta)); 3359 } 3360 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation 3361 g.NavDisableMouseHover = false; 3362 } 3363 } 3364 3365 // 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) 3366 void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags() 3367 { 3368 ImGuiContext& g = *GImGui; 3369 3370 // Find the window hovered by mouse: 3371 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. 3372 // - 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. 3373 // - 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. 3374 g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); 3375 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; 3376 3377 // Modal windows prevents cursor from hovering behind them. 3378 ImGuiWindow* modal_window = GetFrontMostPopupModal(); 3379 if (modal_window) 3380 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) 3381 g.HoveredRootWindow = g.HoveredWindow = NULL; 3382 3383 // Disabled mouse? 3384 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) 3385 g.HoveredWindow = g.HoveredRootWindow = NULL; 3386 3387 // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. 3388 int mouse_earliest_button_down = -1; 3389 bool mouse_any_down = false; 3390 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3391 { 3392 if (g.IO.MouseClicked[i]) 3393 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); 3394 mouse_any_down |= g.IO.MouseDown[i]; 3395 if (g.IO.MouseDown[i]) 3396 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) 3397 mouse_earliest_button_down = i; 3398 } 3399 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; 3400 3401 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. 3402 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) 3403 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; 3404 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) 3405 g.HoveredWindow = g.HoveredRootWindow = NULL; 3406 3407 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app) 3408 if (g.WantCaptureMouseNextFrame != -1) 3409 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); 3410 else 3411 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); 3412 3413 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app) 3414 if (g.WantCaptureKeyboardNextFrame != -1) 3415 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); 3416 else 3417 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); 3418 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) 3419 g.IO.WantCaptureKeyboard = true; 3420 3421 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible 3422 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; 3423 } 3424 3425 void ImGui::NewFrame() 3426 { 3427 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); 3428 ImGuiContext& g = *GImGui; 3429 3430 // Check user data 3431 // (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) 3432 IM_ASSERT(g.Initialized); 3433 IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)"); 3434 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value"); 3435 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 3436 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 3437 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting"); 3438 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)"); 3439 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); 3440 for (int n = 0; n < ImGuiKey_COUNT; n++) 3441 IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); 3442 3443 // Perform simple check for 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 recently added in 1.60 WIP) 3444 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) 3445 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); 3446 3447 // Load settings on first frame 3448 if (!g.SettingsLoaded) 3449 { 3450 IM_ASSERT(g.SettingsWindows.empty()); 3451 LoadIniSettingsFromDisk(g.IO.IniFilename); 3452 g.SettingsLoaded = true; 3453 } 3454 3455 // Save settings (with a delay so we don't spam disk too much) 3456 if (g.SettingsDirtyTimer > 0.0f) 3457 { 3458 g.SettingsDirtyTimer -= g.IO.DeltaTime; 3459 if (g.SettingsDirtyTimer <= 0.0f) 3460 SaveIniSettingsToDisk(g.IO.IniFilename); 3461 } 3462 3463 g.Time += g.IO.DeltaTime; 3464 g.FrameCount += 1; 3465 g.TooltipOverrideCount = 0; 3466 g.WindowsActiveCount = 0; 3467 3468 SetCurrentFont(GetDefaultFont()); 3469 IM_ASSERT(g.Font->IsLoaded()); 3470 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 3471 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; 3472 3473 g.OverlayDrawList.Clear(); 3474 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); 3475 g.OverlayDrawList.PushClipRectFullScreen(); 3476 g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); 3477 3478 // Mark rendering data as invalid to prevent user who may have a handle on it to use it 3479 g.DrawData.Clear(); 3480 3481 // Clear reference to active widget if the widget isn't alive anymore 3482 if (!g.HoveredIdPreviousFrame) 3483 g.HoveredIdTimer = 0.0f; 3484 g.HoveredIdPreviousFrame = g.HoveredId; 3485 g.HoveredId = 0; 3486 g.HoveredIdAllowOverlap = false; 3487 if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) 3488 ClearActiveID(); 3489 if (g.ActiveId) 3490 g.ActiveIdTimer += g.IO.DeltaTime; 3491 g.ActiveIdPreviousFrame = g.ActiveId; 3492 g.ActiveIdIsAlive = false; 3493 g.ActiveIdIsJustActivated = false; 3494 if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId) 3495 g.ScalarAsInputTextId = 0; 3496 3497 // Elapse drag & drop payload 3498 if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) 3499 { 3500 ClearDragDrop(); 3501 g.DragDropPayloadBufHeap.clear(); 3502 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 3503 } 3504 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; 3505 g.DragDropAcceptIdCurr = 0; 3506 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 3507 3508 // Update keyboard input state 3509 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); 3510 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) 3511 g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; 3512 3513 // Update gamepad/keyboard directional navigation 3514 NavUpdate(); 3515 3516 // Update mouse input state 3517 UpdateMouseInputs(); 3518 3519 // Calculate frame-rate for the user, as a purely luxurious feature 3520 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; 3521 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; 3522 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); 3523 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; 3524 3525 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) 3526 UpdateMovingWindow(); 3527 NewFrameUpdateHoveredWindowAndCaptureFlags(); 3528 3529 if (GetFrontMostPopupModal() != NULL) 3530 g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); 3531 else 3532 g.ModalWindowDarkeningRatio = 0.0f; 3533 3534 g.MouseCursor = ImGuiMouseCursor_Arrow; 3535 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; 3536 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default 3537 3538 // Mouse wheel scrolling, scale 3539 if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f)) 3540 { 3541 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). 3542 ImGuiWindow* window = g.HoveredWindow; 3543 ImGuiWindow* scroll_window = window; 3544 while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) 3545 scroll_window = scroll_window->ParentWindow; 3546 const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); 3547 3548 if (g.IO.MouseWheel != 0.0f) 3549 { 3550 if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) 3551 { 3552 // Zoom / Scale window 3553 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); 3554 const float scale = new_font_scale / window->FontWindowScale; 3555 window->FontWindowScale = new_font_scale; 3556 3557 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; 3558 window->Pos += offset; 3559 window->Size *= scale; 3560 window->SizeFull *= scale; 3561 } 3562 else if (!g.IO.KeyCtrl && scroll_allowed) 3563 { 3564 // Mouse wheel vertical scrolling 3565 float scroll_amount = 5 * scroll_window->CalcFontSize(); 3566 scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f); 3567 SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount); 3568 } 3569 } 3570 if (g.IO.MouseWheelH != 0.0f && scroll_allowed) 3571 { 3572 // Mouse wheel horizontal scrolling (for hardware that supports it) 3573 float scroll_amount = scroll_window->CalcFontSize(); 3574 if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) 3575 SetWindowScrollX(window, window->Scroll.x - g.IO.MouseWheelH * scroll_amount); 3576 } 3577 } 3578 3579 // Pressing TAB activate widget focus 3580 if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) 3581 { 3582 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) 3583 g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); 3584 else 3585 g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; 3586 } 3587 g.NavIdTabCounter = INT_MAX; 3588 3589 // Mark all windows as not visible 3590 for (int i = 0; i != g.Windows.Size; i++) 3591 { 3592 ImGuiWindow* window = g.Windows[i]; 3593 window->WasActive = window->Active; 3594 window->Active = false; 3595 window->WriteAccessed = false; 3596 } 3597 3598 // Closing the focused window restore focus to the first active root window in descending z-order 3599 if (g.NavWindow && !g.NavWindow->WasActive) 3600 FocusFrontMostActiveWindow(NULL); 3601 3602 // No window should be open at the beginning of the frame. 3603 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. 3604 g.CurrentWindowStack.resize(0); 3605 g.CurrentPopupStack.resize(0); 3606 ClosePopupsOverWindow(g.NavWindow); 3607 3608 // Create implicit window - we will only render it if the user has added something to it. 3609 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. 3610 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); 3611 Begin("Debug##Default"); 3612 } 3613 3614 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) 3615 { 3616 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); 3617 if (!settings) 3618 settings = AddWindowSettings(name); 3619 return (void*)settings; 3620 } 3621 3622 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) 3623 { 3624 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; 3625 float x, y; 3626 int i; 3627 if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); 3628 else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); 3629 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); 3630 } 3631 3632 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) 3633 { 3634 // Gather data from windows that were active during this session 3635 ImGuiContext& g = *imgui_ctx; 3636 for (int i = 0; i != g.Windows.Size; i++) 3637 { 3638 ImGuiWindow* window = g.Windows[i]; 3639 if (window->Flags & ImGuiWindowFlags_NoSavedSettings) 3640 continue; 3641 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); 3642 if (!settings) 3643 settings = AddWindowSettings(window->Name); 3644 settings->Pos = window->Pos; 3645 settings->Size = window->SizeFull; 3646 settings->Collapsed = window->Collapsed; 3647 } 3648 3649 // Write a buffer 3650 // If a window wasn't opened in this session we preserve its settings 3651 buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve 3652 for (int i = 0; i != g.SettingsWindows.Size; i++) 3653 { 3654 const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; 3655 if (settings->Pos.x == FLT_MAX) 3656 continue; 3657 const char* name = settings->Name; 3658 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() 3659 name = p; 3660 buf->appendf("[%s][%s]\n", handler->TypeName, name); 3661 buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); 3662 buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); 3663 buf->appendf("Collapsed=%d\n", settings->Collapsed); 3664 buf->appendf("\n"); 3665 } 3915 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 3916 void ImGui::UpdateDebugToolItemPicker() 3917 { 3918 ImGuiContext& g = *GImGui; 3919 g.DebugItemPickerBreakId = 0; 3920 if (g.DebugItemPickerActive) 3921 { 3922 const ImGuiID hovered_id = g.HoveredIdPreviousFrame; 3923 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 3924 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) 3925 g.DebugItemPickerActive = false; 3926 if (ImGui::IsMouseClicked(0) && hovered_id) 3927 { 3928 g.DebugItemPickerBreakId = hovered_id; 3929 g.DebugItemPickerActive = false; 3930 } 3931 ImGui::SetNextWindowBgAlpha(0.60f); 3932 ImGui::BeginTooltip(); 3933 ImGui::Text("HoveredId: 0x%08X", hovered_id); 3934 ImGui::Text("Press ESC to abort picking."); 3935 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); 3936 ImGui::EndTooltip(); 3937 } 3666 3938 } 3667 3939 3668 3940 void ImGui::Initialize(ImGuiContext* context) 3669 3941 { 3670 ImGuiContext& g = *context; 3671 IM_ASSERT(!g.Initialized && !g.SettingsLoaded); 3672 g.LogClipboard = IM_NEW(ImGuiTextBuffer)(); 3673 3674 // Add .ini handle for ImGuiWindow type 3675 ImGuiSettingsHandler ini_handler; 3676 ini_handler.TypeName = "Window"; 3677 ini_handler.TypeHash = ImHash("Window", 0, 0); 3678 ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; 3679 ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; 3680 ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; 3681 g.SettingsHandlers.push_front(ini_handler); 3682 3683 g.Initialized = true; 3942 ImGuiContext& g = *context; 3943 IM_ASSERT(!g.Initialized && !g.SettingsLoaded); 3944 3945 // Add .ini handle for ImGuiWindow type 3946 { 3947 ImGuiSettingsHandler ini_handler; 3948 ini_handler.TypeName = "Window"; 3949 ini_handler.TypeHash = ImHashStr("Window"); 3950 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; 3951 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; 3952 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; 3953 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; 3954 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; 3955 g.SettingsHandlers.push_back(ini_handler); 3956 } 3957 3958 #ifdef IMGUI_HAS_TABLE 3959 // Add .ini handle for ImGuiTable type 3960 { 3961 ImGuiSettingsHandler ini_handler; 3962 ini_handler.TypeName = "Table"; 3963 ini_handler.TypeHash = ImHashStr("Table"); 3964 ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; 3965 ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; 3966 ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; 3967 g.SettingsHandlers.push_back(ini_handler); 3968 } 3969 #endif // #ifdef IMGUI_HAS_TABLE 3970 3971 #ifdef IMGUI_HAS_DOCK 3972 #endif // #ifdef IMGUI_HAS_DOCK 3973 3974 g.Initialized = true; 3684 3975 } 3685 3976 … … 3687 3978 void ImGui::Shutdown(ImGuiContext* context) 3688 3979 { 3689 ImGuiContext& g = *context; 3690 3691 // 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) 3692 if (g.IO.Fonts && g.FontAtlasOwnedByContext) 3693 IM_DELETE(g.IO.Fonts); 3694 g.IO.Fonts = NULL; 3695 3696 // Cleanup of other data are conditional on actually having initialize ImGui. 3697 if (!g.Initialized) 3698 return; 3699 3700 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) 3701 if (g.SettingsLoaded) 3702 SaveIniSettingsToDisk(g.IO.IniFilename); 3703 3704 // Clear everything else 3705 for (int i = 0; i < g.Windows.Size; i++) 3706 IM_DELETE(g.Windows[i]); 3707 g.Windows.clear(); 3708 g.WindowsSortBuffer.clear(); 3709 g.CurrentWindow = NULL; 3710 g.CurrentWindowStack.clear(); 3711 g.WindowsById.Clear(); 3712 g.NavWindow = NULL; 3713 g.HoveredWindow = NULL; 3714 g.HoveredRootWindow = NULL; 3715 g.ActiveIdWindow = NULL; 3716 g.MovingWindow = NULL; 3717 g.ColorModifiers.clear(); 3718 g.StyleModifiers.clear(); 3719 g.FontStack.clear(); 3720 g.OpenPopupStack.clear(); 3721 g.CurrentPopupStack.clear(); 3722 g.DrawDataBuilder.ClearFreeMemory(); 3723 g.OverlayDrawList.ClearFreeMemory(); 3724 g.PrivateClipboard.clear(); 3725 g.InputTextState.Text.clear(); 3726 g.InputTextState.InitialText.clear(); 3727 g.InputTextState.TempTextBuffer.clear(); 3728 3729 for (int i = 0; i < g.SettingsWindows.Size; i++) 3730 IM_DELETE(g.SettingsWindows[i].Name); 3731 g.SettingsWindows.clear(); 3732 g.SettingsHandlers.clear(); 3733 3734 if (g.LogFile && g.LogFile != stdout) 3735 { 3736 fclose(g.LogFile); 3737 g.LogFile = NULL; 3738 } 3739 if (g.LogClipboard) 3740 IM_DELETE(g.LogClipboard); 3741 g.LogClipboard = NULL; 3742 3743 g.Initialized = false; 3744 } 3745 3746 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) 3747 { 3748 ImGuiContext& g = *GImGui; 3749 for (int i = 0; i != g.SettingsWindows.Size; i++) 3750 if (g.SettingsWindows[i].Id == id) 3751 return &g.SettingsWindows[i]; 3752 return NULL; 3753 } 3754 3755 static ImGuiWindowSettings* AddWindowSettings(const char* name) 3756 { 3757 ImGuiContext& g = *GImGui; 3758 g.SettingsWindows.push_back(ImGuiWindowSettings()); 3759 ImGuiWindowSettings* settings = &g.SettingsWindows.back(); 3760 settings->Name = ImStrdup(name); 3761 settings->Id = ImHash(name, 0); 3762 return settings; 3763 } 3764 3765 static void LoadIniSettingsFromDisk(const char* ini_filename) 3766 { 3767 if (!ini_filename) 3768 return; 3769 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", NULL, +1); 3770 if (!file_data) 3771 return; 3772 LoadIniSettingsFromMemory(file_data); 3773 ImGui::MemFree(file_data); 3774 } 3775 3776 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) 3777 { 3778 ImGuiContext& g = *GImGui; 3779 const ImGuiID type_hash = ImHash(type_name, 0, 0); 3780 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 3781 if (g.SettingsHandlers[handler_n].TypeHash == type_hash) 3782 return &g.SettingsHandlers[handler_n]; 3783 return NULL; 3784 } 3785 3786 // Zero-tolerance, no error reporting, cheap .ini parsing 3787 static void LoadIniSettingsFromMemory(const char* buf_readonly) 3788 { 3789 // For convenience and to make the code simpler, we'll write zero terminators inside the buffer. So let's create a writable copy. 3790 char* buf = ImStrdup(buf_readonly); 3791 char* buf_end = buf + strlen(buf); 3792 3793 ImGuiContext& g = *GImGui; 3794 void* entry_data = NULL; 3795 ImGuiSettingsHandler* entry_handler = NULL; 3796 3797 char* line_end = NULL; 3798 for (char* line = buf; line < buf_end; line = line_end + 1) 3799 { 3800 // Skip new lines markers, then find end of the line 3801 while (*line == '\n' || *line == '\r') 3802 line++; 3803 line_end = line; 3804 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') 3805 line_end++; 3806 line_end[0] = 0; 3807 3808 if (line[0] == '[' && line_end > line && line_end[-1] == ']') 3809 { 3810 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. 3811 line_end[-1] = 0; 3812 const char* name_end = line_end - 1; 3813 const char* type_start = line + 1; 3814 char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); 3815 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; 3816 if (!type_end || !name_start) 3817 { 3818 name_start = type_start; // Import legacy entries that have no type 3819 type_start = "Window"; 3820 } 3821 else 3822 { 3823 *type_end = 0; // Overwrite first ']' 3824 name_start++; // Skip second '[' 3825 } 3826 entry_handler = ImGui::FindSettingsHandler(type_start); 3827 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; 3828 } 3829 else if (entry_handler != NULL && entry_data != NULL) 3830 { 3831 // Let type handler parse the line 3832 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); 3833 } 3834 } 3835 ImGui::MemFree(buf); 3836 g.SettingsLoaded = true; 3837 } 3838 3839 static void SaveIniSettingsToDisk(const char* ini_filename) 3840 { 3841 ImGuiContext& g = *GImGui; 3842 g.SettingsDirtyTimer = 0.0f; 3843 if (!ini_filename) 3844 return; 3845 3846 ImVector<char> buf; 3847 SaveIniSettingsToMemory(buf); 3848 3849 FILE* f = ImFileOpen(ini_filename, "wt"); 3850 if (!f) 3851 return; 3852 fwrite(buf.Data, sizeof(char), (size_t)buf.Size, f); 3853 fclose(f); 3854 } 3855 3856 static void SaveIniSettingsToMemory(ImVector<char>& out_buf) 3857 { 3858 ImGuiContext& g = *GImGui; 3859 g.SettingsDirtyTimer = 0.0f; 3860 3861 ImGuiTextBuffer buf; 3862 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 3863 { 3864 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; 3865 handler->WriteAllFn(&g, handler, &buf); 3866 } 3867 3868 buf.Buf.pop_back(); // Remove extra zero-terminator used by ImGuiTextBuffer 3869 out_buf.swap(buf.Buf); 3870 } 3871 3872 void ImGui::MarkIniSettingsDirty() 3873 { 3874 ImGuiContext& g = *GImGui; 3875 if (g.SettingsDirtyTimer <= 0.0f) 3876 g.SettingsDirtyTimer = g.IO.IniSavingRate; 3877 } 3878 3879 static void MarkIniSettingsDirty(ImGuiWindow* window) 3880 { 3881 ImGuiContext& g = *GImGui; 3882 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) 3883 if (g.SettingsDirtyTimer <= 0.0f) 3884 g.SettingsDirtyTimer = g.IO.IniSavingRate; 3980 // 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) 3981 ImGuiContext& g = *context; 3982 if (g.IO.Fonts && g.FontAtlasOwnedByContext) 3983 { 3984 g.IO.Fonts->Locked = false; 3985 IM_DELETE(g.IO.Fonts); 3986 } 3987 g.IO.Fonts = NULL; 3988 3989 // Cleanup of other data are conditional on actually having initialized Dear ImGui. 3990 if (!g.Initialized) 3991 return; 3992 3993 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) 3994 if (g.SettingsLoaded && g.IO.IniFilename != NULL) 3995 { 3996 ImGuiContext* backup_context = GImGui; 3997 SetCurrentContext(context); 3998 SaveIniSettingsToDisk(g.IO.IniFilename); 3999 SetCurrentContext(backup_context); 4000 } 4001 4002 // Notify hooked test engine, if any 4003 #ifdef IMGUI_ENABLE_TEST_ENGINE 4004 ImGuiTestEngineHook_Shutdown(context); 4005 #endif 4006 4007 // Clear everything else 4008 for (int i = 0; i < g.Windows.Size; i++) 4009 IM_DELETE(g.Windows[i]); 4010 g.Windows.clear(); 4011 g.WindowsFocusOrder.clear(); 4012 g.WindowsTempSortBuffer.clear(); 4013 g.CurrentWindow = NULL; 4014 g.CurrentWindowStack.clear(); 4015 g.WindowsById.Clear(); 4016 g.NavWindow = NULL; 4017 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; 4018 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; 4019 g.MovingWindow = NULL; 4020 g.ColorModifiers.clear(); 4021 g.StyleModifiers.clear(); 4022 g.FontStack.clear(); 4023 g.OpenPopupStack.clear(); 4024 g.BeginPopupStack.clear(); 4025 g.DrawDataBuilder.ClearFreeMemory(); 4026 g.BackgroundDrawList._ClearFreeMemory(); 4027 g.ForegroundDrawList._ClearFreeMemory(); 4028 4029 g.TabBars.Clear(); 4030 g.CurrentTabBarStack.clear(); 4031 g.ShrinkWidthBuffer.clear(); 4032 4033 g.ClipboardHandlerData.clear(); 4034 g.MenusIdSubmittedThisFrame.clear(); 4035 g.InputTextState.ClearFreeMemory(); 4036 4037 g.SettingsWindows.clear(); 4038 g.SettingsHandlers.clear(); 4039 4040 if (g.LogFile) 4041 { 4042 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 4043 if (g.LogFile != stdout) 4044 #endif 4045 ImFileClose(g.LogFile); 4046 g.LogFile = NULL; 4047 } 4048 g.LogBuffer.clear(); 4049 4050 g.Initialized = false; 3885 4051 } 3886 4052 … … 3888 4054 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) 3889 4055 { 3890 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;3891 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;3892 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))3893 return d;3894 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))3895 return d;3896 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);3897 } 3898 3899 static void AddWindowToSort edBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)3900 { 3901 out_sorted_windows->push_back(window);3902 if (window->Active)3903 {3904 int count = window->DC.ChildWindows.Size;3905 if (count > 1)3906 qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);3907 for (int i = 0; i < count; i++)3908 {3909 ImGuiWindow* child = window->DC.ChildWindows[i];3910 if (child->Active)3911 AddWindowToSortedBuffer(out_sorted_windows, child);3912 }3913 }4056 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; 4057 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; 4058 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) 4059 return d; 4060 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) 4061 return d; 4062 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); 4063 } 4064 4065 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window) 4066 { 4067 out_sorted_windows->push_back(window); 4068 if (window->Active) 4069 { 4070 int count = window->DC.ChildWindows.Size; 4071 if (count > 1) 4072 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); 4073 for (int i = 0; i < count; i++) 4074 { 4075 ImGuiWindow* child = window->DC.ChildWindows[i]; 4076 if (child->Active) 4077 AddWindowToSortBuffer(out_sorted_windows, child); 4078 } 4079 } 3914 4080 } 3915 4081 3916 4082 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list) 3917 4083 { 3918 if (draw_list->CmdBuffer.empty()) 3919 return; 3920 3921 // Remove trailing command if unused 3922 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back(); 3923 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL) 3924 { 3925 draw_list->CmdBuffer.pop_back(); 3926 if (draw_list->CmdBuffer.empty()) 3927 return; 3928 } 3929 3930 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly. 3931 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); 3932 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); 3933 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); 3934 3935 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) 3936 // If this assert triggers because you are drawing lots of stuff manually: 3937 // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents. 3938 // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes. 3939 // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing: 3940 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 3941 // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API. 3942 // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists. 3943 if (sizeof(ImDrawIdx) == 2) 3944 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); 3945 3946 out_list->push_back(draw_list); 4084 // Remove trailing command if unused. 4085 // Technically we could return directly instead of popping, but this make things looks neat in Metrics window as well. 4086 draw_list->_PopUnusedDrawCmd(); 4087 if (draw_list->CmdBuffer.Size == 0) 4088 return; 4089 4090 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. 4091 // May trigger for you if you are using PrimXXX functions incorrectly. 4092 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); 4093 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); 4094 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) 4095 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); 4096 4097 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) 4098 // If this assert triggers because you are drawing lots of stuff manually: 4099 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. 4100 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents. 4101 // - If you want large meshes with more than 64K vertices, you can either: 4102 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. 4103 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't. 4104 // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. 4105 // (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. 4106 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time: 4107 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 4108 // Your own engine or render API may use different parameters or function calls to specify index sizes. 4109 // 2 and 4 bytes indices are generally supported by most graphics API. 4110 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching 4111 // the 64K limit to split your draw commands in multiple draw lists. 4112 if (sizeof(ImDrawIdx) == 2) 4113 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); 4114 4115 out_list->push_back(draw_list); 3947 4116 } 3948 4117 3949 4118 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window) 3950 4119 { 3951 AddDrawListToDrawData(out_render_list, window->DrawList);3952 for (int i = 0; i < window->DC.ChildWindows.Size; i++)3953 {3954 ImGuiWindow* child = window->DC.ChildWindows[i];3955 if (child->Active && child->HiddenFrames == 0) // clipped children may have been marked not active3956 AddWindowToDrawData(out_render_list, child);3957 }3958 } 3959 3960 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) 3961 { 3962 ImGuiContext& g = *GImGui; 3963 g.IO.MetricsActiveWindows++; 3964 if (window->Flags & ImGuiWindowFlags_Tooltip) 3965 AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);3966 else3967 AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);4120 ImGuiContext& g = *GImGui; 4121 g.IO.MetricsRenderWindows++; 4122 AddDrawListToDrawData(out_render_list, window->DrawList); 4123 for (int i = 0; i < window->DC.ChildWindows.Size; i++) 4124 { 4125 ImGuiWindow* child = window->DC.ChildWindows[i]; 4126 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active 4127 AddWindowToDrawData(out_render_list, child); 4128 } 4129 } 4130 4131 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) 4132 static void AddRootWindowToDrawData(ImGuiWindow* window) 4133 { 4134 ImGuiContext& g = *GImGui; 4135 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; 4136 AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window); 3968 4137 } 3969 4138 3970 4139 void ImDrawDataBuilder::FlattenIntoSingleLayer() 3971 4140 { 3972 int n = Layers[0].Size; 3973 int size = n; 3974 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) 3975 size += Layers[i].Size; 3976 Layers[0].resize(size); 3977 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) 3978 { 3979 ImVector<ImDrawList*>& layer = Layers[layer_n]; 3980 if (layer.empty()) 3981 continue; 3982 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); 3983 n += layer.Size; 3984 layer.resize(0); 3985 } 3986 } 3987 3988 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* out_draw_data) 3989 { 3990 out_draw_data->Valid = true; 3991 out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; 3992 out_draw_data->CmdListsCount = draw_lists->Size; 3993 out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0; 3994 for (int n = 0; n < draw_lists->Size; n++) 3995 { 3996 out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; 3997 out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; 3998 } 3999 } 4000 4001 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result. 4141 int n = Layers[0].Size; 4142 int size = n; 4143 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) 4144 size += Layers[i].Size; 4145 Layers[0].resize(size); 4146 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) 4147 { 4148 ImVector<ImDrawList*>& layer = Layers[layer_n]; 4149 if (layer.empty()) 4150 continue; 4151 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); 4152 n += layer.Size; 4153 layer.resize(0); 4154 } 4155 } 4156 4157 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data) 4158 { 4159 ImGuiIO& io = ImGui::GetIO(); 4160 draw_data->Valid = true; 4161 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; 4162 draw_data->CmdListsCount = draw_lists->Size; 4163 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; 4164 draw_data->DisplayPos = ImVec2(0.0f, 0.0f); 4165 draw_data->DisplaySize = io.DisplaySize; 4166 draw_data->FramebufferScale = io.DisplayFramebufferScale; 4167 for (int n = 0; n < draw_lists->Size; n++) 4168 { 4169 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; 4170 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; 4171 } 4172 } 4173 4174 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. 4175 // - When using this function it is sane to ensure that float are perfectly rounded to integer values, 4176 // so that e.g. (int)(max.x-min.x) in user's render produce correct result. 4177 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect(): 4178 // some frequently called functions which to modify both channels and clipping simultaneously tend to use the 4179 // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds. 4002 4180 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) 4003 4181 { 4004 ImGuiWindow* window = GetCurrentWindow();4005 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);4006 window->ClipRect = window->DrawList->_ClipRectStack.back();4182 ImGuiWindow* window = GetCurrentWindow(); 4183 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); 4184 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4007 4185 } 4008 4186 4009 4187 void ImGui::PopClipRect() 4010 4188 { 4011 ImGuiWindow* window = GetCurrentWindow();4012 window->DrawList->PopClipRect();4013 window->ClipRect = window->DrawList->_ClipRectStack.back();4189 ImGuiWindow* window = GetCurrentWindow(); 4190 window->DrawList->PopClipRect(); 4191 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4014 4192 } 4015 4193 … … 4017 4195 void ImGui::EndFrame() 4018 4196 { 4019 ImGuiContext& g = *GImGui; 4020 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() 4021 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times. 4022 return; 4023 4024 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) 4025 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) 4026 { 4027 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); 4028 g.PlatformImeLastPos = g.PlatformImePos; 4029 } 4030 4031 // Hide implicit "Debug" window if it hasn't been used 4032 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls 4033 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) 4034 g.CurrentWindow->Active = false; 4035 End(); 4036 4037 if (g.ActiveId == 0 && g.HoveredId == 0) 4038 { 4039 if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear 4040 { 4041 // Click to focus window and start moving (after we're done with all our widgets) 4042 if (g.IO.MouseClicked[0]) 4043 { 4044 if (g.HoveredRootWindow != NULL) 4045 { 4046 // Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows. 4047 FocusWindow(g.HoveredWindow); 4048 SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow); 4049 g.NavDisableHighlight = true; 4050 g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos; 4051 if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove)) 4052 g.MovingWindow = g.HoveredWindow; 4053 } 4054 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) 4055 { 4056 // Clicking on void disable focus 4057 FocusWindow(NULL); 4058 } 4059 } 4060 4061 // With right mouse button we close popups without changing focus 4062 // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) 4063 if (g.IO.MouseClicked[1]) 4064 { 4065 // Find the top-most window between HoveredWindow and the front most Modal Window. 4066 // This is where we can trim the popup stack. 4067 ImGuiWindow* modal = GetFrontMostPopupModal(); 4068 bool hovered_window_above_modal = false; 4069 if (modal == NULL) 4070 hovered_window_above_modal = true; 4071 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) 4072 { 4073 ImGuiWindow* window = g.Windows[i]; 4074 if (window == modal) 4075 break; 4076 if (window == g.HoveredWindow) 4077 hovered_window_above_modal = true; 4078 } 4079 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); 4080 } 4081 } 4082 } 4083 4084 // Sort the window list so that all child windows are after their parent 4085 // We cannot do that on FocusWindow() because childs may not exist yet 4086 g.WindowsSortBuffer.resize(0); 4087 g.WindowsSortBuffer.reserve(g.Windows.Size); 4088 for (int i = 0; i != g.Windows.Size; i++) 4089 { 4090 ImGuiWindow* window = g.Windows[i]; 4091 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it 4092 continue; 4093 AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); 4094 } 4095 4096 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong 4097 g.Windows.swap(g.WindowsSortBuffer); 4098 4099 // Clear Input data for next frame 4100 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; 4101 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); 4102 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); 4103 4104 g.FrameCountEnded = g.FrameCount; 4197 ImGuiContext& g = *GImGui; 4198 IM_ASSERT(g.Initialized); 4199 4200 // Don't process EndFrame() multiple times. 4201 if (g.FrameCountEnded == g.FrameCount) 4202 return; 4203 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?"); 4204 4205 ErrorCheckEndFrameSanityChecks(); 4206 4207 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) 4208 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) 4209 { 4210 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); 4211 g.PlatformImeLastPos = g.PlatformImePos; 4212 } 4213 4214 // Hide implicit/fallback "Debug" window if it hasn't been used 4215 g.WithinFrameScopeWithImplicitWindow = false; 4216 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) 4217 g.CurrentWindow->Active = false; 4218 End(); 4219 4220 // Update navigation: CTRL+Tab, wrap-around requests 4221 NavEndFrame(); 4222 4223 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) 4224 if (g.DragDropActive) 4225 { 4226 bool is_delivered = g.DragDropPayload.Delivery; 4227 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); 4228 if (is_delivered || is_elapsed) 4229 ClearDragDrop(); 4230 } 4231 4232 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. 4233 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 4234 { 4235 g.DragDropWithinSource = true; 4236 SetTooltip("..."); 4237 g.DragDropWithinSource = false; 4238 } 4239 4240 // End frame 4241 g.WithinFrameScope = false; 4242 g.FrameCountEnded = g.FrameCount; 4243 4244 // Initiate moving window + handle left-click and right-click focus 4245 UpdateMouseMovingWindowEndFrame(); 4246 4247 // Sort the window list so that all child windows are after their parent 4248 // We cannot do that on FocusWindow() because children may not exist yet 4249 g.WindowsTempSortBuffer.resize(0); 4250 g.WindowsTempSortBuffer.reserve(g.Windows.Size); 4251 for (int i = 0; i != g.Windows.Size; i++) 4252 { 4253 ImGuiWindow* window = g.Windows[i]; 4254 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it 4255 continue; 4256 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); 4257 } 4258 4259 // 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. 4260 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size); 4261 g.Windows.swap(g.WindowsTempSortBuffer); 4262 g.IO.MetricsActiveWindows = g.WindowsActiveCount; 4263 4264 // Unlock font atlas 4265 g.IO.Fonts->Locked = false; 4266 4267 // Clear Input data for next frame 4268 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; 4269 g.IO.InputQueueCharacters.resize(0); 4270 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); 4105 4271 } 4106 4272 4107 4273 void ImGui::Render() 4108 4274 { 4109 ImGuiContext& g = *GImGui; 4110 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() 4111 4112 if (g.FrameCountEnded != g.FrameCount) 4113 ImGui::EndFrame(); 4114 g.FrameCountRendered = g.FrameCount; 4115 4116 // Gather windows to render 4117 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; 4118 g.DrawDataBuilder.Clear(); 4119 ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; 4120 for (int n = 0; n != g.Windows.Size; n++) 4121 { 4122 ImGuiWindow* window = g.Windows[n]; 4123 if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) 4124 AddWindowToDrawDataSelectLayer(window); 4125 } 4126 if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window 4127 AddWindowToDrawDataSelectLayer(window_to_render_front_most); 4128 g.DrawDataBuilder.FlattenIntoSingleLayer(); 4129 4130 // Draw software mouse cursor if requested 4131 ImVec2 offset, size, uv[4]; 4132 if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) 4133 { 4134 const ImVec2 pos = g.IO.MousePos - offset; 4135 const ImTextureID tex_id = g.IO.Fonts->TexID; 4136 const float sc = g.Style.MouseCursorScale; 4137 g.OverlayDrawList.PushTextureID(tex_id); 4138 g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1, 0)*sc, pos + ImVec2(1, 0)*sc + size * sc, uv[2], uv[3], IM_COL32(0, 0, 0, 48)); // Shadow 4139 g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2, 0)*sc, pos + ImVec2(2, 0)*sc + size * sc, uv[2], uv[3], IM_COL32(0, 0, 0, 48)); // Shadow 4140 g.OverlayDrawList.AddImage(tex_id, pos, pos + size * sc, uv[2], uv[3], IM_COL32(0, 0, 0, 255)); // Black border 4141 g.OverlayDrawList.AddImage(tex_id, pos, pos + size * sc, uv[0], uv[1], IM_COL32(255, 255, 255, 255)); // White fill 4142 g.OverlayDrawList.PopTextureID(); 4143 } 4144 if (!g.OverlayDrawList.VtxBuffer.empty()) 4145 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); 4146 4147 // Setup ImDrawData structure for end-user 4148 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); 4149 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; 4150 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; 4151 4152 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() 4275 ImGuiContext& g = *GImGui; 4276 IM_ASSERT(g.Initialized); 4277 4278 if (g.FrameCountEnded != g.FrameCount) 4279 EndFrame(); 4280 g.FrameCountRendered = g.FrameCount; 4281 g.IO.MetricsRenderWindows = 0; 4282 g.DrawDataBuilder.Clear(); 4283 4284 // Add background ImDrawList 4285 if (!g.BackgroundDrawList.VtxBuffer.empty()) 4286 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); 4287 4288 // Add ImDrawList to render 4289 ImGuiWindow* windows_to_render_top_most[2]; 4290 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; 4291 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); 4292 for (int n = 0; n != g.Windows.Size; n++) 4293 { 4294 ImGuiWindow* window = g.Windows[n]; 4295 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) 4296 AddRootWindowToDrawData(window); 4297 } 4298 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) 4299 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window 4300 AddRootWindowToDrawData(windows_to_render_top_most[n]); 4301 g.DrawDataBuilder.FlattenIntoSingleLayer(); 4302 4303 // Draw software mouse cursor if requested 4304 if (g.IO.MouseDrawCursor) 4305 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); 4306 4307 // Add foreground ImDrawList 4308 if (!g.ForegroundDrawList.VtxBuffer.empty()) 4309 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); 4310 4311 // Setup ImDrawData structure for end-user 4312 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); 4313 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; 4314 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; 4315 4316 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves. 4153 4317 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 4154 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)4155 g.IO.RenderDrawListsFn(&g.DrawData);4318 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) 4319 g.IO.RenderDrawListsFn(&g.DrawData); 4156 4320 #endif 4157 4321 } 4158 4322 4159 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) 4160 { 4161 const char* text_display_end = text; 4162 if (!text_end) 4163 text_end = (const char*)-1; 4164 4165 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) 4166 text_display_end++; 4167 return text_display_end; 4168 } 4169 4170 // Pass text data straight to log (without being displayed) 4171 void ImGui::LogText(const char* fmt, ...) 4172 { 4173 ImGuiContext& g = *GImGui; 4174 if (!g.LogEnabled) 4175 return; 4176 4177 va_list args; 4178 va_start(args, fmt); 4179 if (g.LogFile) 4180 vfprintf(g.LogFile, fmt, args); 4181 else 4182 g.LogClipboard->appendfv(fmt, args); 4183 va_end(args); 4184 } 4185 4186 // Internal version that takes a position to decide on newline placement and pad items according to their depth. 4187 // We split text into individual lines to add current tree level padding 4188 static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL) 4189 { 4190 ImGuiContext& g = *GImGui; 4191 ImGuiWindow* window = g.CurrentWindow; 4192 4193 if (!text_end) 4194 text_end = ImGui::FindRenderedTextEnd(text, text_end); 4195 4196 const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); 4197 if (ref_pos) 4198 window->DC.LogLinePosY = ref_pos->y; 4199 4200 const char* text_remaining = text; 4201 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth 4202 g.LogStartDepth = window->DC.TreeDepth; 4203 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); 4204 for (;;) 4205 { 4206 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. 4207 const char* line_end = text_remaining; 4208 while (line_end < text_end) 4209 if (*line_end == '\n') 4323 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. 4324 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize) 4325 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) 4326 { 4327 ImGuiContext& g = *GImGui; 4328 4329 const char* text_display_end; 4330 if (hide_text_after_double_hash) 4331 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string 4332 else 4333 text_display_end = text_end; 4334 4335 ImFont* font = g.Font; 4336 const float font_size = g.FontSize; 4337 if (text == text_display_end) 4338 return ImVec2(0.0f, font_size); 4339 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); 4340 4341 // Round 4342 text_size.x = IM_FLOOR(text_size.x + 0.95f); 4343 4344 return text_size; 4345 } 4346 4347 // Find window given position, search front-to-back 4348 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically 4349 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is 4350 // called, aka before the next Begin(). Moving window isn't affected. 4351 static void FindHoveredWindow() 4352 { 4353 ImGuiContext& g = *GImGui; 4354 4355 ImGuiWindow* hovered_window = NULL; 4356 ImGuiWindow* hovered_window_ignoring_moving_window = NULL; 4357 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) 4358 hovered_window = g.MovingWindow; 4359 4360 ImVec2 padding_regular = g.Style.TouchExtraPadding; 4361 ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; 4362 for (int i = g.Windows.Size - 1; i >= 0; i--) 4363 { 4364 ImGuiWindow* window = g.Windows[i]; 4365 if (!window->Active || window->Hidden) 4366 continue; 4367 if (window->Flags & ImGuiWindowFlags_NoMouseInputs) 4368 continue; 4369 4370 // Using the clipped AABB, a child window will typically be clipped by its parent (not always) 4371 ImRect bb(window->OuterRectClipped); 4372 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) 4373 bb.Expand(padding_regular); 4374 else 4375 bb.Expand(padding_for_resize_from_edges); 4376 if (!bb.Contains(g.IO.MousePos)) 4377 continue; 4378 4379 // Support for one rectangular hole in any given window 4380 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) 4381 if (window->HitTestHoleSize.x != 0) 4382 { 4383 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); 4384 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); 4385 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos)) 4386 continue; 4387 } 4388 4389 if (hovered_window == NULL) 4390 hovered_window = window; 4391 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) 4392 hovered_window_ignoring_moving_window = window; 4393 if (hovered_window && hovered_window_ignoring_moving_window) 4210 4394 break; 4211 else 4212 line_end++; 4213 if (line_end >= text_end) 4214 line_end = NULL; 4215 4216 const bool is_first_line = (text == text_remaining); 4217 bool is_last_line = false; 4218 if (line_end == NULL) 4219 { 4220 is_last_line = true; 4221 line_end = text_end; 4222 } 4223 if (line_end != NULL && !(is_last_line && (line_end - text_remaining) == 0)) 4224 { 4225 const int char_count = (int)(line_end - text_remaining); 4226 if (log_new_line || !is_first_line) 4227 ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, text_remaining); 4228 else 4229 ImGui::LogText(" %.*s", char_count, text_remaining); 4230 } 4231 4232 if (is_last_line) 4233 break; 4234 text_remaining = line_end + 1; 4235 } 4236 } 4237 4238 // Internal ImGui functions to render text 4239 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() 4240 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) 4241 { 4242 ImGuiContext& g = *GImGui; 4243 ImGuiWindow* window = g.CurrentWindow; 4244 4245 // Hide anything after a '##' string 4246 const char* text_display_end; 4247 if (hide_text_after_hash) 4248 { 4249 text_display_end = FindRenderedTextEnd(text, text_end); 4250 } 4251 else 4252 { 4253 if (!text_end) 4254 text_end = text + strlen(text); // FIXME-OPT 4255 text_display_end = text_end; 4256 } 4257 4258 if (text != text_display_end) 4259 { 4260 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); 4261 if (g.LogEnabled) 4262 LogRenderedText(&pos, text, text_display_end); 4263 } 4264 } 4265 4266 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) 4267 { 4268 ImGuiContext& g = *GImGui; 4269 ImGuiWindow* window = g.CurrentWindow; 4270 4271 if (!text_end) 4272 text_end = text + strlen(text); // FIXME-OPT 4273 4274 if (text != text_end) 4275 { 4276 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); 4277 if (g.LogEnabled) 4278 LogRenderedText(&pos, text, text_end); 4279 } 4280 } 4281 4282 // Default clip_rect uses (pos_min,pos_max) 4283 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) 4284 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) 4285 { 4286 // Hide anything after a '##' string 4287 const char* text_display_end = FindRenderedTextEnd(text, text_end); 4288 const int text_len = (int)(text_display_end - text); 4289 if (text_len == 0) 4290 return; 4291 4292 ImGuiContext& g = *GImGui; 4293 ImGuiWindow* window = g.CurrentWindow; 4294 4295 // Perform CPU side clipping for single clipped element to avoid using scissor state 4296 ImVec2 pos = pos_min; 4297 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); 4298 4299 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; 4300 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; 4301 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); 4302 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min 4303 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); 4304 4305 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. 4306 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); 4307 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); 4308 4309 // Render 4310 if (need_clipping) 4311 { 4312 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); 4313 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); 4314 } 4315 else 4316 { 4317 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); 4318 } 4319 if (g.LogEnabled) 4320 LogRenderedText(&pos, text, text_display_end); 4321 } 4322 4323 // Render a rectangle shaped with optional rounding and borders 4324 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) 4325 { 4326 ImGuiContext& g = *GImGui; 4327 ImGuiWindow* window = g.CurrentWindow; 4328 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); 4329 const float border_size = g.Style.FrameBorderSize; 4330 if (border && border_size > 0.0f) 4331 { 4332 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 4333 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 4334 } 4335 } 4336 4337 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) 4338 { 4339 ImGuiContext& g = *GImGui; 4340 ImGuiWindow* window = g.CurrentWindow; 4341 const float border_size = g.Style.FrameBorderSize; 4342 if (border_size > 0.0f) 4343 { 4344 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 4345 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 4346 } 4347 } 4348 4349 // Render a triangle to denote expanded/collapsed state 4350 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale) 4351 { 4352 ImGuiContext& g = *GImGui; 4353 ImGuiWindow* window = g.CurrentWindow; 4354 4355 const float h = g.FontSize * 1.00f; 4356 float r = h * 0.40f * scale; 4357 ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale); 4358 4359 ImVec2 a, b, c; 4360 switch (dir) 4361 { 4362 case ImGuiDir_Up: 4363 case ImGuiDir_Down: 4364 if (dir == ImGuiDir_Up) r = -r; 4365 center.y -= r * 0.25f; 4366 a = ImVec2(0, 1) * r; 4367 b = ImVec2(-0.866f, -0.5f) * r; 4368 c = ImVec2(+0.866f, -0.5f) * r; 4369 break; 4370 case ImGuiDir_Left: 4371 case ImGuiDir_Right: 4372 if (dir == ImGuiDir_Left) r = -r; 4373 center.x -= r * 0.25f; 4374 a = ImVec2(1, 0) * r; 4375 b = ImVec2(-0.500f, +0.866f) * r; 4376 c = ImVec2(-0.500f, -0.866f) * r; 4377 break; 4378 case ImGuiDir_None: 4379 case ImGuiDir_COUNT: 4380 IM_ASSERT(0); 4381 break; 4382 } 4383 4384 window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text)); 4385 } 4386 4387 void ImGui::RenderBullet(ImVec2 pos) 4388 { 4389 ImGuiContext& g = *GImGui; 4390 ImGuiWindow* window = g.CurrentWindow; 4391 window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); 4392 } 4393 4394 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) 4395 { 4396 ImGuiContext& g = *GImGui; 4397 ImGuiWindow* window = g.CurrentWindow; 4398 4399 float thickness = ImMax(sz / 5.0f, 1.0f); 4400 sz -= thickness * 0.5f; 4401 pos += ImVec2(thickness*0.25f, thickness*0.25f); 4402 4403 float third = sz / 3.0f; 4404 float bx = pos.x + third; 4405 float by = pos.y + sz - third * 0.5f; 4406 window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); 4407 window->DrawList->PathLineTo(ImVec2(bx, by)); 4408 window->DrawList->PathLineTo(ImVec2(bx + third * 2, by - third * 2)); 4409 window->DrawList->PathStroke(col, false, thickness); 4410 } 4411 4412 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) 4413 { 4414 ImGuiContext& g = *GImGui; 4415 if (id != g.NavId) 4416 return; 4417 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) 4418 return; 4419 ImGuiWindow* window = ImGui::GetCurrentWindow(); 4420 if (window->DC.NavHideHighlightOneFrame) 4421 return; 4422 4423 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; 4424 ImRect display_rect = bb; 4425 display_rect.ClipWith(window->ClipRect); 4426 if (flags & ImGuiNavHighlightFlags_TypeDefault) 4427 { 4428 const float THICKNESS = 2.0f; 4429 const float DISTANCE = 3.0f + THICKNESS * 0.5f; 4430 display_rect.Expand(ImVec2(DISTANCE, DISTANCE)); 4431 bool fully_visible = window->ClipRect.Contains(display_rect); 4432 if (!fully_visible) 4433 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); 4434 window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f, THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f, THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); 4435 if (!fully_visible) 4436 window->DrawList->PopClipRect(); 4437 } 4438 if (flags & ImGuiNavHighlightFlags_TypeThin) 4439 { 4440 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); 4441 } 4442 } 4443 4444 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. 4445 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) 4446 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) 4447 { 4448 ImGuiContext& g = *GImGui; 4449 4450 const char* text_display_end; 4451 if (hide_text_after_double_hash) 4452 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string 4453 else 4454 text_display_end = text_end; 4455 4456 ImFont* font = g.Font; 4457 const float font_size = g.FontSize; 4458 if (text == text_display_end) 4459 return ImVec2(0.0f, font_size); 4460 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); 4461 4462 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) 4463 const float font_scale = font_size / font->FontSize; 4464 const float character_spacing_x = 1.0f * font_scale; 4465 if (text_size.x > 0.0f) 4466 text_size.x -= character_spacing_x; 4467 text_size.x = (float)(int)(text_size.x + 0.95f); 4468 4469 return text_size; 4470 } 4471 4472 // Helper to calculate coarse clipping of large list of evenly sized items. 4473 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. 4474 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX 4475 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) 4476 { 4477 ImGuiContext& g = *GImGui; 4478 ImGuiWindow* window = g.CurrentWindow; 4479 if (g.LogEnabled) 4480 { 4481 // If logging is active, do not perform any clipping 4482 *out_items_display_start = 0; 4483 *out_items_display_end = items_count; 4484 return; 4485 } 4486 if (window->SkipItems) 4487 { 4488 *out_items_display_start = *out_items_display_end = 0; 4489 return; 4490 } 4491 4492 const ImVec2 pos = window->DC.CursorPos; 4493 int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); 4494 int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); 4495 if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to 4496 start--; 4497 if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) 4498 end++; 4499 4500 start = ImClamp(start, 0, items_count); 4501 end = ImClamp(end + 1, start, items_count); 4502 *out_items_display_start = start; 4503 *out_items_display_end = end; 4504 } 4505 4506 // Find window given position, search front-to-back 4507 // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. 4508 static ImGuiWindow* FindHoveredWindow() 4509 { 4510 ImGuiContext& g = *GImGui; 4511 for (int i = g.Windows.Size - 1; i >= 0; i--) 4512 { 4513 ImGuiWindow* window = g.Windows[i]; 4514 if (!window->Active) 4515 continue; 4516 if (window->Flags & ImGuiWindowFlags_NoInputs) 4517 continue; 4518 4519 // Using the clipped AABB, a child window will typically be clipped by its parent (not always) 4520 ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); 4521 if (bb.Contains(g.IO.MousePos)) 4522 return window; 4523 } 4524 return NULL; 4395 } 4396 4397 g.HoveredWindow = hovered_window; 4398 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; 4399 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; 4525 4400 } 4526 4401 … … 4530 4405 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) 4531 4406 { 4532 ImGuiContext& g = *GImGui; 4533 4534 // Clip 4535 ImRect rect_clipped(r_min, r_max); 4536 if (clip) 4537 rect_clipped.ClipWith(g.CurrentWindow->ClipRect); 4538 4539 // Expand for touch input 4540 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); 4541 return rect_for_touch.Contains(g.IO.MousePos); 4542 } 4543 4544 static bool IsKeyPressedMap(ImGuiKey key, bool repeat) 4545 { 4546 const int key_index = GImGui->IO.KeyMap[key]; 4547 return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false; 4407 ImGuiContext& g = *GImGui; 4408 4409 // Clip 4410 ImRect rect_clipped(r_min, r_max); 4411 if (clip) 4412 rect_clipped.ClipWith(g.CurrentWindow->ClipRect); 4413 4414 // Expand for touch input 4415 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); 4416 if (!rect_for_touch.Contains(g.IO.MousePos)) 4417 return false; 4418 return true; 4548 4419 } 4549 4420 4550 4421 int ImGui::GetKeyIndex(ImGuiKey imgui_key) 4551 4422 { 4552 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); 4553 return GImGui->IO.KeyMap[imgui_key]; 4554 } 4555 4556 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]! 4423 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); 4424 ImGuiContext& g = *GImGui; 4425 return g.IO.KeyMap[imgui_key]; 4426 } 4427 4428 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! 4429 // Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]! 4557 4430 bool ImGui::IsKeyDown(int user_key_index) 4558 4431 { 4559 if (user_key_index < 0) return false; 4560 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown)); 4561 return GImGui->IO.KeysDown[user_key_index]; 4562 } 4563 4564 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate) 4565 { 4566 if (t == 0.0f) 4567 return 1; 4568 if (t <= repeat_delay || repeat_rate <= 0.0f) 4569 return 0; 4570 const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate); 4571 return (count > 0) ? count : 0; 4432 if (user_key_index < 0) 4433 return false; 4434 ImGuiContext& g = *GImGui; 4435 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4436 return g.IO.KeysDown[user_key_index]; 4437 } 4438 4439 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) 4440 // t1 = current time (e.g.: g.Time) 4441 // An event is triggered at: 4442 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N 4443 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) 4444 { 4445 if (t1 == 0.0f) 4446 return 1; 4447 if (t0 >= t1) 4448 return 0; 4449 if (repeat_rate <= 0.0f) 4450 return (t0 < repeat_delay) && (t1 >= repeat_delay); 4451 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); 4452 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); 4453 const int count = count_t1 - count_t0; 4454 return count; 4572 4455 } 4573 4456 4574 4457 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) 4575 4458 { 4576 ImGuiContext& g = *GImGui; 4577 if (key_index < 0) return false; 4578 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4579 const float t = g.IO.KeysDownDuration[key_index]; 4580 return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate); 4459 ImGuiContext& g = *GImGui; 4460 if (key_index < 0) 4461 return 0; 4462 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4463 const float t = g.IO.KeysDownDuration[key_index]; 4464 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); 4581 4465 } 4582 4466 4583 4467 bool ImGui::IsKeyPressed(int user_key_index, bool repeat) 4584 4468 { 4585 ImGuiContext& g = *GImGui; 4586 if (user_key_index < 0) return false; 4587 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4588 const float t = g.IO.KeysDownDuration[user_key_index]; 4589 if (t == 0.0f) 4590 return true; 4591 if (repeat && t > g.IO.KeyRepeatDelay) 4592 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; 4593 return false; 4469 ImGuiContext& g = *GImGui; 4470 if (user_key_index < 0) 4471 return false; 4472 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4473 const float t = g.IO.KeysDownDuration[user_key_index]; 4474 if (t == 0.0f) 4475 return true; 4476 if (repeat && t > g.IO.KeyRepeatDelay) 4477 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; 4478 return false; 4594 4479 } 4595 4480 4596 4481 bool ImGui::IsKeyReleased(int user_key_index) 4597 4482 { 4598 ImGuiContext& g = *GImGui;4599 if (user_key_index < 0) return false;4600 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));4601 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];4602 } 4603 4604 bool ImGui::IsMouseDown( intbutton)4605 { 4606 ImGuiContext& g = *GImGui;4607 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));4608 return g.IO.MouseDown[button];4609 } 4610 4611 bool ImGui::Is AnyMouseDown()4612 { 4613 ImGuiContext& g = *GImGui;4614 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)4615 if (g.IO.MouseDown[n])4616 return true;4617 return false;4618 } 4619 4620 bool ImGui::IsMouseClicked(int button, bool repeat) 4621 { 4622 ImGuiContext& g = *GImGui;4623 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));4624 const float t = g.IO.MouseDownDuration[button];4625 if (t == 0.0f)4626 return true;4627 4628 if (repeat && t > g.IO.KeyRepeatDelay) 4629 { 4630 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; 4631 if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))4632 return true;4633 }4634 4635 return false; 4636 } 4637 4638 bool ImGui::IsMouseReleased(int button) 4639 { 4640 ImGuiContext& g = *GImGui;4641 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4642 return g.IO.MouseReleased[button]; 4643 } 4644 4645 bool ImGui::IsMouseD oubleClicked(int button)4646 { 4647 ImGuiContext& g = *GImGui;4648 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));4649 return g.IO.MouseDoubleClicked[button];4650 } 4651 4652 bool ImGui::IsMouseDragging(int button, float lock_threshold) 4653 { 4654 ImGuiContext& g = *GImGui; 4655 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4656 if (!g.IO.MouseDown[button])4657 return false;4658 if (lock_threshold < 0.0f)4659 lock_threshold = g.IO.MouseDragThreshold;4660 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;4483 ImGuiContext& g = *GImGui; 4484 if (user_key_index < 0) return false; 4485 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4486 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; 4487 } 4488 4489 bool ImGui::IsMouseDown(ImGuiMouseButton button) 4490 { 4491 ImGuiContext& g = *GImGui; 4492 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4493 return g.IO.MouseDown[button]; 4494 } 4495 4496 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) 4497 { 4498 ImGuiContext& g = *GImGui; 4499 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4500 const float t = g.IO.MouseDownDuration[button]; 4501 if (t == 0.0f) 4502 return true; 4503 4504 if (repeat && t > g.IO.KeyRepeatDelay) 4505 { 4506 // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. 4507 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); 4508 if (amount > 0) 4509 return true; 4510 } 4511 return false; 4512 } 4513 4514 bool ImGui::IsMouseReleased(ImGuiMouseButton button) 4515 { 4516 ImGuiContext& g = *GImGui; 4517 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4518 return g.IO.MouseReleased[button]; 4519 } 4520 4521 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) 4522 { 4523 ImGuiContext& g = *GImGui; 4524 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4525 return g.IO.MouseDoubleClicked[button]; 4526 } 4527 4528 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. 4529 // [Internal] This doesn't test if the button is pressed 4530 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) 4531 { 4532 ImGuiContext& g = *GImGui; 4533 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4534 if (lock_threshold < 0.0f) 4535 lock_threshold = g.IO.MouseDragThreshold; 4536 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; 4537 } 4538 4539 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) 4540 { 4541 ImGuiContext& g = *GImGui; 4542 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4543 if (!g.IO.MouseDown[button]) 4544 return false; 4545 return IsMouseDragPastThreshold(button, lock_threshold); 4661 4546 } 4662 4547 4663 4548 ImVec2 ImGui::GetMousePos() 4664 4549 { 4665 return GImGui->IO.MousePos; 4550 ImGuiContext& g = *GImGui; 4551 return g.IO.MousePos; 4666 4552 } 4667 4553 … … 4669 4555 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() 4670 4556 { 4671 ImGuiContext& g = *GImGui;4672 if (g.CurrentPopupStack.Size > 0)4673 return g.OpenPopupStack[g.CurrentPopupStack.Size - 1].OpenMousePos;4674 return g.IO.MousePos;4675 } 4676 4677 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position 4557 ImGuiContext& g = *GImGui; 4558 if (g.BeginPopupStack.Size > 0) 4559 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; 4560 return g.IO.MousePos; 4561 } 4562 4563 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. 4678 4564 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) 4679 4565 { 4680 if (mouse_pos == NULL) 4681 mouse_pos = &GImGui->IO.MousePos; 4682 const float MOUSE_INVALID = -256000.0f; 4683 return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID; 4684 } 4685 4566 // The assert is only to silence a false-positive in XCode Static Analysis. 4567 // 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). 4568 IM_ASSERT(GImGui != NULL); 4569 const float MOUSE_INVALID = -256000.0f; 4570 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; 4571 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; 4572 } 4573 4574 bool ImGui::IsAnyMouseDown() 4575 { 4576 ImGuiContext& g = *GImGui; 4577 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) 4578 if (g.IO.MouseDown[n]) 4579 return true; 4580 return false; 4581 } 4582 4583 // Return the delta from the initial clicking position while the mouse button is clicked or was just released. 4584 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once. 4686 4585 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. 4687 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) 4688 { 4689 ImGuiContext& g = *GImGui; 4690 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4691 if (lock_threshold < 0.0f) 4692 lock_threshold = g.IO.MouseDragThreshold; 4693 if (g.IO.MouseDown[button]) 4694 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) 4695 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). 4696 return ImVec2(0.0f, 0.0f); 4697 } 4698 4699 void ImGui::ResetMouseDragDelta(int button) 4700 { 4701 ImGuiContext& g = *GImGui; 4702 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4703 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr 4704 g.IO.MouseClickedPos[button] = g.IO.MousePos; 4586 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) 4587 { 4588 ImGuiContext& g = *GImGui; 4589 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4590 if (lock_threshold < 0.0f) 4591 lock_threshold = g.IO.MouseDragThreshold; 4592 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) 4593 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) 4594 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) 4595 return g.IO.MousePos - g.IO.MouseClickedPos[button]; 4596 return ImVec2(0.0f, 0.0f); 4597 } 4598 4599 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) 4600 { 4601 ImGuiContext& g = *GImGui; 4602 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4603 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr 4604 g.IO.MouseClickedPos[button] = g.IO.MousePos; 4705 4605 } 4706 4606 4707 4607 ImGuiMouseCursor ImGui::GetMouseCursor() 4708 4608 { 4709 return GImGui->MouseCursor;4609 return GImGui->MouseCursor; 4710 4610 } 4711 4611 4712 4612 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) 4713 4613 { 4714 GImGui->MouseCursor = cursor_type;4614 GImGui->MouseCursor = cursor_type; 4715 4615 } 4716 4616 4717 4617 void ImGui::CaptureKeyboardFromApp(bool capture) 4718 4618 { 4719 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;4619 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; 4720 4620 } 4721 4621 4722 4622 void ImGui::CaptureMouseFromApp(bool capture) 4723 4623 { 4724 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;4624 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; 4725 4625 } 4726 4626 4727 4627 bool ImGui::IsItemActive() 4728 4628 { 4729 ImGuiContext& g = *GImGui; 4730 if (g.ActiveId) 4731 { 4732 ImGuiWindow* window = g.CurrentWindow; 4733 return g.ActiveId == window->DC.LastItemId; 4734 } 4735 return false; 4629 ImGuiContext& g = *GImGui; 4630 if (g.ActiveId) 4631 { 4632 ImGuiWindow* window = g.CurrentWindow; 4633 return g.ActiveId == window->DC.LastItemId; 4634 } 4635 return false; 4636 } 4637 4638 bool ImGui::IsItemActivated() 4639 { 4640 ImGuiContext& g = *GImGui; 4641 if (g.ActiveId) 4642 { 4643 ImGuiWindow* window = g.CurrentWindow; 4644 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) 4645 return true; 4646 } 4647 return false; 4648 } 4649 4650 bool ImGui::IsItemDeactivated() 4651 { 4652 ImGuiContext& g = *GImGui; 4653 ImGuiWindow* window = g.CurrentWindow; 4654 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) 4655 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; 4656 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); 4657 } 4658 4659 bool ImGui::IsItemDeactivatedAfterEdit() 4660 { 4661 ImGuiContext& g = *GImGui; 4662 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); 4736 4663 } 4737 4664 4738 4665 bool ImGui::IsItemFocused() 4739 4666 { 4740 ImGuiContext& g = *GImGui; 4741 return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; 4742 } 4743 4744 bool ImGui::IsItemClicked(int mouse_button) 4745 { 4746 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_Default); 4667 ImGuiContext& g = *GImGui; 4668 ImGuiWindow* window = g.CurrentWindow; 4669 4670 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) 4671 return false; 4672 return true; 4673 } 4674 4675 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) 4676 { 4677 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); 4678 } 4679 4680 bool ImGui::IsItemToggledOpen() 4681 { 4682 ImGuiContext& g = *GImGui; 4683 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; 4684 } 4685 4686 bool ImGui::IsItemToggledSelection() 4687 { 4688 ImGuiContext& g = *GImGui; 4689 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; 4747 4690 } 4748 4691 4749 4692 bool ImGui::IsAnyItemHovered() 4750 4693 { 4751 ImGuiContext& g = *GImGui;4752 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;4694 ImGuiContext& g = *GImGui; 4695 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; 4753 4696 } 4754 4697 4755 4698 bool ImGui::IsAnyItemActive() 4756 4699 { 4757 ImGuiContext& g = *GImGui;4758 return g.ActiveId != 0;4700 ImGuiContext& g = *GImGui; 4701 return g.ActiveId != 0; 4759 4702 } 4760 4703 4761 4704 bool ImGui::IsAnyItemFocused() 4762 4705 { 4763 ImGuiContext& g = *GImGui;4764 return g.NavId != 0 && !g.NavDisableHighlight;4706 ImGuiContext& g = *GImGui; 4707 return g.NavId != 0 && !g.NavDisableHighlight; 4765 4708 } 4766 4709 4767 4710 bool ImGui::IsItemVisible() 4768 4711 { 4769 ImGuiWindow* window = GetCurrentWindowRead(); 4770 return window->ClipRect.Overlaps(window->DC.LastItemRect); 4712 ImGuiWindow* window = GetCurrentWindowRead(); 4713 return window->ClipRect.Overlaps(window->DC.LastItemRect); 4714 } 4715 4716 bool ImGui::IsItemEdited() 4717 { 4718 ImGuiWindow* window = GetCurrentWindowRead(); 4719 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; 4771 4720 } 4772 4721 … … 4774 4723 void ImGui::SetItemAllowOverlap() 4775 4724 { 4776 ImGuiContext& g = *GImGui;4777 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)4778 g.HoveredIdAllowOverlap = true;4779 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)4780 g.ActiveIdAllowOverlap = true;4725 ImGuiContext& g = *GImGui; 4726 if (g.HoveredId == g.CurrentWindow->DC.LastItemId) 4727 g.HoveredIdAllowOverlap = true; 4728 if (g.ActiveId == g.CurrentWindow->DC.LastItemId) 4729 g.ActiveIdAllowOverlap = true; 4781 4730 } 4782 4731 4783 4732 ImVec2 ImGui::GetItemRectMin() 4784 4733 { 4785 ImGuiWindow* window = GetCurrentWindowRead();4786 return window->DC.LastItemRect.Min;4734 ImGuiWindow* window = GetCurrentWindowRead(); 4735 return window->DC.LastItemRect.Min; 4787 4736 } 4788 4737 4789 4738 ImVec2 ImGui::GetItemRectMax() 4790 4739 { 4791 ImGuiWindow* window = GetCurrentWindowRead();4792 return window->DC.LastItemRect.Max;4740 ImGuiWindow* window = GetCurrentWindowRead(); 4741 return window->DC.LastItemRect.Max; 4793 4742 } 4794 4743 4795 4744 ImVec2 ImGui::GetItemRectSize() 4796 4745 { 4797 ImGuiWindow* window = GetCurrentWindowRead();4798 return window->DC.LastItemRect.GetSize();4746 ImGuiWindow* window = GetCurrentWindowRead(); 4747 return window->DC.LastItemRect.GetSize(); 4799 4748 } 4800 4749 4801 4750 static ImRect GetViewportRect() 4802 4751 { 4803 ImGuiContext& g = *GImGui; 4804 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) 4805 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); 4806 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 4807 } 4808 4809 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. 4810 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) 4811 { 4812 ImGuiContext& g = *GImGui; 4813 char window_name[16]; 4814 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); 4815 if (override_previous_tooltip) 4816 if (ImGuiWindow* window = FindWindowByName(window_name)) 4817 if (window->Active) 4818 { 4819 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. 4820 window->HiddenFrames = 1; 4821 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); 4822 } 4823 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNav; 4824 Begin(window_name, NULL, flags | extra_flags); 4825 } 4826 4827 void ImGui::SetTooltipV(const char* fmt, va_list args) 4828 { 4829 BeginTooltipEx(0, true); 4830 TextV(fmt, args); 4831 EndTooltip(); 4832 } 4833 4834 void ImGui::SetTooltip(const char* fmt, ...) 4835 { 4836 va_list args; 4837 va_start(args, fmt); 4838 SetTooltipV(fmt, args); 4839 va_end(args); 4840 } 4841 4842 void ImGui::BeginTooltip() 4843 { 4844 BeginTooltipEx(0, false); 4845 } 4846 4847 void ImGui::EndTooltip() 4848 { 4849 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls 4850 End(); 4851 } 4852 4853 // Mark popup as open (toggle toward open state). 4854 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. 4855 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). 4856 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) 4857 void ImGui::OpenPopupEx(ImGuiID id) 4858 { 4859 ImGuiContext& g = *GImGui; 4860 ImGuiWindow* parent_window = g.CurrentWindow; 4861 int current_stack_size = g.CurrentPopupStack.Size; 4862 ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. 4863 popup_ref.PopupId = id; 4864 popup_ref.Window = NULL; 4865 popup_ref.ParentWindow = parent_window; 4866 popup_ref.OpenFrameCount = g.FrameCount; 4867 popup_ref.OpenParentId = parent_window->IDStack.back(); 4868 popup_ref.OpenMousePos = g.IO.MousePos; 4869 popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); 4870 4871 //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); 4872 if (g.OpenPopupStack.Size < current_stack_size + 1) 4873 { 4874 g.OpenPopupStack.push_back(popup_ref); 4875 } 4876 else 4877 { 4878 // Close child popups if any 4879 g.OpenPopupStack.resize(current_stack_size + 1); 4880 4881 // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui 4882 // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing 4883 // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. 4884 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) 4885 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; 4886 else 4887 g.OpenPopupStack[current_stack_size] = popup_ref; 4888 4889 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). 4890 // This is equivalent to what ClosePopupToLevel() does. 4891 //if (g.OpenPopupStack[current_stack_size].PopupId == id) 4892 // FocusWindow(parent_window); 4893 } 4894 } 4895 4896 void ImGui::OpenPopup(const char* str_id) 4897 { 4898 ImGuiContext& g = *GImGui; 4899 OpenPopupEx(g.CurrentWindow->GetID(str_id)); 4900 } 4901 4902 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) 4903 { 4904 ImGuiContext& g = *GImGui; 4905 if (g.OpenPopupStack.empty()) 4906 return; 4907 4908 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. 4909 // Don't close our own child popup windows. 4910 int n = 0; 4911 if (ref_window) 4912 { 4913 for (n = 0; n < g.OpenPopupStack.Size; n++) 4914 { 4915 ImGuiPopupRef& popup = g.OpenPopupStack[n]; 4916 if (!popup.Window) 4917 continue; 4918 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); 4919 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) 4920 continue; 4921 4922 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) 4923 bool has_focus = false; 4924 for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) 4925 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); 4926 if (!has_focus) 4927 break; 4928 } 4929 } 4930 if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below 4931 ClosePopupToLevel(n); 4932 } 4933 4934 ImGuiWindow* ImGui::GetFrontMostPopupModal() 4935 { 4936 ImGuiContext& g = *GImGui; 4937 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) 4938 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) 4939 if (popup->Flags & ImGuiWindowFlags_Modal) 4940 return popup; 4941 return NULL; 4942 } 4943 4944 static void ClosePopupToLevel(int remaining) 4945 { 4946 IM_ASSERT(remaining >= 0); 4947 ImGuiContext& g = *GImGui; 4948 ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining - 1].Window : g.OpenPopupStack[0].ParentWindow; 4949 if (g.NavLayer == 0) 4950 focus_window = NavRestoreLastChildNavWindow(focus_window); 4951 ImGui::FocusWindow(focus_window); 4952 focus_window->DC.NavHideHighlightOneFrame = true; 4953 g.OpenPopupStack.resize(remaining); 4954 } 4955 4956 void ImGui::ClosePopup(ImGuiID id) 4957 { 4958 if (!IsPopupOpen(id)) 4959 return; 4960 ImGuiContext& g = *GImGui; 4961 ClosePopupToLevel(g.OpenPopupStack.Size - 1); 4962 } 4963 4964 // Close the popup we have begin-ed into. 4965 void ImGui::CloseCurrentPopup() 4966 { 4967 ImGuiContext& g = *GImGui; 4968 int popup_idx = g.CurrentPopupStack.Size - 1; 4969 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) 4970 return; 4971 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) 4972 popup_idx--; 4973 ClosePopupToLevel(popup_idx); 4974 } 4975 4976 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) 4977 { 4978 ImGuiContext& g = *GImGui; 4979 if (!IsPopupOpen(id)) 4980 { 4981 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values 4982 return false; 4983 } 4984 4985 char name[20]; 4986 if (extra_flags & ImGuiWindowFlags_ChildMenu) 4987 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth 4988 else 4989 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame 4990 4991 bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); 4992 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) 4993 EndPopup(); 4994 4995 return is_open; 4996 } 4997 4998 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) 4999 { 5000 ImGuiContext& g = *GImGui; 5001 if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance 5002 { 5003 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values 5004 return false; 5005 } 5006 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 5007 } 5008 5009 bool ImGui::IsPopupOpen(ImGuiID id) 5010 { 5011 ImGuiContext& g = *GImGui; 5012 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; 5013 } 5014 5015 bool ImGui::IsPopupOpen(const char* str_id) 5016 { 5017 ImGuiContext& g = *GImGui; 5018 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); 5019 } 5020 5021 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) 5022 { 5023 ImGuiContext& g = *GImGui; 5024 ImGuiWindow* window = g.CurrentWindow; 5025 const ImGuiID id = window->GetID(name); 5026 if (!IsPopupOpen(id)) 5027 { 5028 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values 5029 return false; 5030 } 5031 5032 // Center modal windows by default 5033 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. 5034 if (g.NextWindowData.PosCond == 0) 5035 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); 5036 5037 bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); 5038 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) 5039 { 5040 EndPopup(); 5041 if (is_open) 5042 ClosePopup(id); 5043 return false; 5044 } 5045 5046 return is_open; 5047 } 5048 5049 static void NavProcessMoveRequestWrapAround(ImGuiWindow* window) 5050 { 5051 ImGuiContext& g = *GImGui; 5052 if (g.NavWindow == window && NavMoveRequestButNoResultYet()) 5053 if ((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == 0) 5054 { 5055 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 5056 ImGui::NavMoveRequestCancel(); 5057 g.NavWindow->NavRectRel[0].Min.y = g.NavWindow->NavRectRel[0].Max.y = ((g.NavMoveDir == ImGuiDir_Up) ? ImMax(window->SizeFull.y, window->SizeContents.y) : 0.0f) - window->Scroll.y; 5058 } 5059 } 5060 5061 void ImGui::EndPopup() 5062 { 5063 ImGuiContext& g = *GImGui; (void)g; 5064 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls 5065 IM_ASSERT(g.CurrentPopupStack.Size > 0); 5066 5067 // Make all menus and popups wrap around for now, may need to expose that policy. 5068 NavProcessMoveRequestWrapAround(g.CurrentWindow); 5069 5070 End(); 5071 } 5072 5073 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) 5074 { 5075 ImGuiWindow* window = GImGui->CurrentWindow; 5076 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 5077 { 5078 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! 5079 IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 5080 OpenPopupEx(id); 5081 return true; 5082 } 5083 return false; 5084 } 5085 5086 // This is a helper to handle the simplest case of associating one named popup to one given widget. 5087 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). 5088 // You can pass a NULL str_id to use the identifier of the last item. 5089 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) 5090 { 5091 ImGuiWindow* window = GImGui->CurrentWindow; 5092 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! 5093 IM_ASSERT(id != 0); // However, you cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 5094 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 5095 OpenPopupEx(id); 5096 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 5097 } 5098 5099 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) 5100 { 5101 if (!str_id) 5102 str_id = "window_context"; 5103 ImGuiID id = GImGui->CurrentWindow->GetID(str_id); 5104 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 5105 if (also_over_items || !IsAnyItemHovered()) 5106 OpenPopupEx(id); 5107 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 5108 } 5109 5110 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) 5111 { 5112 if (!str_id) 5113 str_id = "void_context"; 5114 ImGuiID id = GImGui->CurrentWindow->GetID(str_id); 5115 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) 5116 OpenPopupEx(id); 5117 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 5118 } 5119 5120 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 5121 { 5122 ImGuiContext& g = *GImGui; 5123 ImGuiWindow* parent_window = ImGui::GetCurrentWindow(); 5124 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; 5125 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag 5126 5127 const ImVec2 content_avail = ImGui::GetContentRegionAvail(); 5128 ImVec2 size = ImFloor(size_arg); 5129 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); 5130 if (size.x <= 0.0f) 5131 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) 5132 if (size.y <= 0.0f) 5133 size.y = ImMax(content_avail.y + size.y, 4.0f); 5134 5135 const float backup_border_size = g.Style.ChildBorderSize; 5136 if (!border) 5137 g.Style.ChildBorderSize = 0.0f; 5138 flags |= extra_flags; 5139 5140 char title[256]; 5141 if (name) 5142 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name); 5143 else 5144 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); 5145 5146 ImGui::SetNextWindowSize(size); 5147 bool ret = ImGui::Begin(title, NULL, flags); 5148 ImGuiWindow* child_window = ImGui::GetCurrentWindow(); 5149 child_window->ChildId = id; 5150 child_window->AutoFitChildAxises = auto_fit_axises; 5151 g.Style.ChildBorderSize = backup_border_size; 5152 5153 // Process navigation-in immediately so NavInit can run on first frame 5154 if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) 5155 { 5156 ImGui::FocusWindow(child_window); 5157 ImGui::NavInitWindow(child_window, false); 5158 ImGui::SetActiveID(id + 1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item 5159 g.ActiveIdSource = ImGuiInputSource_Nav; 5160 } 5161 5162 return ret; 4752 ImGuiContext& g = *GImGui; 4753 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 4754 } 4755 4756 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) 4757 { 4758 ImGuiContext& g = *GImGui; 4759 ImGuiWindow* parent_window = g.CurrentWindow; 4760 4761 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; 4762 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag 4763 4764 // Size 4765 const ImVec2 content_avail = GetContentRegionAvail(); 4766 ImVec2 size = ImFloor(size_arg); 4767 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); 4768 if (size.x <= 0.0f) 4769 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) 4770 if (size.y <= 0.0f) 4771 size.y = ImMax(content_avail.y + size.y, 4.0f); 4772 SetNextWindowSize(size); 4773 4774 // 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. 4775 char title[256]; 4776 if (name) 4777 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id); 4778 else 4779 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); 4780 4781 const float backup_border_size = g.Style.ChildBorderSize; 4782 if (!border) 4783 g.Style.ChildBorderSize = 0.0f; 4784 bool ret = Begin(title, NULL, flags); 4785 g.Style.ChildBorderSize = backup_border_size; 4786 4787 ImGuiWindow* child_window = g.CurrentWindow; 4788 child_window->ChildId = id; 4789 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises; 4790 4791 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. 4792 // While this is not really documented/defined, it seems that the expected thing to do. 4793 if (child_window->BeginCount == 1) 4794 parent_window->DC.CursorPos = child_window->Pos; 4795 4796 // Process navigation-in immediately so NavInit can run on first frame 4797 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) 4798 { 4799 FocusWindow(child_window); 4800 NavInitWindow(child_window, false); 4801 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item 4802 g.ActiveIdSource = ImGuiInputSource_Nav; 4803 } 4804 return ret; 5163 4805 } 5164 4806 5165 4807 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 5166 4808 { 5167 ImGuiWindow* window = GetCurrentWindow();5168 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);4809 ImGuiWindow* window = GetCurrentWindow(); 4810 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); 5169 4811 } 5170 4812 5171 4813 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 5172 4814 { 5173 IM_ASSERT(id != 0);5174 return BeginChildEx(NULL, id, size_arg, border, extra_flags);4815 IM_ASSERT(id != 0); 4816 return BeginChildEx(NULL, id, size_arg, border, extra_flags); 5175 4817 } 5176 4818 5177 4819 void ImGui::EndChild() 5178 4820 { 5179 ImGuiContext& g = *GImGui; 5180 ImGuiWindow* window = g.CurrentWindow; 5181 5182 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss 5183 if (window->BeginCount > 1) 5184 { 5185 End(); 5186 } 5187 else 5188 { 5189 // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. 5190 ImVec2 sz = GetWindowSize(); 5191 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f 5192 sz.x = ImMax(4.0f, sz.x); 5193 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) 5194 sz.y = ImMax(4.0f, sz.y); 5195 End(); 5196 5197 ImGuiWindow* parent_window = g.CurrentWindow; 5198 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); 5199 ItemSize(sz); 5200 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) 5201 { 5202 ItemAdd(bb, window->ChildId); 5203 RenderNavHighlight(bb, window->ChildId); 5204 5205 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child 5206 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) 5207 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); 5208 } 5209 else 5210 { 5211 // Not navigable into 5212 ItemAdd(bb, 0); 5213 } 5214 } 4821 ImGuiContext& g = *GImGui; 4822 ImGuiWindow* window = g.CurrentWindow; 4823 4824 IM_ASSERT(g.WithinEndChild == false); 4825 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls 4826 4827 g.WithinEndChild = true; 4828 if (window->BeginCount > 1) 4829 { 4830 End(); 4831 } 4832 else 4833 { 4834 ImVec2 sz = window->Size; 4835 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f 4836 sz.x = ImMax(4.0f, sz.x); 4837 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) 4838 sz.y = ImMax(4.0f, sz.y); 4839 End(); 4840 4841 ImGuiWindow* parent_window = g.CurrentWindow; 4842 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); 4843 ItemSize(sz); 4844 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) 4845 { 4846 ItemAdd(bb, window->ChildId); 4847 RenderNavHighlight(bb, window->ChildId); 4848 4849 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child 4850 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) 4851 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); 4852 } 4853 else 4854 { 4855 // Not navigable into 4856 ItemAdd(bb, 0); 4857 } 4858 } 4859 g.WithinEndChild = false; 5215 4860 } 5216 4861 … … 5218 4863 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) 5219 4864 { 5220 ImGuiContext& g = *GImGui; 5221 const ImGuiStyle& style = g.Style; 5222 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); 5223 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); 5224 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); 5225 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); 5226 return BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); 4865 ImGuiContext& g = *GImGui; 4866 const ImGuiStyle& style = g.Style; 4867 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); 4868 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); 4869 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); 4870 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); 4871 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); 4872 PopStyleVar(3); 4873 PopStyleColor(); 4874 return ret; 5227 4875 } 5228 4876 5229 4877 void ImGui::EndChildFrame() 5230 4878 { 5231 EndChild(); 5232 PopStyleVar(3); 5233 PopStyleColor(); 5234 } 5235 5236 // Save and compare stack sizes on Begin()/End() to detect usage errors 5237 static void CheckStacksSize(ImGuiWindow* window, bool write) 5238 { 5239 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) 5240 ImGuiContext& g = *GImGui; 5241 int* p_backup = &window->DC.StackSizesBackup[0]; 5242 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() 5243 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() 5244 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++; }// Too few or too many EndMenu()/EndPopup() 5245 // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. 5246 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() 5247 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() 5248 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() 5249 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); 5250 } 5251 5252 enum ImGuiPopupPositionPolicy 5253 { 5254 ImGuiPopupPositionPolicy_Default, 5255 ImGuiPopupPositionPolicy_ComboBox 4879 EndChild(); 4880 } 4881 4882 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) 4883 { 4884 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); 4885 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); 4886 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); 4887 } 4888 4889 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) 4890 { 4891 ImGuiContext& g = *GImGui; 4892 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); 4893 } 4894 4895 ImGuiWindow* ImGui::FindWindowByName(const char* name) 4896 { 4897 ImGuiID id = ImHashStr(name); 4898 return FindWindowByID(id); 4899 } 4900 4901 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) 4902 { 4903 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y)); 4904 if (settings->Size.x > 0 && settings->Size.y > 0) 4905 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y)); 4906 window->Collapsed = settings->Collapsed; 4907 } 4908 4909 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) 4910 { 4911 ImGuiContext& g = *GImGui; 4912 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); 4913 4914 // Create window the first time 4915 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); 4916 window->Flags = flags; 4917 g.WindowsById.SetVoidPtr(window->ID, window); 4918 4919 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. 4920 window->Pos = ImVec2(60, 60); 4921 4922 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. 4923 if (!(flags & ImGuiWindowFlags_NoSavedSettings)) 4924 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) 4925 { 4926 // Retrieve settings from .ini file 4927 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 4928 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); 4929 ApplyWindowSettings(window, settings); 4930 } 4931 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values 4932 4933 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) 4934 { 4935 window->AutoFitFramesX = window->AutoFitFramesY = 2; 4936 window->AutoFitOnlyGrows = false; 4937 } 4938 else 4939 { 4940 if (window->Size.x <= 0.0f) 4941 window->AutoFitFramesX = 2; 4942 if (window->Size.y <= 0.0f) 4943 window->AutoFitFramesY = 2; 4944 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); 4945 } 4946 4947 g.WindowsFocusOrder.push_back(window); 4948 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) 4949 g.Windows.push_front(window); // Quite slow but rare and only once 4950 else 4951 g.Windows.push_back(window); 4952 return window; 4953 } 4954 4955 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) 4956 { 4957 ImGuiContext& g = *GImGui; 4958 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) 4959 { 4960 // Using -1,-1 on either X/Y axis to preserve the current size. 4961 ImRect cr = g.NextWindowData.SizeConstraintRect; 4962 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; 4963 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; 4964 if (g.NextWindowData.SizeCallback) 4965 { 4966 ImGuiSizeCallbackData data; 4967 data.UserData = g.NextWindowData.SizeCallbackUserData; 4968 data.Pos = window->Pos; 4969 data.CurrentSize = window->SizeFull; 4970 data.DesiredSize = new_size; 4971 g.NextWindowData.SizeCallback(&data); 4972 new_size = data.DesiredSize; 4973 } 4974 new_size.x = IM_FLOOR(new_size.x); 4975 new_size.y = IM_FLOOR(new_size.y); 4976 } 4977 4978 // Minimum size 4979 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) 4980 { 4981 ImGuiWindow* window_for_height = window; 4982 new_size = ImMax(new_size, g.Style.WindowMinSize); 4983 new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows 4984 } 4985 return new_size; 4986 } 4987 4988 static ImVec2 CalcWindowContentSize(ImGuiWindow* window) 4989 { 4990 if (window->Collapsed) 4991 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 4992 return window->ContentSize; 4993 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) 4994 return window->ContentSize; 4995 4996 ImVec2 sz; 4997 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); 4998 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); 4999 return sz; 5000 } 5001 5002 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) 5003 { 5004 ImGuiContext& g = *GImGui; 5005 ImGuiStyle& style = g.Style; 5006 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); 5007 ImVec2 size_pad = window->WindowPadding * 2.0f; 5008 ImVec2 size_desired = size_contents + size_pad + size_decorations; 5009 if (window->Flags & ImGuiWindowFlags_Tooltip) 5010 { 5011 // Tooltip always resize 5012 return size_desired; 5013 } 5014 else 5015 { 5016 // Maximum window size is determined by the viewport size or monitor size 5017 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; 5018 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; 5019 ImVec2 size_min = style.WindowMinSize; 5020 if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) 5021 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); 5022 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); 5023 5024 // When the window cannot fit all contents (either because of constraints, either because screen is too small), 5025 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. 5026 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5027 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); 5028 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); 5029 if (will_have_scrollbar_x) 5030 size_auto_fit.y += style.ScrollbarSize; 5031 if (will_have_scrollbar_y) 5032 size_auto_fit.x += style.ScrollbarSize; 5033 return size_auto_fit; 5034 } 5035 } 5036 5037 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window) 5038 { 5039 ImVec2 size_contents = CalcWindowContentSize(window); 5040 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents); 5041 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5042 return size_final; 5043 } 5044 5045 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) 5046 { 5047 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) 5048 return ImGuiCol_PopupBg; 5049 if (flags & ImGuiWindowFlags_ChildWindow) 5050 return ImGuiCol_ChildBg; 5051 return ImGuiCol_WindowBg; 5052 } 5053 5054 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) 5055 { 5056 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left 5057 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right 5058 ImVec2 size_expected = pos_max - pos_min; 5059 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected); 5060 *out_pos = pos_min; 5061 if (corner_norm.x == 0.0f) 5062 out_pos->x -= (size_constrained.x - size_expected.x); 5063 if (corner_norm.y == 0.0f) 5064 out_pos->y -= (size_constrained.y - size_expected.y); 5065 *out_size = size_constrained; 5066 } 5067 5068 struct ImGuiResizeGripDef 5069 { 5070 ImVec2 CornerPosN; 5071 ImVec2 InnerDir; 5072 int AngleMin12, AngleMax12; 5256 5073 }; 5257 5074 5258 static ImRect FindAllowedExtentRectForWindow(ImGuiWindow*) 5259 { 5260 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; 5261 ImRect r_screen = GetViewportRect(); 5262 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); 5263 return r_screen; 5264 } 5265 5266 // 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.) 5267 // 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. 5268 static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) 5269 { 5270 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); 5271 //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); 5272 //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); 5273 5274 // Combo Box policy (we want a connecting edge) 5275 if (policy == ImGuiPopupPositionPolicy_ComboBox) 5276 { 5277 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; 5278 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 5279 { 5280 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 5281 if (n != -1 && dir == *last_dir) // Already tried this direction? 5282 continue; 5283 ImVec2 pos; 5284 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) 5285 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right 5286 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left 5287 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left 5288 if (!r_outer.Contains(ImRect(pos, pos + size))) 5289 continue; 5290 *last_dir = dir; 5291 return pos; 5292 } 5293 } 5294 5295 // Default popup policy 5296 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; 5297 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 5298 { 5299 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 5300 if (n != -1 && dir == *last_dir) // Already tried this direction? 5301 continue; 5302 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); 5303 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); 5304 if (avail_w < size.x || avail_h < size.y) 5305 continue; 5306 ImVec2 pos; 5307 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; 5308 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; 5309 *last_dir = dir; 5310 return pos; 5311 } 5312 5313 // Fallback, try to keep within display 5314 *last_dir = ImGuiDir_None; 5315 ImVec2 pos = ref_pos; 5316 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); 5317 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); 5318 return pos; 5319 } 5320 5321 static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) 5322 { 5323 ImGuiContext& g = *GImGui; 5324 5325 ImRect r_outer = FindAllowedExtentRectForWindow(window); 5326 if (window->Flags & ImGuiWindowFlags_ChildMenu) 5327 { 5328 // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. 5329 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. 5330 IM_ASSERT(g.CurrentWindow == window); 5331 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; 5332 float horizontal_overlap = g.Style.ItemSpacing.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). 5333 ImRect r_avoid; 5334 if (parent_window->DC.MenuBarAppending) 5335 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); 5336 else 5337 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); 5338 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 5339 } 5340 if (window->Flags & ImGuiWindowFlags_Popup) 5341 { 5342 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); 5343 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 5344 } 5345 if (window->Flags & ImGuiWindowFlags_Tooltip) 5346 { 5347 // Position tooltip (always follows mouse) 5348 float sc = g.Style.MouseCursorScale; 5349 ImVec2 ref_pos = NavCalcPreferredRefPos(); 5350 ImRect r_avoid; 5351 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) 5352 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); 5353 else 5354 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. 5355 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 5356 if (window->AutoPosLastDirection == ImGuiDir_None) 5357 pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. 5358 return pos; 5359 } 5360 IM_ASSERT(0); 5361 return window->Pos; 5362 } 5363 5364 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) 5365 { 5366 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); 5367 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); 5368 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); 5369 } 5370 5371 ImGuiWindow* ImGui::FindWindowByName(const char* name) 5372 { 5373 ImGuiContext& g = *GImGui; 5374 ImGuiID id = ImHash(name, 0); 5375 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); 5376 } 5377 5378 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) 5379 { 5380 ImGuiContext& g = *GImGui; 5381 5382 // Create window the first time 5383 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); 5384 window->Flags = flags; 5385 g.WindowsById.SetVoidPtr(window->ID, window); 5386 5387 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. 5388 window->Pos = ImVec2(60, 60); 5389 5390 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. 5391 if (!(flags & ImGuiWindowFlags_NoSavedSettings)) 5392 { 5393 // Retrieve settings from .ini file 5394 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) 5395 { 5396 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); 5397 window->Pos = ImFloor(settings->Pos); 5398 window->Collapsed = settings->Collapsed; 5399 if (ImLengthSqr(settings->Size) > 0.00001f) 5400 size = ImFloor(settings->Size); 5401 } 5402 } 5403 window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; 5404 5405 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) 5406 { 5407 window->AutoFitFramesX = window->AutoFitFramesY = 2; 5408 window->AutoFitOnlyGrows = false; 5409 } 5410 else 5411 { 5412 if (window->Size.x <= 0.0f) 5413 window->AutoFitFramesX = 2; 5414 if (window->Size.y <= 0.0f) 5415 window->AutoFitFramesY = 2; 5416 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); 5417 } 5418 5419 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) 5420 g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once 5421 else 5422 g.Windows.push_back(window); 5423 return window; 5424 } 5425 5426 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) 5427 { 5428 ImGuiContext& g = *GImGui; 5429 if (g.NextWindowData.SizeConstraintCond != 0) 5430 { 5431 // Using -1,-1 on either X/Y axis to preserve the current size. 5432 ImRect cr = g.NextWindowData.SizeConstraintRect; 5433 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; 5434 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; 5435 if (g.NextWindowData.SizeCallback) 5436 { 5437 ImGuiSizeCallbackData data; 5438 data.UserData = g.NextWindowData.SizeCallbackUserData; 5439 data.Pos = window->Pos; 5440 data.CurrentSize = window->SizeFull; 5441 data.DesiredSize = new_size; 5442 g.NextWindowData.SizeCallback(&data); 5443 new_size = data.DesiredSize; 5444 } 5445 } 5446 5447 // Minimum size 5448 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) 5449 { 5450 new_size = ImMax(new_size, g.Style.WindowMinSize); 5451 new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows 5452 } 5453 return new_size; 5454 } 5455 5456 static ImVec2 CalcSizeContents(ImGuiWindow* window) 5457 { 5458 ImVec2 sz; 5459 sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x)); 5460 sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y)); 5461 return sz + window->WindowPadding; 5462 } 5463 5464 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) 5465 { 5466 ImGuiContext& g = *GImGui; 5467 ImGuiStyle& style = g.Style; 5468 if (window->Flags & ImGuiWindowFlags_Tooltip) 5469 { 5470 // Tooltip always resize 5471 return size_contents; 5472 } 5473 else 5474 { 5475 // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. 5476 ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding * 2.0f)); 5477 ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); 5478 if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) 5479 size_auto_fit.y += style.ScrollbarSize; 5480 if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) 5481 size_auto_fit.x += style.ScrollbarSize; 5482 return size_auto_fit; 5483 } 5484 } 5485 5486 static float GetScrollMaxX(ImGuiWindow* window) 5487 { 5488 return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); 5489 } 5490 5491 static float GetScrollMaxY(ImGuiWindow* window) 5492 { 5493 return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); 5494 } 5495 5496 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) 5497 { 5498 ImVec2 scroll = window->Scroll; 5499 float cr_x = window->ScrollTargetCenterRatio.x; 5500 float cr_y = window->ScrollTargetCenterRatio.y; 5501 if (window->ScrollTarget.x < FLT_MAX) 5502 scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x); 5503 if (window->ScrollTarget.y < FLT_MAX) 5504 scroll.y = window->ScrollTarget.y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y); 5505 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); 5506 if (!window->Collapsed && !window->SkipItems) 5507 { 5508 scroll.x = ImMin(scroll.x, GetScrollMaxX(window)); 5509 scroll.y = ImMin(scroll.y, GetScrollMaxY(window)); 5510 } 5511 return scroll; 5512 } 5513 5514 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) 5515 { 5516 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) 5517 return ImGuiCol_PopupBg; 5518 if (flags & ImGuiWindowFlags_ChildWindow) 5519 return ImGuiCol_ChildBg; 5520 return ImGuiCol_WindowBg; 5521 } 5522 5523 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) 5524 { 5525 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left 5526 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right 5527 ImVec2 size_expected = pos_max - pos_min; 5528 ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected); 5529 *out_pos = pos_min; 5530 if (corner_norm.x == 0.0f) 5531 out_pos->x -= (size_constrained.x - size_expected.x); 5532 if (corner_norm.y == 0.0f) 5533 out_pos->y -= (size_constrained.y - size_expected.y); 5534 *out_size = size_constrained; 5535 } 5536 5537 struct ImGuiResizeGripDef 5538 { 5539 ImVec2 CornerPos; 5540 ImVec2 InnerDir; 5541 int AngleMin12, AngleMax12; 5075 static const ImGuiResizeGripDef resize_grip_def[4] = 5076 { 5077 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right 5078 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left 5079 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) 5080 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused) 5542 5081 }; 5543 5082 5544 const ImGuiResizeGripDef resize_grip_def[4] = 5545 { 5546 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right 5547 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left 5548 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left 5549 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right 5083 struct ImGuiResizeBorderDef 5084 { 5085 ImVec2 InnerDir; 5086 ImVec2 CornerPosN1, CornerPosN2; 5087 float OuterAngle; 5550 5088 }; 5551 5089 5552 static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) 5553 { 5554 ImRect rect = window->Rect(); 5555 if (thickness == 0.0f) rect.Max -= ImVec2(1, 1); 5556 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); 5557 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); 5558 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); 5559 if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); 5560 IM_ASSERT(0); 5561 return ImRect(); 5090 static const ImGuiResizeBorderDef resize_border_def[4] = 5091 { 5092 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top 5093 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right 5094 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom 5095 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left 5096 }; 5097 5098 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) 5099 { 5100 ImRect rect = window->Rect(); 5101 if (thickness == 0.0f) rect.Max -= ImVec2(1, 1); 5102 if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } // Top 5103 if (border_n == 1) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } // Right 5104 if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } // Bottom 5105 if (border_n == 3) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } // Left 5106 IM_ASSERT(0); 5107 return ImRect(); 5108 } 5109 5110 // 0..3: corners (Lower-right, Lower-left, Unused, Unused) 5111 // 4..7: borders (Top, Right, Bottom, Left) 5112 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) 5113 { 5114 IM_ASSERT(n >= 0 && n <= 7); 5115 ImGuiID id = window->ID; 5116 id = ImHashStr("#RESIZE", 0, id); 5117 id = ImHashData(&n, sizeof(int), id); 5118 return id; 5562 5119 } 5563 5120 5564 5121 // Handle resize for: Resize Grips, Borders, Gamepad 5565 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) 5566 { 5567 ImGuiContext& g = *GImGui; 5568 ImGuiWindowFlags flags = window->Flags; 5569 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5570 return; 5571 5572 const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0; 5573 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); 5574 const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); 5575 5576 ImVec2 pos_target(FLT_MAX, FLT_MAX); 5577 ImVec2 size_target(FLT_MAX, FLT_MAX); 5578 5579 // Manual resize grips 5580 PushID("#RESIZE"); 5581 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5582 { 5583 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5584 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); 5585 5586 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window 5587 ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); 5588 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); 5589 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); 5590 bool hovered, held; 5591 ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); 5592 if (hovered || held) 5593 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 5594 5595 if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) 5596 { 5597 // Manual auto-fit when double-clicking 5598 size_target = CalcSizeAfterConstraint(window, size_auto_fit); 5599 ClearActiveID(); 5600 } 5601 else if (held) 5602 { 5603 // Resize from any of the four corners 5604 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position 5605 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip 5606 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); 5607 } 5608 if (resize_grip_n == 0 || held || hovered) 5609 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); 5610 } 5611 for (int border_n = 0; border_n < resize_border_count; border_n++) 5612 { 5613 const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. 5614 const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise 5615 bool hovered, held; 5616 ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); 5617 ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); 5618 if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) 5619 { 5620 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; 5621 if (held) *border_held = border_n; 5622 } 5623 if (held) 5624 { 5625 ImVec2 border_target = window->Pos; 5626 ImVec2 border_posn; 5627 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } 5628 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } 5629 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } 5630 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } 5631 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); 5632 } 5633 } 5634 PopID(); 5635 5636 // Navigation resize (keyboard/gamepad) 5637 if (g.NavWindowingTarget == window) 5638 { 5639 ImVec2 nav_resize_delta; 5640 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) 5641 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 5642 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 5643 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); 5644 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) 5645 { 5646 const float NAV_RESIZE_SPEED = 600.0f; 5647 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); 5648 g.NavWindowingToggleLayer = false; 5649 g.NavDisableMouseHover = true; 5650 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); 5651 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. 5652 size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); 5653 } 5654 } 5655 5656 // Apply back modified position/size to window 5657 if (size_target.x != FLT_MAX) 5658 { 5659 window->SizeFull = size_target; 5660 MarkIniSettingsDirty(window); 5661 } 5662 if (pos_target.x != FLT_MAX) 5663 { 5664 window->Pos = ImFloor(pos_target); 5665 MarkIniSettingsDirty(window); 5666 } 5667 5668 window->Size = window->SizeFull; 5669 } 5670 5671 // Push a new ImGui window to add widgets to. 5122 // Return true when using auto-fit (double click on resize grip) 5123 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) 5124 { 5125 ImGuiContext& g = *GImGui; 5126 ImGuiWindowFlags flags = window->Flags; 5127 5128 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5129 return false; 5130 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. 5131 return false; 5132 5133 bool ret_auto_fit = false; 5134 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; 5135 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 5136 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); 5137 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; 5138 5139 ImVec2 pos_target(FLT_MAX, FLT_MAX); 5140 ImVec2 size_target(FLT_MAX, FLT_MAX); 5141 5142 // Resize grips and borders are on layer 1 5143 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5144 5145 // Manual resize grips 5146 PushID("#RESIZE"); 5147 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5148 { 5149 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5150 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); 5151 5152 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window 5153 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); 5154 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); 5155 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); 5156 bool hovered, held; 5157 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); 5158 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); 5159 if (hovered || held) 5160 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 5161 5162 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) 5163 { 5164 // Manual auto-fit when double-clicking 5165 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5166 ret_auto_fit = true; 5167 ClearActiveID(); 5168 } 5169 else if (held) 5170 { 5171 // Resize from any of the four corners 5172 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position 5173 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip 5174 ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); 5175 ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); 5176 corner_target = ImClamp(corner_target, clamp_min, clamp_max); 5177 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); 5178 } 5179 if (resize_grip_n == 0 || held || hovered) 5180 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); 5181 } 5182 for (int border_n = 0; border_n < resize_border_count; border_n++) 5183 { 5184 bool hovered, held; 5185 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); 5186 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); 5187 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); 5188 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) 5189 { 5190 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; 5191 if (held) 5192 *border_held = border_n; 5193 } 5194 if (held) 5195 { 5196 ImVec2 border_target = window->Pos; 5197 ImVec2 border_posn; 5198 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top 5199 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right 5200 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom 5201 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left 5202 ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX); 5203 ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX); 5204 border_target = ImClamp(border_target, clamp_min, clamp_max); 5205 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); 5206 } 5207 } 5208 PopID(); 5209 5210 // Restore nav layer 5211 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5212 5213 // Navigation resize (keyboard/gamepad) 5214 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) 5215 { 5216 ImVec2 nav_resize_delta; 5217 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) 5218 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 5219 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 5220 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); 5221 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) 5222 { 5223 const float NAV_RESIZE_SPEED = 600.0f; 5224 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); 5225 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); 5226 g.NavWindowingToggleLayer = false; 5227 g.NavDisableMouseHover = true; 5228 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); 5229 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. 5230 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); 5231 } 5232 } 5233 5234 // Apply back modified position/size to window 5235 if (size_target.x != FLT_MAX) 5236 { 5237 window->SizeFull = size_target; 5238 MarkIniSettingsDirty(window); 5239 } 5240 if (pos_target.x != FLT_MAX) 5241 { 5242 window->Pos = ImFloor(pos_target); 5243 MarkIniSettingsDirty(window); 5244 } 5245 5246 window->Size = window->SizeFull; 5247 return ret_auto_fit; 5248 } 5249 5250 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect) 5251 { 5252 ImGuiContext& g = *GImGui; 5253 ImVec2 size_for_clamping = window->Size; 5254 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5255 size_for_clamping.y = window->TitleBarHeight(); 5256 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); 5257 } 5258 5259 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) 5260 { 5261 ImGuiContext& g = *GImGui; 5262 float rounding = window->WindowRounding; 5263 float border_size = window->WindowBorderSize; 5264 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) 5265 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 5266 5267 int border_held = window->ResizeBorderHeld; 5268 if (border_held != -1) 5269 { 5270 const ImGuiResizeBorderDef& def = resize_border_def[border_held]; 5271 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); 5272 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); 5273 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); 5274 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual 5275 } 5276 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5277 { 5278 float y = window->Pos.y + window->TitleBarHeight() - 1; 5279 window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); 5280 } 5281 } 5282 5283 // Draw background and borders 5284 // Draw and handle scrollbars 5285 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) 5286 { 5287 ImGuiContext& g = *GImGui; 5288 ImGuiStyle& style = g.Style; 5289 ImGuiWindowFlags flags = window->Flags; 5290 5291 // Ensure that ScrollBar doesn't read last frame's SkipItems 5292 IM_ASSERT(window->BeginCount == 0); 5293 window->SkipItems = false; 5294 5295 // Draw window + handle manual resize 5296 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. 5297 const float window_rounding = window->WindowRounding; 5298 const float window_border_size = window->WindowBorderSize; 5299 if (window->Collapsed) 5300 { 5301 // Title bar only 5302 float backup_border_size = style.FrameBorderSize; 5303 g.Style.FrameBorderSize = window->WindowBorderSize; 5304 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); 5305 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); 5306 g.Style.FrameBorderSize = backup_border_size; 5307 } 5308 else 5309 { 5310 // Window background 5311 if (!(flags & ImGuiWindowFlags_NoBackground)) 5312 { 5313 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); 5314 bool override_alpha = false; 5315 float alpha = 1.0f; 5316 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) 5317 { 5318 alpha = g.NextWindowData.BgAlphaVal; 5319 override_alpha = true; 5320 } 5321 if (override_alpha) 5322 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); 5323 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); 5324 } 5325 5326 // Title bar 5327 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 5328 { 5329 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); 5330 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); 5331 } 5332 5333 // Menu bar 5334 if (flags & ImGuiWindowFlags_MenuBar) 5335 { 5336 ImRect menu_bar_rect = window->MenuBarRect(); 5337 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. 5338 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, ImDrawCornerFlags_Top); 5339 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) 5340 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); 5341 } 5342 5343 // Scrollbars 5344 if (window->ScrollbarX) 5345 Scrollbar(ImGuiAxis_X); 5346 if (window->ScrollbarY) 5347 Scrollbar(ImGuiAxis_Y); 5348 5349 // Render resize grips (after their input handling so we don't have a frame of latency) 5350 if (!(flags & ImGuiWindowFlags_NoResize)) 5351 { 5352 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5353 { 5354 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5355 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); 5356 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))); 5357 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))); 5358 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); 5359 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); 5360 } 5361 } 5362 5363 // Borders 5364 RenderWindowOuterBorders(window); 5365 } 5366 } 5367 5368 // Render title text, collapse button, close button 5369 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) 5370 { 5371 ImGuiContext& g = *GImGui; 5372 ImGuiStyle& style = g.Style; 5373 ImGuiWindowFlags flags = window->Flags; 5374 5375 const bool has_close_button = (p_open != NULL); 5376 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); 5377 5378 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) 5379 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; 5380 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; 5381 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5382 5383 // Layout buttons 5384 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. 5385 float pad_l = style.FramePadding.x; 5386 float pad_r = style.FramePadding.x; 5387 float button_sz = g.FontSize; 5388 ImVec2 close_button_pos; 5389 ImVec2 collapse_button_pos; 5390 if (has_close_button) 5391 { 5392 pad_r += button_sz; 5393 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5394 } 5395 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) 5396 { 5397 pad_r += button_sz; 5398 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5399 } 5400 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) 5401 { 5402 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); 5403 pad_l += button_sz; 5404 } 5405 5406 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) 5407 if (has_collapse_button) 5408 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) 5409 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function 5410 5411 // Close button 5412 if (has_close_button) 5413 if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) 5414 *p_open = false; 5415 5416 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5417 window->DC.ItemFlags = item_flags_backup; 5418 5419 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) 5420 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. 5421 const char* UNSAVED_DOCUMENT_MARKER = "*"; 5422 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; 5423 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); 5424 5425 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, 5426 // while uncentered title text will still reach edges correct. 5427 if (pad_l > style.FramePadding.x) 5428 pad_l += g.Style.ItemInnerSpacing.x; 5429 if (pad_r > style.FramePadding.x) 5430 pad_r += g.Style.ItemInnerSpacing.x; 5431 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) 5432 { 5433 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center 5434 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); 5435 pad_l = ImMax(pad_l, pad_extend * centerness); 5436 pad_r = ImMax(pad_r, pad_extend * centerness); 5437 } 5438 5439 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); 5440 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); 5441 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] 5442 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); 5443 if (flags & ImGuiWindowFlags_UnsavedDocument) 5444 { 5445 ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); 5446 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); 5447 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); 5448 } 5449 } 5450 5451 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) 5452 { 5453 window->ParentWindow = parent_window; 5454 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; 5455 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) 5456 window->RootWindow = parent_window->RootWindow; 5457 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) 5458 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; 5459 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) 5460 { 5461 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); 5462 window->RootWindowForNav = window->RootWindowForNav->ParentWindow; 5463 } 5464 } 5465 5466 // Push a new Dear ImGui window to add widgets to. 5672 5467 // - 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. 5673 5468 // - Begin/End can be called multiple times during the frame with the same window name to append content. … … 5678 5473 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) 5679 5474 { 5680 ImGuiContext& g = *GImGui; 5681 const ImGuiStyle& style = g.Style; 5682 IM_ASSERT(name != NULL); // Window name required 5683 IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() 5684 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet 5685 5686 // Find or create 5687 ImGuiWindow* window = FindWindowByName(name); 5688 const bool window_just_created = (window == NULL); 5689 if (window_just_created) 5690 { 5691 ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. 5692 window = CreateNewWindow(name, size_on_first_use, flags); 5693 } 5694 5695 // Automatically disable manual moving/resizing when NoInputs is set 5696 if (flags & ImGuiWindowFlags_NoInputs) 5697 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 5698 5699 if (flags & ImGuiWindowFlags_NavFlattened) 5700 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); 5701 5702 const int current_frame = g.FrameCount; 5703 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); 5704 if (first_begin_of_the_frame) 5705 window->Flags = (ImGuiWindowFlags)flags; 5706 else 5707 flags = window->Flags; 5708 5709 // Update the Appearing flag 5710 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on 5711 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0); 5712 if (flags & ImGuiWindowFlags_Popup) 5713 { 5714 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; 5715 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed 5716 window_just_activated_by_user |= (window != popup_ref.Window); 5717 } 5718 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); 5719 window->CloseButton = (p_open != NULL); 5720 if (window->Appearing) 5721 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); 5722 5723 // 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 5724 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); 5725 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; 5726 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); 5727 5728 // Add to stack 5729 g.CurrentWindowStack.push_back(window); 5730 SetCurrentWindow(window); 5731 CheckStacksSize(window, true); 5732 if (flags & ImGuiWindowFlags_Popup) 5733 { 5734 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; 5735 popup_ref.Window = window; 5736 g.CurrentPopupStack.push_back(popup_ref); 5737 window->PopupId = popup_ref.PopupId; 5738 } 5739 5740 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) 5741 window->NavLastIds[0] = 0; 5742 5743 // Process SetNextWindow***() calls 5744 bool window_pos_set_by_api = false; 5745 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; 5746 if (g.NextWindowData.PosCond) 5747 { 5748 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; 5749 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) 5750 { 5751 // May be processed on the next frame if this is our first frame and we are measuring size 5752 // FIXME: Look into removing the branch so everything can go through this same code path for consistency. 5753 window->SetWindowPosVal = g.NextWindowData.PosVal; 5754 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; 5755 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 5756 } 5757 else 5758 { 5759 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); 5760 } 5761 } 5762 if (g.NextWindowData.SizeCond) 5763 { 5764 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); 5765 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); 5766 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); 5767 } 5768 if (g.NextWindowData.ContentSizeCond) 5769 { 5770 // Adjust passed "client size" to become a "window size" 5771 window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal; 5772 if (window->SizeContentsExplicit.y != 0.0f) 5773 window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight(); 5774 } 5775 else if (first_begin_of_the_frame) 5776 { 5777 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); 5778 } 5779 if (g.NextWindowData.CollapsedCond) 5780 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); 5781 if (g.NextWindowData.FocusCond) 5782 FocusWindow(window); 5783 if (window->Appearing) 5784 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); 5785 5786 // When reusing window again multiple times a frame, just append content (don't need to setup again) 5787 if (first_begin_of_the_frame) 5788 { 5789 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) 5790 5791 // Initialize 5792 window->ParentWindow = parent_window; 5793 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = window->RootWindowForNav = window; 5794 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !window_is_child_tooltip) 5795 window->RootWindow = parent_window->RootWindow; 5796 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) 5797 window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = parent_window->RootWindowForTitleBarHighlight; // Same value in master branch, will differ for docking 5798 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) 5799 window->RootWindowForNav = window->RootWindowForNav->ParentWindow; 5800 5801 window->Active = true; 5802 window->BeginOrderWithinParent = 0; 5803 window->BeginOrderWithinContext = g.WindowsActiveCount++; 5804 window->BeginCount = 0; 5805 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); 5806 window->LastFrameActive = current_frame; 5807 window->IDStack.resize(1); 5808 5809 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS 5810 5811 // Update contents size from last frame for auto-fitting (or use explicit size) 5812 window->SizeContents = CalcSizeContents(window); 5813 if (window->HiddenFrames > 0) 5814 window->HiddenFrames--; 5815 5816 // Hide new windows for one frame until they calculate their size 5817 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) 5818 window->HiddenFrames = 1; 5819 5820 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) 5821 // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. 5822 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) 5823 { 5824 window->HiddenFrames = 1; 5825 if (flags & ImGuiWindowFlags_AlwaysAutoResize) 5826 { 5475 ImGuiContext& g = *GImGui; 5476 const ImGuiStyle& style = g.Style; 5477 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required 5478 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame() 5479 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet 5480 5481 // Find or create 5482 ImGuiWindow* window = FindWindowByName(name); 5483 const bool window_just_created = (window == NULL); 5484 if (window_just_created) 5485 window = CreateNewWindow(name, flags); 5486 5487 // Automatically disable manual moving/resizing when NoInputs is set 5488 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) 5489 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 5490 5491 if (flags & ImGuiWindowFlags_NavFlattened) 5492 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); 5493 5494 const int current_frame = g.FrameCount; 5495 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); 5496 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); 5497 5498 // Update the Appearing flag 5499 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on 5500 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); 5501 if (flags & ImGuiWindowFlags_Popup) 5502 { 5503 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5504 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed 5505 window_just_activated_by_user |= (window != popup_ref.Window); 5506 } 5507 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); 5508 if (window->Appearing) 5509 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); 5510 5511 // Update Flags, LastFrameActive, BeginOrderXXX fields 5512 if (first_begin_of_the_frame) 5513 { 5514 window->Flags = (ImGuiWindowFlags)flags; 5515 window->LastFrameActive = current_frame; 5516 window->LastTimeActive = (float)g.Time; 5517 window->BeginOrderWithinParent = 0; 5518 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); 5519 } 5520 else 5521 { 5522 flags = window->Flags; 5523 } 5524 5525 // 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 5526 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); 5527 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; 5528 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); 5529 5530 // We allow window memory to be compacted so recreate the base stack when needed. 5531 if (window->IDStack.Size == 0) 5532 window->IDStack.push_back(window->ID); 5533 5534 // Add to stack 5535 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() 5536 g.CurrentWindowStack.push_back(window); 5537 g.CurrentWindow = NULL; 5538 ErrorCheckBeginEndCompareStacksSize(window, true); 5539 if (flags & ImGuiWindowFlags_Popup) 5540 { 5541 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5542 popup_ref.Window = window; 5543 g.BeginPopupStack.push_back(popup_ref); 5544 window->PopupId = popup_ref.PopupId; 5545 } 5546 5547 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) 5548 window->NavLastIds[0] = 0; 5549 5550 // Update ->RootWindow and others pointers (before any possible call to FocusWindow) 5551 if (first_begin_of_the_frame) 5552 UpdateWindowParentAndRootLinks(window, flags, parent_window); 5553 5554 // Process SetNextWindow***() calls 5555 // (FIXME: Consider splitting the HasXXX flags into X/Y components 5556 bool window_pos_set_by_api = false; 5557 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; 5558 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) 5559 { 5560 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; 5561 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) 5562 { 5563 // May be processed on the next frame if this is our first frame and we are measuring size 5564 // FIXME: Look into removing the branch so everything can go through this same code path for consistency. 5565 window->SetWindowPosVal = g.NextWindowData.PosVal; 5566 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; 5567 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 5568 } 5569 else 5570 { 5571 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); 5572 } 5573 } 5574 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) 5575 { 5576 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); 5577 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); 5578 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); 5579 } 5580 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) 5581 { 5582 if (g.NextWindowData.ScrollVal.x >= 0.0f) 5583 { 5584 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x; 5585 window->ScrollTargetCenterRatio.x = 0.0f; 5586 } 5587 if (g.NextWindowData.ScrollVal.y >= 0.0f) 5588 { 5589 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y; 5590 window->ScrollTargetCenterRatio.y = 0.0f; 5591 } 5592 } 5593 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize) 5594 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; 5595 else if (first_begin_of_the_frame) 5596 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); 5597 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) 5598 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); 5599 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) 5600 FocusWindow(window); 5601 if (window->Appearing) 5602 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); 5603 5604 // When reusing window again multiple times a frame, just append content (don't need to setup again) 5605 if (first_begin_of_the_frame) 5606 { 5607 // Initialize 5608 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) 5609 window->Active = true; 5610 window->HasCloseButton = (p_open != NULL); 5611 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); 5612 window->IDStack.resize(1); 5613 window->DrawList->_ResetForNewFrame(); 5614 5615 // Restore buffer capacity when woken from a compacted state, to avoid 5616 if (window->MemoryCompacted) 5617 GcAwakeTransientWindowBuffers(window); 5618 5619 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). 5620 // 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. 5621 bool window_title_visible_elsewhere = false; 5622 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB 5623 window_title_visible_elsewhere = true; 5624 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) 5625 { 5626 size_t buf_len = (size_t)window->NameBufLen; 5627 window->Name = ImStrdupcpy(window->Name, &buf_len, name); 5628 window->NameBufLen = (int)buf_len; 5629 } 5630 5631 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS 5632 5633 // Update contents size from last frame for auto-fitting (or use explicit size) 5634 window->ContentSize = CalcWindowContentSize(window); 5635 if (window->HiddenFramesCanSkipItems > 0) 5636 window->HiddenFramesCanSkipItems--; 5637 if (window->HiddenFramesCannotSkipItems > 0) 5638 window->HiddenFramesCannotSkipItems--; 5639 5640 // Hide new windows for one frame until they calculate their size 5641 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) 5642 window->HiddenFramesCannotSkipItems = 1; 5643 5644 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) 5645 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. 5646 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) 5647 { 5648 window->HiddenFramesCannotSkipItems = 1; 5649 if (flags & ImGuiWindowFlags_AlwaysAutoResize) 5650 { 5651 if (!window_size_x_set_by_api) 5652 window->Size.x = window->SizeFull.x = 0.f; 5653 if (!window_size_y_set_by_api) 5654 window->Size.y = window->SizeFull.y = 0.f; 5655 window->ContentSize = ImVec2(0.f, 0.f); 5656 } 5657 } 5658 5659 // SELECT VIEWPORT 5660 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) 5661 SetCurrentWindow(window); 5662 5663 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) 5664 5665 if (flags & ImGuiWindowFlags_ChildWindow) 5666 window->WindowBorderSize = style.ChildBorderSize; 5667 else 5668 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; 5669 window->WindowPadding = style.WindowPadding; 5670 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) 5671 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); 5672 5673 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. 5674 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); 5675 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; 5676 5677 // Collapse window by double-clicking on title bar 5678 // 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 5679 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) 5680 { 5681 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. 5682 ImRect title_bar_rect = window->TitleBarRect(); 5683 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) 5684 window->WantCollapseToggle = true; 5685 if (window->WantCollapseToggle) 5686 { 5687 window->Collapsed = !window->Collapsed; 5688 MarkIniSettingsDirty(window); 5689 FocusWindow(window); 5690 } 5691 } 5692 else 5693 { 5694 window->Collapsed = false; 5695 } 5696 window->WantCollapseToggle = false; 5697 5698 // SIZE 5699 5700 // Calculate auto-fit size, handle automatic resize 5701 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize); 5702 bool use_current_size_for_scrollbar_x = window_just_created; 5703 bool use_current_size_for_scrollbar_y = window_just_created; 5704 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) 5705 { 5706 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. 5827 5707 if (!window_size_x_set_by_api) 5828 window->Size.x = window->SizeFull.x = 0.f; 5708 { 5709 window->SizeFull.x = size_auto_fit.x; 5710 use_current_size_for_scrollbar_x = true; 5711 } 5829 5712 if (!window_size_y_set_by_api) 5830 window->Size.y = window->SizeFull.y = 0.f; 5831 window->SizeContents = ImVec2(0.f, 0.f); 5832 } 5833 } 5834 5835 SetCurrentWindow(window); 5836 5837 // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) 5838 window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; 5839 window->WindowPadding = style.WindowPadding; 5840 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) 5841 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); 5842 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); 5843 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; 5844 5845 // Collapse window by double-clicking on title bar 5846 // 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 5847 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) 5848 { 5849 ImRect title_bar_rect = window->TitleBarRect(); 5850 if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])) 5851 { 5852 window->Collapsed = !window->Collapsed; 5853 MarkIniSettingsDirty(window); 5713 { 5714 window->SizeFull.y = size_auto_fit.y; 5715 use_current_size_for_scrollbar_y = true; 5716 } 5717 } 5718 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5719 { 5720 // Auto-fit may only grow window during the first few frames 5721 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. 5722 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) 5723 { 5724 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; 5725 use_current_size_for_scrollbar_x = true; 5726 } 5727 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) 5728 { 5729 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; 5730 use_current_size_for_scrollbar_y = true; 5731 } 5732 if (!window->Collapsed) 5733 MarkIniSettingsDirty(window); 5734 } 5735 5736 // Apply minimum/maximum window size constraints and final size 5737 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); 5738 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; 5739 5740 // Decoration size 5741 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 5742 5743 // POSITION 5744 5745 // Popup latch its initial position, will position itself when it appears next frame 5746 if (window_just_activated_by_user) 5747 { 5748 window->AutoPosLastDirection = ImGuiDir_None; 5749 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos() 5750 window->Pos = g.BeginPopupStack.back().OpenPopupPos; 5751 } 5752 5753 // Position child window 5754 if (flags & ImGuiWindowFlags_ChildWindow) 5755 { 5756 IM_ASSERT(parent_window && parent_window->Active); 5757 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; 5758 parent_window->DC.ChildWindows.push_back(window); 5759 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) 5760 window->Pos = parent_window->DC.CursorPos; 5761 } 5762 5763 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); 5764 if (window_pos_with_pivot) 5765 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) 5766 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) 5767 window->Pos = FindBestWindowPosForPopup(window); 5768 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) 5769 window->Pos = FindBestWindowPosForPopup(window); 5770 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) 5771 window->Pos = FindBestWindowPosForPopup(window); 5772 5773 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) 5774 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. 5775 ImRect viewport_rect(GetViewportRect()); 5776 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); 5777 ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding); 5778 5779 // Clamp position/size so window stays visible within its viewport or monitor 5780 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. 5781 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 5782 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) 5783 ClampWindowRect(window, visibility_rect); 5784 window->Pos = ImFloor(window->Pos); 5785 5786 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) 5787 // Large values tend to lead to variety of artifacts and are not recommended. 5788 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; 5789 5790 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. 5791 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5792 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f); 5793 5794 // Apply window focus (new and reactivated windows are moved to front) 5795 bool want_focus = false; 5796 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) 5797 { 5798 if (flags & ImGuiWindowFlags_Popup) 5799 want_focus = true; 5800 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) 5801 want_focus = true; 5802 } 5803 5804 // Handle manual resize: Resize Grips, Borders, Gamepad 5805 int border_held = -1; 5806 ImU32 resize_grip_col[4] = {}; 5807 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. 5808 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 5809 if (!window->Collapsed) 5810 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) 5811 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; 5812 window->ResizeBorderHeld = (signed char)border_held; 5813 5814 // SCROLLBAR VISIBILITY 5815 5816 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). 5817 if (!window->Collapsed) 5818 { 5819 // When reading the current size we need to read it after size constraints have been applied. 5820 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. 5821 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); 5822 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; 5823 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; 5824 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; 5825 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; 5826 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? 5827 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); 5828 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)); 5829 if (window->ScrollbarX && !window->ScrollbarY) 5830 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); 5831 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); 5832 } 5833 5834 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) 5835 // Update various regions. Variables they depends on should be set above in this function. 5836 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. 5837 5838 // Outer rectangle 5839 // Not affected by window border size. Used by: 5840 // - FindHoveredWindow() (w/ extra padding when border resize is enabled) 5841 // - Begin() initial clipping rect for drawing window background and borders. 5842 // - Begin() clipping whole child 5843 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; 5844 const ImRect outer_rect = window->Rect(); 5845 const ImRect title_bar_rect = window->TitleBarRect(); 5846 window->OuterRectClipped = outer_rect; 5847 window->OuterRectClipped.ClipWith(host_rect); 5848 5849 // Inner rectangle 5850 // Not affected by window border size. Used by: 5851 // - InnerClipRect 5852 // - ScrollToBringRectIntoView() 5853 // - NavUpdatePageUpPageDown() 5854 // - Scrollbar() 5855 window->InnerRect.Min.x = window->Pos.x; 5856 window->InnerRect.Min.y = window->Pos.y + decoration_up_height; 5857 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; 5858 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; 5859 5860 // Inner clipping rectangle. 5861 // Will extend a little bit outside the normal work region. 5862 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. 5863 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. 5864 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. 5865 // Affected by window/frame border size. Used by: 5866 // - Begin() initial clip rect 5867 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); 5868 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 5869 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); 5870 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 5871 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); 5872 window->InnerClipRect.ClipWithFull(host_rect); 5873 5874 // Default item width. Make it proportional to window size if window manually resizes 5875 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) 5876 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f); 5877 else 5878 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f); 5879 5880 // SCROLLING 5881 5882 // Lock down maximum scrolling 5883 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate 5884 // for right/bottom aligned items without creating a scrollbar. 5885 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); 5886 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); 5887 5888 // Apply scrolling 5889 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); 5890 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 5891 5892 // DRAWING 5893 5894 // Setup draw list and outer clipping rectangle 5895 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); 5896 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); 5897 PushClipRect(host_rect.Min, host_rect.Max, false); 5898 5899 // Draw modal window background (darkens what is behind them, all viewports) 5900 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; 5901 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); 5902 if (dim_bg_for_modal || dim_bg_for_window_list) 5903 { 5904 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); 5905 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); 5906 } 5907 5908 // Draw navigation selection/windowing rectangle background 5909 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) 5910 { 5911 ImRect bb = window->Rect(); 5912 bb.Expand(g.FontSize); 5913 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway 5914 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); 5915 } 5916 5917 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. 5918 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. 5919 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. 5920 // We also disabled this when we have dimming overlay behind this specific one child. 5921 // FIXME: More 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. 5922 { 5923 bool render_decorations_in_parent = false; 5924 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) 5925 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) 5926 render_decorations_in_parent = true; 5927 if (render_decorations_in_parent) 5928 window->DrawList = parent_window->DrawList; 5929 5930 // Handle title bar, scrollbar, resize grips and resize borders 5931 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; 5932 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); 5933 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); 5934 5935 if (render_decorations_in_parent) 5936 window->DrawList = &window->DrawListInst; 5937 } 5938 5939 // Draw navigation selection/windowing rectangle border 5940 if (g.NavWindowingTargetAnim == window) 5941 { 5942 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); 5943 ImRect bb = window->Rect(); 5944 bb.Expand(g.FontSize); 5945 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward 5946 { 5947 bb.Expand(-g.FontSize - 1.0f); 5948 rounding = window->WindowRounding; 5949 } 5950 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); 5951 } 5952 5953 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) 5954 5955 // Work rectangle. 5956 // Affected by window padding and border size. Used by: 5957 // - Columns() for right-most edge 5958 // - TreeNode(), CollapsingHeader() for right-most edge 5959 // - BeginTabBar() for right-most edge 5960 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); 5961 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); 5962 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->ScrollbarSizes.x)); 5963 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 - decoration_up_height - window->ScrollbarSizes.y)); 5964 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); 5965 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); 5966 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; 5967 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; 5968 window->ParentWorkRect = window->WorkRect; 5969 5970 // [LEGACY] Content Region 5971 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. 5972 // Used by: 5973 // - Mouse wheel scrolling + many other things 5974 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; 5975 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; 5976 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->ScrollbarSizes.x)); 5977 window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); 5978 5979 // Setup drawing context 5980 // (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.) 5981 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; 5982 window->DC.GroupOffset.x = 0.0f; 5983 window->DC.ColumnsOffset.x = 0.0f; 5984 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); 5985 window->DC.CursorPos = window->DC.CursorStartPos; 5986 window->DC.CursorPosPrevLine = window->DC.CursorPos; 5987 window->DC.CursorMaxPos = window->DC.CursorStartPos; 5988 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); 5989 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; 5990 5991 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5992 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; 5993 window->DC.NavLayerActiveMaskNext = 0x00; 5994 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595 5995 window->DC.NavHideHighlightOneFrame = false; 5996 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); 5997 5998 window->DC.MenuBarAppending = false; 5999 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); 6000 window->DC.TreeDepth = 0; 6001 window->DC.TreeJumpToParentOnPopMask = 0x00; 6002 window->DC.ChildWindows.resize(0); 6003 window->DC.StateStorage = &window->StateStorage; 6004 window->DC.CurrentColumns = NULL; 6005 window->DC.LayoutType = ImGuiLayoutType_Vertical; 6006 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; 6007 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; 6008 6009 window->DC.ItemWidth = window->ItemWidthDefault; 6010 window->DC.TextWrapPos = -1.0f; // disabled 6011 window->DC.ItemFlagsStack.resize(0); 6012 window->DC.ItemWidthStack.resize(0); 6013 window->DC.TextWrapPosStack.resize(0); 6014 window->DC.GroupStack.resize(0); 6015 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; 6016 if (parent_window) 6017 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); 6018 6019 if (window->AutoFitFramesX > 0) 6020 window->AutoFitFramesX--; 6021 if (window->AutoFitFramesY > 0) 6022 window->AutoFitFramesY--; 6023 6024 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) 6025 if (want_focus) 6026 { 5854 6027 FocusWindow(window); 5855 } 5856 } 5857 else 5858 { 5859 window->Collapsed = false; 5860 } 5861 window->CollapseToggleWanted = false; 5862 5863 // SIZE 5864 5865 // Calculate auto-fit size, handle automatic resize 5866 const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents); 5867 ImVec2 size_full_modified(FLT_MAX, FLT_MAX); 5868 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) 5869 { 5870 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. 5871 if (!window_size_x_set_by_api) 5872 window->SizeFull.x = size_full_modified.x = size_auto_fit.x; 5873 if (!window_size_y_set_by_api) 5874 window->SizeFull.y = size_full_modified.y = size_auto_fit.y; 5875 } 5876 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5877 { 5878 // Auto-fit may only grow window during the first few frames 5879 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. 5880 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) 5881 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; 5882 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) 5883 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; 5884 if (!window->Collapsed) 5885 MarkIniSettingsDirty(window); 5886 } 5887 5888 // Apply minimum/maximum window size constraints and final size 5889 window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull); 5890 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; 5891 5892 // SCROLLBAR STATUS 5893 5894 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). 5895 if (!window->Collapsed) 5896 { 5897 // When reading the current size we need to read it after size constraints have been applied 5898 float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x; 5899 float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y; 5900 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); 5901 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); 5902 if (window->ScrollbarX && !window->ScrollbarY) 5903 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); 5904 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); 5905 } 5906 5907 // POSITION 5908 5909 // Popup latch its initial position, will position itself when it appears next frame 5910 if (window_just_activated_by_user) 5911 { 5912 window->AutoPosLastDirection = ImGuiDir_None; 5913 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) 5914 window->Pos = g.CurrentPopupStack.back().OpenPopupPos; 5915 } 5916 5917 // Position child window 5918 if (flags & ImGuiWindowFlags_ChildWindow) 5919 { 5920 window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size; 5921 parent_window->DC.ChildWindows.push_back(window); 5922 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) 5923 window->Pos = parent_window->DC.CursorPos; 5924 } 5925 5926 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0); 5927 if (window_pos_with_pivot) 5928 SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering) 5929 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) 5930 window->Pos = FindBestWindowPosForPopup(window); 5931 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) 5932 window->Pos = FindBestWindowPosForPopup(window); 5933 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) 5934 window->Pos = FindBestWindowPosForPopup(window); 5935 5936 // Clamp position so it stays visible 5937 if (!(flags & ImGuiWindowFlags_ChildWindow)) 5938 { 5939 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. 5940 { 5941 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); 5942 window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size; 5943 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding); 5944 } 5945 } 5946 window->Pos = ImFloor(window->Pos); 5947 5948 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) 5949 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; 5950 5951 // Prepare for focus requests 5952 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter + 1)) % (window->FocusIdxAllCounter + 1); 5953 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter + 1)) % (window->FocusIdxTabCounter + 1); 5954 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; 5955 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; 5956 5957 // Apply scrolling 5958 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); 5959 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 5960 5961 // Apply focus, new windows appears in front 5962 bool want_focus = false; 5963 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) 5964 if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) 5965 want_focus = true; 5966 5967 // Handle manual resize: Resize Grips, Borders, Gamepad 5968 int border_held = -1; 5969 ImU32 resize_grip_col[4] = { 0 }; 5970 const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 5971 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); 5972 if (!window->Collapsed) 5973 UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); 5974 5975 // Default item width. Make it proportional to window size if window manually resizes 5976 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) 5977 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); 5978 else 5979 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f); 5980 5981 // DRAWING 5982 5983 // Setup draw list and outer clipping rectangle 5984 window->DrawList->Clear(); 5985 window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); 5986 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); 5987 ImRect viewport_rect(GetViewportRect()); 5988 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) 5989 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); 5990 else 5991 PushClipRect(viewport_rect.Min, viewport_rect.Max, true); 5992 5993 // Draw modal window background (darkens what is behind them) 5994 if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal()) 5995 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); 5996 5997 // Draw navigation selection/windowing rectangle background 5998 if (g.NavWindowingTarget == window) 5999 { 6000 ImRect bb = window->Rect(); 6001 bb.Expand(g.FontSize); 6002 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway 6003 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); 6004 } 6005 6006 // Draw window + handle manual resize 6007 const float window_rounding = window->WindowRounding; 6008 const float window_border_size = window->WindowBorderSize; 6009 const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); 6010 const ImRect title_bar_rect = window->TitleBarRect(); 6011 if (window->Collapsed) 6012 { 6013 // Title bar only 6014 float backup_border_size = style.FrameBorderSize; 6015 g.Style.FrameBorderSize = window->WindowBorderSize; 6016 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); 6017 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); 6018 g.Style.FrameBorderSize = backup_border_size; 6019 } 6020 else 6021 { 6022 // Window background 6023 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); 6024 if (g.NextWindowData.BgAlphaCond != 0) 6025 { 6026 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); 6027 g.NextWindowData.BgAlphaCond = 0; 6028 } 6029 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); 6030 6031 // Title bar 6032 ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); 6033 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 6034 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); 6035 6036 // Menu bar 6037 if (flags & ImGuiWindowFlags_MenuBar) 6038 { 6039 ImRect menu_bar_rect = window->MenuBarRect(); 6040 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. 6041 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); 6042 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) 6043 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); 6044 } 6045 6046 // Scrollbars 6047 if (window->ScrollbarX) 6048 Scrollbar(ImGuiLayoutType_Horizontal); 6049 if (window->ScrollbarY) 6050 Scrollbar(ImGuiLayoutType_Vertical); 6051 6052 // Render resize grips (after their input handling so we don't have a frame of latency) 6053 if (!(flags & ImGuiWindowFlags_NoResize)) 6054 { 6055 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 6056 { 6057 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 6058 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); 6059 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); 6060 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); 6061 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); 6062 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); 6063 } 6064 } 6065 6066 // Borders 6067 if (window_border_size > 0.0f) 6068 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); 6069 if (border_held != -1) 6070 { 6071 ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); 6072 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); 6073 } 6074 if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) 6075 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); 6076 } 6077 6078 // Draw navigation selection/windowing rectangle border 6079 if (g.NavWindowingTarget == window) 6080 { 6081 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); 6082 ImRect bb = window->Rect(); 6083 bb.Expand(g.FontSize); 6084 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward 6085 { 6086 bb.Expand(-g.FontSize - 1.0f); 6087 rounding = window->WindowRounding; 6088 } 6089 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); 6090 } 6091 6092 // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. 6093 window->SizeFullAtLastBegin = window->SizeFull; 6094 6095 // Update ContentsRegionMax. All the variable it depends on are set above in this function. 6096 window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x; 6097 window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); 6098 window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); 6099 window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); 6100 6101 // Setup drawing context 6102 // (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.) 6103 window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; 6104 window->DC.GroupOffsetX = 0.0f; 6105 window->DC.ColumnsOffsetX = 0.0f; 6106 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); 6107 window->DC.CursorPos = window->DC.CursorStartPos; 6108 window->DC.CursorPosPrevLine = window->DC.CursorPos; 6109 window->DC.CursorMaxPos = window->DC.CursorStartPos; 6110 window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; 6111 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; 6112 window->DC.NavHideHighlightOneFrame = false; 6113 window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); 6114 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; 6115 window->DC.NavLayerActiveMaskNext = 0x00; 6116 window->DC.MenuBarAppending = false; 6117 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; 6118 window->DC.ChildWindows.resize(0); 6119 window->DC.LayoutType = ImGuiLayoutType_Vertical; 6120 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; 6121 window->DC.ItemFlags = ImGuiItemFlags_Default_; 6122 window->DC.ItemWidth = window->ItemWidthDefault; 6123 window->DC.TextWrapPos = -1.0f; // disabled 6124 window->DC.ItemFlagsStack.resize(0); 6125 window->DC.ItemWidthStack.resize(0); 6126 window->DC.TextWrapPosStack.resize(0); 6127 window->DC.ColumnsSet = NULL; 6128 window->DC.TreeDepth = 0; 6129 window->DC.TreeDepthMayJumpToParentOnPop = 0x00; 6130 window->DC.StateStorage = &window->StateStorage; 6131 window->DC.GroupStack.resize(0); 6132 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); 6133 6134 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags)) 6135 { 6136 window->DC.ItemFlags = parent_window->DC.ItemFlags; 6137 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); 6138 } 6139 6140 if (window->AutoFitFramesX > 0) 6141 window->AutoFitFramesX--; 6142 if (window->AutoFitFramesY > 0) 6143 window->AutoFitFramesY--; 6144 6145 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) 6146 if (want_focus) 6147 { 6148 FocusWindow(window); 6149 NavInitWindow(window, false); 6150 } 6151 6152 // Title bar 6153 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 6154 { 6155 // Close & collapse button are on layer 1 (same as menus) and don't default focus 6156 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; 6157 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; 6158 window->DC.NavLayerCurrent++; 6159 window->DC.NavLayerCurrentMask <<= 1; 6160 6161 // Collapse button 6162 if (!(flags & ImGuiWindowFlags_NoCollapse)) 6163 { 6164 ImGuiID id = window->GetID("#COLLAPSE"); 6165 ImRect bb(window->Pos + style.FramePadding + ImVec2(1, 1), window->Pos + style.FramePadding + ImVec2(g.FontSize, g.FontSize) - ImVec2(1, 1)); 6166 ItemAdd(bb, id); 6167 if (ButtonBehavior(bb, id, NULL, NULL)) 6168 window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function 6169 RenderNavHighlight(bb, id); 6170 RenderArrow(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); 6171 } 6172 6173 // Close button 6174 if (p_open != NULL) 6175 { 6176 const float pad = style.FramePadding.y; 6177 const float rad = g.FontSize * 0.5f; 6178 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1)) 6179 *p_open = false; 6180 } 6181 6182 window->DC.NavLayerCurrent--; 6183 window->DC.NavLayerCurrentMask >>= 1; 6184 window->DC.ItemFlags = item_flags_backup; 6185 6186 // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.) 6187 ImVec2 text_size = CalcTextSize(name, NULL, true); 6188 ImRect text_r = title_bar_rect; 6189 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); 6190 float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); 6191 if (style.WindowTitleAlign.x > 0.0f) 6192 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); 6193 text_r.Min.x += pad_left; 6194 text_r.Max.x -= pad_right; 6195 ImRect clip_rect = text_r; 6196 clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() 6197 RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); 6198 } 6199 6200 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() 6201 window->WindowRectClipped = window->Rect(); 6202 window->WindowRectClipped.ClipWith(window->ClipRect); 6203 6204 // Pressing CTRL+C while holding on a window copy its content to the clipboard 6205 // 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. 6206 // Maybe we can support CTRL+C on every element? 6207 /* 6208 if (g.ActiveId == move_id) 6209 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) 6210 ImGui::LogToClipboard(); 6211 */ 6212 6213 // Inner rectangle 6214 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame 6215 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. 6216 window->InnerRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; 6217 window->InnerRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); 6218 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize; 6219 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize; 6220 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); 6221 6222 // Inner clipping rectangle 6223 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. 6224 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); 6225 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y); 6226 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); 6227 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y); 6228 6229 // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.). 6230 window->DC.LastItemId = window->MoveId; 6231 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; 6232 window->DC.LastItemRect = title_bar_rect; 6233 } 6234 6235 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); 6236 6237 // 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) 6238 if (first_begin_of_the_frame) 6239 window->WriteAccessed = false; 6240 6241 window->BeginCount++; 6242 g.NextWindowData.Clear(); 6243 6244 // Child window can be out of sight and have "negative" clip windows. 6245 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). 6246 if (flags & ImGuiWindowFlags_ChildWindow) 6247 { 6248 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); 6249 window->Collapsed = parent_window && parent_window->Collapsed; 6250 6251 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 6252 window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y); 6253 6254 // We also hide the window from rendering because we've already added its border to the command list. 6255 // (we could perform the check earlier in the function but it is simpler at this point) 6256 if (window->Collapsed) 6257 window->Active = false; 6258 } 6259 if (style.Alpha <= 0.0f) 6260 window->Active = false; 6261 6262 // Return false if we don't intend to display anything to allow user to perform an early out optimization 6263 window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0; 6264 return !window->SkipItems; 6265 } 6266 6267 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead. 6268 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 6269 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags) 6270 { 6271 // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file. 6272 if (size_first_use.x != 0.0f || size_first_use.y != 0.0f) 6273 ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); 6274 6275 // Old API feature: override the window background alpha with a parameter. 6276 if (bg_alpha_override >= 0.0f) 6277 ImGui::SetNextWindowBgAlpha(bg_alpha_override); 6278 6279 return ImGui::Begin(name, p_open, flags); 6280 } 6281 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS 6028 NavInitWindow(window, false); 6029 } 6030 6031 // Title bar 6032 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 6033 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); 6034 6035 // Clear hit test shape every frame 6036 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; 6037 6038 // Pressing CTRL+C while holding on a window copy its content to the clipboard 6039 // 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. 6040 // Maybe we can support CTRL+C on every element? 6041 /* 6042 if (g.ActiveId == move_id) 6043 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) 6044 LogToClipboard(); 6045 */ 6046 6047 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). 6048 // This is useful to allow creating context menus on title bar only, etc. 6049 SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); 6050 6051 #ifdef IMGUI_ENABLE_TEST_ENGINE 6052 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) 6053 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); 6054 #endif 6055 } 6056 else 6057 { 6058 // Append 6059 SetCurrentWindow(window); 6060 } 6061 6062 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); 6063 6064 // 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) 6065 if (first_begin_of_the_frame) 6066 window->WriteAccessed = false; 6067 6068 window->BeginCount++; 6069 g.NextWindowData.ClearFlags(); 6070 6071 // Update visibility 6072 if (first_begin_of_the_frame) 6073 { 6074 if (flags & ImGuiWindowFlags_ChildWindow) 6075 { 6076 // Child window can be out of sight and have "negative" clip windows. 6077 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). 6078 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); 6079 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 6080 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) 6081 window->HiddenFramesCanSkipItems = 1; 6082 6083 // Hide along with parent or if parent is collapsed 6084 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) 6085 window->HiddenFramesCanSkipItems = 1; 6086 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) 6087 window->HiddenFramesCannotSkipItems = 1; 6088 } 6089 6090 // 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) 6091 if (style.Alpha <= 0.0f) 6092 window->HiddenFramesCanSkipItems = 1; 6093 6094 // Update the Hidden flag 6095 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); 6096 6097 // Update the SkipItems flag, used to early out of all items functions (no layout required) 6098 bool skip_items = false; 6099 if (window->Collapsed || !window->Active || window->Hidden) 6100 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) 6101 skip_items = true; 6102 window->SkipItems = skip_items; 6103 } 6104 6105 return !window->SkipItems; 6106 } 6282 6107 6283 6108 void ImGui::End() 6284 6109 { 6285 ImGuiContext& g = *GImGui; 6286 ImGuiWindow* window = g.CurrentWindow; 6287 6288 if (window->DC.ColumnsSet != NULL) 6289 EndColumns(); 6290 PopClipRect(); // Inner window clip rectangle 6291 6292 // Stop logging 6293 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging 6294 LogFinish(); 6295 6296 // Pop from window stack 6297 g.CurrentWindowStack.pop_back(); 6298 if (window->Flags & ImGuiWindowFlags_Popup) 6299 g.CurrentPopupStack.pop_back(); 6300 CheckStacksSize(window, false); 6301 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); 6302 } 6303 6304 // Vertical scrollbar 6305 // The entire piece of code below is rather confusing because: 6306 // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) 6307 // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar 6308 // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. 6309 void ImGui::Scrollbar(ImGuiLayoutType direction) 6310 { 6311 ImGuiContext& g = *GImGui; 6312 ImGuiWindow* window = g.CurrentWindow; 6313 6314 const bool horizontal = (direction == ImGuiLayoutType_Horizontal); 6315 const ImGuiStyle& style = g.Style; 6316 const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); 6317 6318 // Render background 6319 bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); 6320 float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; 6321 const ImRect window_rect = window->Rect(); 6322 const float border_size = window->WindowBorderSize; 6323 ImRect bb = horizontal 6324 ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) 6325 : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); 6326 if (!horizontal) 6327 bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); 6328 if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) 6329 return; 6330 6331 int window_rounding_corners; 6332 if (horizontal) 6333 window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); 6334 else 6335 window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); 6336 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); 6337 bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); 6338 6339 // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) 6340 float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); 6341 float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; 6342 float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w; 6343 float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; 6344 6345 // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) 6346 // But we maintain a minimum size in pixel to allow for the user to still aim inside. 6347 IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. 6348 const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f); 6349 const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); 6350 const float grab_h_norm = grab_h_pixels / scrollbar_size_v; 6351 6352 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). 6353 bool held = false; 6354 bool hovered = false; 6355 const bool previously_held = (g.ActiveId == id); 6356 ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); 6357 6358 float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); 6359 float scroll_ratio = ImSaturate(scroll_v / scroll_max); 6360 float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; 6361 if (held && grab_h_norm < 1.0f) 6362 { 6363 float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; 6364 float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; 6365 float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; 6366 6367 // Click position in scrollbar normalized space (0.0f->1.0f) 6368 const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); 6369 SetHoveredID(id); 6370 6371 bool seek_absolute = false; 6372 if (!previously_held) 6373 { 6374 // On initial click calculate the distance between mouse and the center of the grab 6375 if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) 6376 { 6377 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; 6378 } 6379 else 6380 { 6381 seek_absolute = true; 6382 *click_delta_to_grab_center_v = 0.0f; 6383 } 6384 } 6385 6386 // Apply scroll 6387 // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position 6388 const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); 6389 scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); 6390 if (horizontal) 6391 window->Scroll.x = scroll_v; 6392 else 6393 window->Scroll.y = scroll_v; 6394 6395 // Update values for rendering 6396 scroll_ratio = ImSaturate(scroll_v / scroll_max); 6397 grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; 6398 6399 // Update distance to grab now that we have seeked and saturated 6400 if (seek_absolute) 6401 *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f; 6402 } 6403 6404 // Render 6405 const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); 6406 ImRect grab_rect; 6407 if (horizontal) 6408 grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); 6409 else 6410 grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); 6411 window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); 6412 } 6413 6414 void ImGui::BringWindowToFront(ImGuiWindow* window) 6415 { 6416 ImGuiContext& g = *GImGui; 6417 ImGuiWindow* current_front_window = g.Windows.back(); 6418 if (current_front_window == window || current_front_window->RootWindow == window) 6419 return; 6420 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window 6421 if (g.Windows[i] == window) 6422 { 6423 g.Windows.erase(g.Windows.Data + i); 6424 g.Windows.push_back(window); 6425 break; 6426 } 6427 } 6428 6429 void ImGui::BringWindowToBack(ImGuiWindow* window) 6430 { 6431 ImGuiContext& g = *GImGui; 6432 if (g.Windows[0] == window) 6433 return; 6434 for (int i = 0; i < g.Windows.Size; i++) 6435 if (g.Windows[i] == window) 6436 { 6437 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); 6438 g.Windows[0] = window; 6439 break; 6440 } 6110 ImGuiContext& g = *GImGui; 6111 ImGuiWindow* window = g.CurrentWindow; 6112 6113 // Error checking: verify that user hasn't called End() too many times! 6114 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow) 6115 { 6116 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); 6117 return; 6118 } 6119 IM_ASSERT(g.CurrentWindowStack.Size > 0); 6120 6121 // Error checking: verify that user doesn't directly call End() on a child window. 6122 if (window->Flags & ImGuiWindowFlags_ChildWindow) 6123 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); 6124 6125 // Close anything that is open 6126 if (window->DC.CurrentColumns) 6127 EndColumns(); 6128 PopClipRect(); // Inner window clip rectangle 6129 6130 // Stop logging 6131 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging 6132 LogFinish(); 6133 6134 // Pop from window stack 6135 g.CurrentWindowStack.pop_back(); 6136 if (window->Flags & ImGuiWindowFlags_Popup) 6137 g.BeginPopupStack.pop_back(); 6138 ErrorCheckBeginEndCompareStacksSize(window, false); 6139 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); 6140 } 6141 6142 void ImGui::BringWindowToFocusFront(ImGuiWindow* window) 6143 { 6144 ImGuiContext& g = *GImGui; 6145 if (g.WindowsFocusOrder.back() == window) 6146 return; 6147 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window 6148 if (g.WindowsFocusOrder[i] == window) 6149 { 6150 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); 6151 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; 6152 break; 6153 } 6154 } 6155 6156 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) 6157 { 6158 ImGuiContext& g = *GImGui; 6159 ImGuiWindow* current_front_window = g.Windows.back(); 6160 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better) 6161 return; 6162 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window 6163 if (g.Windows[i] == window) 6164 { 6165 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); 6166 g.Windows[g.Windows.Size - 1] = window; 6167 break; 6168 } 6169 } 6170 6171 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) 6172 { 6173 ImGuiContext& g = *GImGui; 6174 if (g.Windows[0] == window) 6175 return; 6176 for (int i = 0; i < g.Windows.Size; i++) 6177 if (g.Windows[i] == window) 6178 { 6179 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); 6180 g.Windows[0] = window; 6181 break; 6182 } 6441 6183 } 6442 6184 … … 6444 6186 void ImGui::FocusWindow(ImGuiWindow* window) 6445 6187 { 6446 ImGuiContext& g = *GImGui; 6447 6448 if (g.NavWindow != window) 6449 { 6450 g.NavWindow = window; 6451 if (window && g.NavDisableMouseHover) 6452 g.NavMousePosDirty = true; 6453 g.NavInitRequest = false; 6454 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId 6455 g.NavIdIsAlive = false; 6456 g.NavLayer = 0; 6457 //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL); 6458 } 6459 6460 // Passing NULL allow to disable keyboard focus 6461 if (!window) 6462 return; 6463 6464 // Move the root window to the top of the pile 6465 if (window->RootWindow) 6466 window = window->RootWindow; 6467 6468 // Steal focus on active widgets 6469 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it.. 6470 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window) 6471 ClearActiveID(); 6472 6473 // Bring to front 6474 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) 6475 BringWindowToFront(window); 6476 } 6477 6478 void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window) 6479 { 6480 ImGuiContext& g = *GImGui; 6481 for (int i = g.Windows.Size - 1; i >= 0; i--) 6482 if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) 6483 { 6484 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); 6485 FocusWindow(focus_window); 6486 return; 6487 } 6488 } 6489 6490 void ImGui::PushItemWidth(float item_width) 6491 { 6492 ImGuiWindow* window = GetCurrentWindow(); 6493 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); 6494 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); 6495 } 6496 6497 void ImGui::PushMultiItemsWidths(int components, float w_full) 6498 { 6499 ImGuiWindow* window = GetCurrentWindow(); 6500 const ImGuiStyle& style = GImGui->Style; 6501 if (w_full <= 0.0f) 6502 w_full = CalcItemWidth(); 6503 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); 6504 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); 6505 window->DC.ItemWidthStack.push_back(w_item_last); 6506 for (int i = 0; i < components - 1; i++) 6507 window->DC.ItemWidthStack.push_back(w_item_one); 6508 window->DC.ItemWidth = window->DC.ItemWidthStack.back(); 6509 } 6510 6511 void ImGui::PopItemWidth() 6512 { 6513 ImGuiWindow* window = GetCurrentWindow(); 6514 window->DC.ItemWidthStack.pop_back(); 6515 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); 6516 } 6517 6518 float ImGui::CalcItemWidth() 6519 { 6520 ImGuiWindow* window = GetCurrentWindowRead(); 6521 float w = window->DC.ItemWidth; 6522 if (w < 0.0f) 6523 { 6524 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. 6525 float width_to_right_edge = GetContentRegionAvail().x; 6526 w = ImMax(1.0f, width_to_right_edge + w); 6527 } 6528 w = (float)(int)w; 6529 return w; 6530 } 6531 6532 static ImFont* GetDefaultFont() 6533 { 6534 ImGuiContext& g = *GImGui; 6535 return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; 6188 ImGuiContext& g = *GImGui; 6189 6190 if (g.NavWindow != window) 6191 { 6192 g.NavWindow = window; 6193 if (window && g.NavDisableMouseHover) 6194 g.NavMousePosDirty = true; 6195 g.NavInitRequest = false; 6196 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId 6197 g.NavFocusScopeId = 0; 6198 g.NavIdIsAlive = false; 6199 g.NavLayer = ImGuiNavLayer_Main; 6200 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); 6201 } 6202 6203 // Close popups if any 6204 ClosePopupsOverWindow(window, false); 6205 6206 // Move the root window to the top of the pile 6207 IM_ASSERT(window == NULL || window->RootWindow != NULL); 6208 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop 6209 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; 6210 6211 // Steal active widgets. Some of the cases it triggers includes: 6212 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. 6213 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) 6214 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) 6215 if (!g.ActiveIdNoClearOnFocusLoss) 6216 ClearActiveID(); 6217 6218 // Passing NULL allow to disable keyboard focus 6219 if (!window) 6220 return; 6221 6222 // Bring to front 6223 BringWindowToFocusFront(focus_front_window); 6224 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) 6225 BringWindowToDisplayFront(display_front_window); 6226 } 6227 6228 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) 6229 { 6230 ImGuiContext& g = *GImGui; 6231 6232 int start_idx = g.WindowsFocusOrder.Size - 1; 6233 if (under_this_window != NULL) 6234 { 6235 int under_this_window_idx = FindWindowFocusIndex(under_this_window); 6236 if (under_this_window_idx != -1) 6237 start_idx = under_this_window_idx - 1; 6238 } 6239 for (int i = start_idx; i >= 0; i--) 6240 { 6241 // 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. 6242 ImGuiWindow* window = g.WindowsFocusOrder[i]; 6243 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) 6244 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) 6245 { 6246 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); 6247 FocusWindow(focus_window); 6248 return; 6249 } 6250 } 6251 FocusWindow(NULL); 6536 6252 } 6537 6253 6538 6254 void ImGui::SetCurrentFont(ImFont* font) 6539 6255 { 6540 ImGuiContext& g = *GImGui; 6541 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? 6542 IM_ASSERT(font->Scale > 0.0f); 6543 g.Font = font; 6544 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; 6545 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; 6546 6547 ImFontAtlas* atlas = g.Font->ContainerAtlas; 6548 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; 6549 g.DrawListSharedData.Font = g.Font; 6550 g.DrawListSharedData.FontSize = g.FontSize; 6256 ImGuiContext& g = *GImGui; 6257 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? 6258 IM_ASSERT(font->Scale > 0.0f); 6259 g.Font = font; 6260 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); 6261 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; 6262 6263 ImFontAtlas* atlas = g.Font->ContainerAtlas; 6264 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; 6265 g.DrawListSharedData.TexUvLines = atlas->TexUvLines; 6266 g.DrawListSharedData.Font = g.Font; 6267 g.DrawListSharedData.FontSize = g.FontSize; 6551 6268 } 6552 6269 6553 6270 void ImGui::PushFont(ImFont* font) 6554 6271 { 6555 ImGuiContext& g = *GImGui;6556 if (!font)6557 font = GetDefaultFont();6558 SetCurrentFont(font);6559 g.FontStack.push_back(font);6560 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);6272 ImGuiContext& g = *GImGui; 6273 if (!font) 6274 font = GetDefaultFont(); 6275 SetCurrentFont(font); 6276 g.FontStack.push_back(font); 6277 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); 6561 6278 } 6562 6279 6563 6280 void ImGui::PopFont() 6564 6281 { 6565 ImGuiContext& g = *GImGui;6566 g.CurrentWindow->DrawList->PopTextureID();6567 g.FontStack.pop_back();6568 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());6282 ImGuiContext& g = *GImGui; 6283 g.CurrentWindow->DrawList->PopTextureID(); 6284 g.FontStack.pop_back(); 6285 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); 6569 6286 } 6570 6287 6571 6288 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) 6572 6289 { 6573 ImGuiWindow* window = GetCurrentWindow();6574 if (enabled)6575 window->DC.ItemFlags |= option;6576 else6577 window->DC.ItemFlags &= ~option;6578 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);6290 ImGuiWindow* window = GetCurrentWindow(); 6291 if (enabled) 6292 window->DC.ItemFlags |= option; 6293 else 6294 window->DC.ItemFlags &= ~option; 6295 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); 6579 6296 } 6580 6297 6581 6298 void ImGui::PopItemFlag() 6582 6299 { 6583 ImGuiWindow* window = GetCurrentWindow(); 6584 window->DC.ItemFlagsStack.pop_back(); 6585 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); 6586 } 6587 6300 ImGuiWindow* window = GetCurrentWindow(); 6301 window->DC.ItemFlagsStack.pop_back(); 6302 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); 6303 } 6304 6305 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. 6588 6306 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) 6589 6307 { 6590 PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus,allow_keyboard_focus);6308 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); 6591 6309 } 6592 6310 6593 6311 void ImGui::PopAllowKeyboardFocus() 6594 6312 { 6595 PopItemFlag();6313 PopItemFlag(); 6596 6314 } 6597 6315 6598 6316 void ImGui::PushButtonRepeat(bool repeat) 6599 6317 { 6600 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);6318 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); 6601 6319 } 6602 6320 6603 6321 void ImGui::PopButtonRepeat() 6604 6322 { 6605 PopItemFlag();6323 PopItemFlag(); 6606 6324 } 6607 6325 6608 6326 void ImGui::PushTextWrapPos(float wrap_pos_x) 6609 6327 { 6610 ImGuiWindow* window = GetCurrentWindow();6611 window->DC.TextWrapPos = wrap_pos_x;6612 window->DC.TextWrapPosStack.push_back(wrap_pos_x);6328 ImGuiWindow* window = GetCurrentWindow(); 6329 window->DC.TextWrapPos = wrap_pos_x; 6330 window->DC.TextWrapPosStack.push_back(wrap_pos_x); 6613 6331 } 6614 6332 6615 6333 void ImGui::PopTextWrapPos() 6616 6334 { 6617 ImGuiWindow* window = GetCurrentWindow(); 6618 window->DC.TextWrapPosStack.pop_back(); 6619 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); 6620 } 6621 6622 // 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 6623 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) 6624 { 6625 ImGuiContext& g = *GImGui; 6626 ImGuiColMod backup; 6627 backup.Col = idx; 6628 backup.BackupValue = g.Style.Colors[idx]; 6629 g.ColorModifiers.push_back(backup); 6630 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); 6631 } 6632 6633 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) 6634 { 6635 ImGuiContext& g = *GImGui; 6636 ImGuiColMod backup; 6637 backup.Col = idx; 6638 backup.BackupValue = g.Style.Colors[idx]; 6639 g.ColorModifiers.push_back(backup); 6640 g.Style.Colors[idx] = col; 6641 } 6642 6643 void ImGui::PopStyleColor(int count) 6644 { 6645 ImGuiContext& g = *GImGui; 6646 while (count > 0) 6647 { 6648 ImGuiColMod& backup = g.ColorModifiers.back(); 6649 g.Style.Colors[backup.Col] = backup.BackupValue; 6650 g.ColorModifiers.pop_back(); 6651 count--; 6652 } 6653 } 6654 6655 struct ImGuiStyleVarInfo 6656 { 6657 ImGuiDataType Type; 6658 ImU32 Count; 6659 ImU32 Offset; 6660 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } 6661 }; 6662 6663 static const ImGuiStyleVarInfo GStyleVarInfo[] = 6664 { 6665 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha 6666 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding 6667 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding 6668 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize 6669 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize 6670 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign 6671 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding 6672 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize 6673 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding 6674 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize 6675 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding 6676 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding 6677 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize 6678 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing 6679 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing 6680 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing 6681 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize 6682 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding 6683 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize 6684 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding 6685 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign 6686 }; 6687 6688 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) 6689 { 6690 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); 6691 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); 6692 return &GStyleVarInfo[idx]; 6693 } 6694 6695 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) 6696 { 6697 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 6698 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) 6699 { 6700 ImGuiContext& g = *GImGui; 6701 float* pvar = (float*)var_info->GetVarPtr(&g.Style); 6702 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 6703 *pvar = val; 6704 return; 6705 } 6706 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float. 6707 } 6708 6709 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) 6710 { 6711 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 6712 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) 6713 { 6714 ImGuiContext& g = *GImGui; 6715 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); 6716 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 6717 *pvar = val; 6718 return; 6719 } 6720 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2. 6721 } 6722 6723 void ImGui::PopStyleVar(int count) 6724 { 6725 ImGuiContext& g = *GImGui; 6726 while (count > 0) 6727 { 6728 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. 6729 ImGuiStyleMod& backup = g.StyleModifiers.back(); 6730 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); 6731 void* data = info->GetVarPtr(&g.Style); 6732 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } 6733 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } 6734 g.StyleModifiers.pop_back(); 6735 count--; 6736 } 6737 } 6738 6739 const char* ImGui::GetStyleColorName(ImGuiCol idx) 6740 { 6741 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; 6742 switch (idx) 6743 { 6744 case ImGuiCol_Text: return "Text"; 6745 case ImGuiCol_TextDisabled: return "TextDisabled"; 6746 case ImGuiCol_WindowBg: return "WindowBg"; 6747 case ImGuiCol_ChildBg: return "ChildBg"; 6748 case ImGuiCol_PopupBg: return "PopupBg"; 6749 case ImGuiCol_Border: return "Border"; 6750 case ImGuiCol_BorderShadow: return "BorderShadow"; 6751 case ImGuiCol_FrameBg: return "FrameBg"; 6752 case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; 6753 case ImGuiCol_FrameBgActive: return "FrameBgActive"; 6754 case ImGuiCol_TitleBg: return "TitleBg"; 6755 case ImGuiCol_TitleBgActive: return "TitleBgActive"; 6756 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; 6757 case ImGuiCol_MenuBarBg: return "MenuBarBg"; 6758 case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; 6759 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; 6760 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; 6761 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; 6762 case ImGuiCol_CheckMark: return "CheckMark"; 6763 case ImGuiCol_SliderGrab: return "SliderGrab"; 6764 case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; 6765 case ImGuiCol_Button: return "Button"; 6766 case ImGuiCol_ButtonHovered: return "ButtonHovered"; 6767 case ImGuiCol_ButtonActive: return "ButtonActive"; 6768 case ImGuiCol_Header: return "Header"; 6769 case ImGuiCol_HeaderHovered: return "HeaderHovered"; 6770 case ImGuiCol_HeaderActive: return "HeaderActive"; 6771 case ImGuiCol_Separator: return "Separator"; 6772 case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; 6773 case ImGuiCol_SeparatorActive: return "SeparatorActive"; 6774 case ImGuiCol_ResizeGrip: return "ResizeGrip"; 6775 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; 6776 case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; 6777 case ImGuiCol_PlotLines: return "PlotLines"; 6778 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; 6779 case ImGuiCol_PlotHistogram: return "PlotHistogram"; 6780 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; 6781 case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; 6782 case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; 6783 case ImGuiCol_DragDropTarget: return "DragDropTarget"; 6784 case ImGuiCol_NavHighlight: return "NavHighlight"; 6785 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; 6786 } 6787 IM_ASSERT(0); 6788 return "Unknown"; 6335 ImGuiWindow* window = GetCurrentWindow(); 6336 window->DC.TextWrapPosStack.pop_back(); 6337 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); 6789 6338 } 6790 6339 6791 6340 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) 6792 6341 { 6793 if (window->RootWindow == potential_parent)6794 return true;6795 while (window != NULL)6796 {6797 if (window == potential_parent)6798 return true;6799 window = window->ParentWindow;6800 }6801 return false;6342 if (window->RootWindow == potential_parent) 6343 return true; 6344 while (window != NULL) 6345 { 6346 if (window == potential_parent) 6347 return true; 6348 window = window->ParentWindow; 6349 } 6350 return false; 6802 6351 } 6803 6352 6804 6353 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) 6805 6354 { 6806 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function 6807 ImGuiContext& g = *GImGui; 6808 6809 if (flags & ImGuiHoveredFlags_AnyWindow) 6810 { 6811 if (g.HoveredWindow == NULL) 6812 return false; 6813 } 6814 else 6815 { 6816 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) 6817 { 6818 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: 6819 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) 6355 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function 6356 ImGuiContext& g = *GImGui; 6357 6358 if (flags & ImGuiHoveredFlags_AnyWindow) 6359 { 6360 if (g.HoveredWindow == NULL) 6820 6361 return false; 6821 break; 6822 case ImGuiHoveredFlags_RootWindow: 6823 if (g.HoveredWindow != g.CurrentWindow->RootWindow) 6362 } 6363 else 6364 { 6365 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) 6366 { 6367 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: 6368 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) 6369 return false; 6370 break; 6371 case ImGuiHoveredFlags_RootWindow: 6372 if (g.HoveredWindow != g.CurrentWindow->RootWindow) 6373 return false; 6374 break; 6375 case ImGuiHoveredFlags_ChildWindows: 6376 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow)) 6377 return false; 6378 break; 6379 default: 6380 if (g.HoveredWindow != g.CurrentWindow) 6381 return false; 6382 break; 6383 } 6384 } 6385 6386 if (!IsWindowContentHoverable(g.HoveredWindow, flags)) 6387 return false; 6388 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 6389 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) 6824 6390 return false; 6825 break; 6826 case ImGuiHoveredFlags_ChildWindows: 6827 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow)) 6828 return false; 6829 break; 6830 default: 6831 if (g.HoveredWindow != g.CurrentWindow) 6832 return false; 6833 break; 6834 } 6835 } 6836 6837 if (!IsWindowContentHoverable(g.HoveredRootWindow, flags)) 6838 return false; 6839 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 6840 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) 6841 return false; 6842 return true; 6391 return true; 6843 6392 } 6844 6393 6845 6394 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) 6846 6395 { 6847 ImGuiContext& g = *GImGui;6848 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() 6849 6850 if (flags & ImGuiFocusedFlags_AnyWindow)6851 return g.NavWindow != NULL; 6852 6853 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))6854 {6855 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:6856 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;6857 case ImGuiFocusedFlags_RootWindow:6858 return g.NavWindow == g.CurrentWindow->RootWindow;6859 case ImGuiFocusedFlags_ChildWindows:6860 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);6861 default:6862 return g.NavWindow == g.CurrentWindow;6863 }6396 ImGuiContext& g = *GImGui; 6397 6398 if (flags & ImGuiFocusedFlags_AnyWindow) 6399 return g.NavWindow != NULL; 6400 6401 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() 6402 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) 6403 { 6404 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: 6405 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; 6406 case ImGuiFocusedFlags_RootWindow: 6407 return g.NavWindow == g.CurrentWindow->RootWindow; 6408 case ImGuiFocusedFlags_ChildWindows: 6409 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); 6410 default: 6411 return g.NavWindow == g.CurrentWindow; 6412 } 6864 6413 } 6865 6414 6866 6415 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) 6416 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically. 6417 // If you want a window to never be focused, you may use the e.g. NoInputs flag. 6867 6418 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) 6868 6419 { 6869 ImGuiContext& g = *GImGui; 6870 return window->Active && window == window->RootWindowForTabbing && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow); 6420 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); 6871 6421 } 6872 6422 6873 6423 float ImGui::GetWindowWidth() 6874 6424 { 6875 ImGuiWindow* window = GImGui->CurrentWindow;6876 return window->Size.x;6425 ImGuiWindow* window = GImGui->CurrentWindow; 6426 return window->Size.x; 6877 6427 } 6878 6428 6879 6429 float ImGui::GetWindowHeight() 6880 6430 { 6881 ImGuiWindow* window = GImGui->CurrentWindow;6882 return window->Size.y;6431 ImGuiWindow* window = GImGui->CurrentWindow; 6432 return window->Size.y; 6883 6433 } 6884 6434 6885 6435 ImVec2 ImGui::GetWindowPos() 6886 6436 { 6887 ImGuiContext& g = *GImGui; 6888 ImGuiWindow* window = g.CurrentWindow; 6889 return window->Pos; 6890 } 6891 6892 static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x) 6893 { 6894 window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. 6895 window->Scroll.x = new_scroll_x; 6896 window->DC.CursorMaxPos.x -= window->Scroll.x; 6897 } 6898 6899 static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) 6900 { 6901 window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. 6902 window->Scroll.y = new_scroll_y; 6903 window->DC.CursorMaxPos.y -= window->Scroll.y; 6904 } 6905 6906 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) 6907 { 6908 // Test condition (NB: bit 0 is always true) and clear flags for next time 6909 if (cond && (window->SetWindowPosAllowFlags & cond) == 0) 6910 return; 6911 6912 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6913 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6914 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); 6915 6916 // Set 6917 const ImVec2 old_pos = window->Pos; 6918 window->Pos = ImFloor(pos); 6919 window->DC.CursorPos += (window->Pos - old_pos); // 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 6920 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. 6437 ImGuiContext& g = *GImGui; 6438 ImGuiWindow* window = g.CurrentWindow; 6439 return window->Pos; 6440 } 6441 6442 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) 6443 { 6444 // Test condition (NB: bit 0 is always true) and clear flags for next time 6445 if (cond && (window->SetWindowPosAllowFlags & cond) == 0) 6446 return; 6447 6448 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6449 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6450 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); 6451 6452 // Set 6453 const ImVec2 old_pos = window->Pos; 6454 window->Pos = ImFloor(pos); 6455 ImVec2 offset = window->Pos - old_pos; 6456 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 6457 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. 6458 window->DC.CursorStartPos += offset; 6921 6459 } 6922 6460 6923 6461 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) 6924 6462 { 6925 ImGuiWindow* window = GetCurrentWindowRead();6926 SetWindowPos(window, pos, cond);6463 ImGuiWindow* window = GetCurrentWindowRead(); 6464 SetWindowPos(window, pos, cond); 6927 6465 } 6928 6466 6929 6467 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) 6930 6468 { 6931 if (ImGuiWindow* window = FindWindowByName(name))6932 SetWindowPos(window, pos, cond);6469 if (ImGuiWindow* window = FindWindowByName(name)) 6470 SetWindowPos(window, pos, cond); 6933 6471 } 6934 6472 6935 6473 ImVec2 ImGui::GetWindowSize() 6936 6474 { 6937 ImGuiWindow* window = GetCurrentWindowRead();6938 return window->Size;6939 } 6940 6941 static voidSetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)6942 { 6943 // Test condition (NB: bit 0 is always true) and clear flags for next time6944 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)6945 return;6946 6947 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.6948 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);6949 6950 // Set6951 if (size.x > 0.0f)6952 {6953 window->AutoFitFramesX = 0;6954 window->SizeFull.x = size.x;6955 }6956 else6957 {6958 window->AutoFitFramesX = 2;6959 window->AutoFitOnlyGrows = false;6960 }6961 if (size.y > 0.0f)6962 {6963 window->AutoFitFramesY = 0;6964 window->SizeFull.y = size.y;6965 }6966 else6967 {6968 window->AutoFitFramesY = 2;6969 window->AutoFitOnlyGrows = false;6970 }6475 ImGuiWindow* window = GetCurrentWindowRead(); 6476 return window->Size; 6477 } 6478 6479 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) 6480 { 6481 // Test condition (NB: bit 0 is always true) and clear flags for next time 6482 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) 6483 return; 6484 6485 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6486 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6487 6488 // Set 6489 if (size.x > 0.0f) 6490 { 6491 window->AutoFitFramesX = 0; 6492 window->SizeFull.x = IM_FLOOR(size.x); 6493 } 6494 else 6495 { 6496 window->AutoFitFramesX = 2; 6497 window->AutoFitOnlyGrows = false; 6498 } 6499 if (size.y > 0.0f) 6500 { 6501 window->AutoFitFramesY = 0; 6502 window->SizeFull.y = IM_FLOOR(size.y); 6503 } 6504 else 6505 { 6506 window->AutoFitFramesY = 2; 6507 window->AutoFitOnlyGrows = false; 6508 } 6971 6509 } 6972 6510 6973 6511 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) 6974 6512 { 6975 SetWindowSize(GImGui->CurrentWindow, size, cond);6513 SetWindowSize(GImGui->CurrentWindow, size, cond); 6976 6514 } 6977 6515 6978 6516 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) 6979 6517 { 6980 if (ImGuiWindow* window = FindWindowByName(name)) 6981 SetWindowSize(window, size, cond); 6982 } 6983 6984 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) 6985 { 6986 // Test condition (NB: bit 0 is always true) and clear flags for next time 6987 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) 6988 return; 6989 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6990 6991 // Set 6992 window->Collapsed = collapsed; 6518 if (ImGuiWindow* window = FindWindowByName(name)) 6519 SetWindowSize(window, size, cond); 6520 } 6521 6522 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) 6523 { 6524 // Test condition (NB: bit 0 is always true) and clear flags for next time 6525 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) 6526 return; 6527 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6528 6529 // Set 6530 window->Collapsed = collapsed; 6531 } 6532 6533 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) 6534 { 6535 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters 6536 window->HitTestHoleSize = ImVec2ih(size); 6537 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); 6993 6538 } 6994 6539 6995 6540 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) 6996 6541 { 6997 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);6542 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); 6998 6543 } 6999 6544 7000 6545 bool ImGui::IsWindowCollapsed() 7001 6546 { 7002 ImGuiWindow* window = GetCurrentWindowRead();7003 return window->Collapsed;6547 ImGuiWindow* window = GetCurrentWindowRead(); 6548 return window->Collapsed; 7004 6549 } 7005 6550 7006 6551 bool ImGui::IsWindowAppearing() 7007 6552 { 7008 ImGuiWindow* window = GetCurrentWindowRead();7009 return window->Appearing;6553 ImGuiWindow* window = GetCurrentWindowRead(); 6554 return window->Appearing; 7010 6555 } 7011 6556 7012 6557 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) 7013 6558 { 7014 if (ImGuiWindow* window = FindWindowByName(name))7015 SetWindowCollapsed(window, collapsed, cond);6559 if (ImGuiWindow* window = FindWindowByName(name)) 6560 SetWindowCollapsed(window, collapsed, cond); 7016 6561 } 7017 6562 7018 6563 void ImGui::SetWindowFocus() 7019 6564 { 7020 FocusWindow(GImGui->CurrentWindow);6565 FocusWindow(GImGui->CurrentWindow); 7021 6566 } 7022 6567 7023 6568 void ImGui::SetWindowFocus(const char* name) 7024 6569 { 7025 if (name)7026 {7027 if (ImGuiWindow* window = FindWindowByName(name))7028 FocusWindow(window);7029 }7030 else7031 {7032 FocusWindow(NULL);7033 }6570 if (name) 6571 { 6572 if (ImGuiWindow* window = FindWindowByName(name)) 6573 FocusWindow(window); 6574 } 6575 else 6576 { 6577 FocusWindow(NULL); 6578 } 7034 6579 } 7035 6580 7036 6581 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) 7037 6582 { 7038 ImGuiContext& g = *GImGui; 7039 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 7040 g.NextWindowData.PosVal = pos; 7041 g.NextWindowData.PosPivotVal = pivot; 7042 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; 6583 ImGuiContext& g = *GImGui; 6584 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6585 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos; 6586 g.NextWindowData.PosVal = pos; 6587 g.NextWindowData.PosPivotVal = pivot; 6588 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; 7043 6589 } 7044 6590 7045 6591 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) 7046 6592 { 7047 ImGuiContext& g = *GImGui; 7048 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 7049 g.NextWindowData.SizeVal = size; 7050 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; 6593 ImGuiContext& g = *GImGui; 6594 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6595 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize; 6596 g.NextWindowData.SizeVal = size; 6597 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; 7051 6598 } 7052 6599 7053 6600 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) 7054 6601 { 7055 ImGuiContext& g = *GImGui; 7056 g.NextWindowData.SizeConstraintCond = ImGuiCond_Always; 7057 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); 7058 g.NextWindowData.SizeCallback = custom_callback; 7059 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; 7060 } 7061 6602 ImGuiContext& g = *GImGui; 6603 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; 6604 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); 6605 g.NextWindowData.SizeCallback = custom_callback; 6606 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; 6607 } 6608 6609 // Content size = inner scrollable rectangle, padded with WindowPadding. 6610 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. 7062 6611 void ImGui::SetNextWindowContentSize(const ImVec2& size) 7063 6612 { 7064 ImGuiContext& g = *GImGui; 7065 g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value. 7066 g.NextWindowData.ContentSizeCond = ImGuiCond_Always; 6613 ImGuiContext& g = *GImGui; 6614 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; 6615 g.NextWindowData.ContentSizeVal = size; 6616 } 6617 6618 void ImGui::SetNextWindowScroll(const ImVec2& scroll) 6619 { 6620 ImGuiContext& g = *GImGui; 6621 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll; 6622 g.NextWindowData.ScrollVal = scroll; 7067 6623 } 7068 6624 7069 6625 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) 7070 6626 { 7071 ImGuiContext& g = *GImGui; 7072 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 7073 g.NextWindowData.CollapsedVal = collapsed; 7074 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; 6627 ImGuiContext& g = *GImGui; 6628 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6629 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed; 6630 g.NextWindowData.CollapsedVal = collapsed; 6631 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; 7075 6632 } 7076 6633 7077 6634 void ImGui::SetNextWindowFocus() 7078 6635 { 7079 ImGuiContext& g = *GImGui;7080 g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)6636 ImGuiContext& g = *GImGui; 6637 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus; 7081 6638 } 7082 6639 7083 6640 void ImGui::SetNextWindowBgAlpha(float alpha) 7084 6641 { 7085 ImGuiContext& g = *GImGui; 7086 g.NextWindowData.BgAlphaVal = alpha; 7087 g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) 7088 } 7089 7090 // In window space (not screen space!) 7091 ImVec2 ImGui::GetContentRegionMax() 7092 { 7093 ImGuiWindow* window = GetCurrentWindowRead(); 7094 ImVec2 mx = window->ContentsRegionRect.Max; 7095 if (window->DC.ColumnsSet) 7096 mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x; 7097 return mx; 7098 } 7099 7100 ImVec2 ImGui::GetContentRegionAvail() 7101 { 7102 ImGuiWindow* window = GetCurrentWindowRead(); 7103 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos); 7104 } 7105 7106 float ImGui::GetContentRegionAvailWidth() 7107 { 7108 return GetContentRegionAvail().x; 7109 } 7110 7111 // In window space (not screen space!) 7112 ImVec2 ImGui::GetWindowContentRegionMin() 7113 { 7114 ImGuiWindow* window = GetCurrentWindowRead(); 7115 return window->ContentsRegionRect.Min; 7116 } 7117 7118 ImVec2 ImGui::GetWindowContentRegionMax() 7119 { 7120 ImGuiWindow* window = GetCurrentWindowRead(); 7121 return window->ContentsRegionRect.Max; 7122 } 7123 7124 float ImGui::GetWindowContentRegionWidth() 7125 { 7126 ImGuiWindow* window = GetCurrentWindowRead(); 7127 return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x; 7128 } 7129 7130 float ImGui::GetTextLineHeight() 7131 { 7132 ImGuiContext& g = *GImGui; 7133 return g.FontSize; 7134 } 7135 7136 float ImGui::GetTextLineHeightWithSpacing() 7137 { 7138 ImGuiContext& g = *GImGui; 7139 return g.FontSize + g.Style.ItemSpacing.y; 7140 } 7141 7142 float ImGui::GetFrameHeight() 7143 { 7144 ImGuiContext& g = *GImGui; 7145 return g.FontSize + g.Style.FramePadding.y * 2.0f; 7146 } 7147 7148 float ImGui::GetFrameHeightWithSpacing() 7149 { 7150 ImGuiContext& g = *GImGui; 7151 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; 6642 ImGuiContext& g = *GImGui; 6643 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha; 6644 g.NextWindowData.BgAlphaVal = alpha; 7152 6645 } 7153 6646 7154 6647 ImDrawList* ImGui::GetWindowDrawList() 7155 6648 { 7156 ImGuiWindow* window = GetCurrentWindow();7157 return window->DrawList;6649 ImGuiWindow* window = GetCurrentWindow(); 6650 return window->DrawList; 7158 6651 } 7159 6652 7160 6653 ImFont* ImGui::GetFont() 7161 6654 { 7162 return GImGui->Font;6655 return GImGui->Font; 7163 6656 } 7164 6657 7165 6658 float ImGui::GetFontSize() 7166 6659 { 7167 return GImGui->FontSize;6660 return GImGui->FontSize; 7168 6661 } 7169 6662 7170 6663 ImVec2 ImGui::GetFontTexUvWhitePixel() 7171 6664 { 7172 return GImGui->DrawListSharedData.TexUvWhitePixel;6665 return GImGui->DrawListSharedData.TexUvWhitePixel; 7173 6666 } 7174 6667 7175 6668 void ImGui::SetWindowFontScale(float scale) 7176 6669 { 7177 ImGuiContext& g = *GImGui; 7178 ImGuiWindow* window = GetCurrentWindow(); 7179 window->FontWindowScale = scale; 7180 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 6670 IM_ASSERT(scale > 0.0f); 6671 ImGuiContext& g = *GImGui; 6672 ImGuiWindow* window = GetCurrentWindow(); 6673 window->FontWindowScale = scale; 6674 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 6675 } 6676 6677 void ImGui::ActivateItem(ImGuiID id) 6678 { 6679 ImGuiContext& g = *GImGui; 6680 g.NavNextActivateId = id; 6681 } 6682 6683 // Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there. 6684 void ImGui::PushFocusScope(ImGuiID id) 6685 { 6686 ImGuiContext& g = *GImGui; 6687 ImGuiWindow* window = g.CurrentWindow; 6688 window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent); 6689 window->DC.NavFocusScopeIdCurrent = id; 6690 } 6691 6692 void ImGui::PopFocusScope() 6693 { 6694 ImGuiContext& g = *GImGui; 6695 ImGuiWindow* window = g.CurrentWindow; 6696 window->DC.NavFocusScopeIdCurrent = window->IDStack.back(); 6697 window->IDStack.pop_back(); 6698 } 6699 6700 void ImGui::SetKeyboardFocusHere(int offset) 6701 { 6702 IM_ASSERT(offset >= -1); // -1 is allowed but not below 6703 ImGuiContext& g = *GImGui; 6704 ImGuiWindow* window = g.CurrentWindow; 6705 g.FocusRequestNextWindow = window; 6706 g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; 6707 g.FocusRequestNextCounterTabStop = INT_MAX; 6708 } 6709 6710 void ImGui::SetItemDefaultFocus() 6711 { 6712 ImGuiContext& g = *GImGui; 6713 ImGuiWindow* window = g.CurrentWindow; 6714 if (!window->Appearing) 6715 return; 6716 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) 6717 { 6718 g.NavInitRequest = false; 6719 g.NavInitResultId = g.NavWindow->DC.LastItemId; 6720 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); 6721 NavUpdateAnyRequestFlag(); 6722 if (!IsItemVisible()) 6723 SetScrollHereY(); 6724 } 6725 } 6726 6727 void ImGui::SetStateStorage(ImGuiStorage* tree) 6728 { 6729 ImGuiWindow* window = GImGui->CurrentWindow; 6730 window->DC.StateStorage = tree ? tree : &window->StateStorage; 6731 } 6732 6733 ImGuiStorage* ImGui::GetStateStorage() 6734 { 6735 ImGuiWindow* window = GImGui->CurrentWindow; 6736 return window->DC.StateStorage; 6737 } 6738 6739 void ImGui::PushID(const char* str_id) 6740 { 6741 ImGuiContext& g = *GImGui; 6742 ImGuiWindow* window = g.CurrentWindow; 6743 ImGuiID id = window->GetIDNoKeepAlive(str_id); 6744 window->IDStack.push_back(id); 6745 } 6746 6747 void ImGui::PushID(const char* str_id_begin, const char* str_id_end) 6748 { 6749 ImGuiContext& g = *GImGui; 6750 ImGuiWindow* window = g.CurrentWindow; 6751 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end); 6752 window->IDStack.push_back(id); 6753 } 6754 6755 void ImGui::PushID(const void* ptr_id) 6756 { 6757 ImGuiContext& g = *GImGui; 6758 ImGuiWindow* window = g.CurrentWindow; 6759 ImGuiID id = window->GetIDNoKeepAlive(ptr_id); 6760 window->IDStack.push_back(id); 6761 } 6762 6763 void ImGui::PushID(int int_id) 6764 { 6765 ImGuiContext& g = *GImGui; 6766 ImGuiWindow* window = g.CurrentWindow; 6767 ImGuiID id = window->GetIDNoKeepAlive(int_id); 6768 window->IDStack.push_back(id); 6769 } 6770 6771 // Push a given id value ignoring the ID stack as a seed. 6772 void ImGui::PushOverrideID(ImGuiID id) 6773 { 6774 ImGuiContext& g = *GImGui; 6775 ImGuiWindow* window = g.CurrentWindow; 6776 window->IDStack.push_back(id); 6777 } 6778 6779 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call 6780 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level. 6781 // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more) 6782 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) 6783 { 6784 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 6785 ImGui::KeepAliveID(id); 6786 #ifdef IMGUI_ENABLE_TEST_ENGINE 6787 ImGuiContext& g = *GImGui; 6788 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 6789 #endif 6790 return id; 6791 } 6792 6793 void ImGui::PopID() 6794 { 6795 ImGuiWindow* window = GImGui->CurrentWindow; 6796 window->IDStack.pop_back(); 6797 } 6798 6799 ImGuiID ImGui::GetID(const char* str_id) 6800 { 6801 ImGuiWindow* window = GImGui->CurrentWindow; 6802 return window->GetID(str_id); 6803 } 6804 6805 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) 6806 { 6807 ImGuiWindow* window = GImGui->CurrentWindow; 6808 return window->GetID(str_id_begin, str_id_end); 6809 } 6810 6811 ImGuiID ImGui::GetID(const void* ptr_id) 6812 { 6813 ImGuiWindow* window = GImGui->CurrentWindow; 6814 return window->GetID(ptr_id); 6815 } 6816 6817 bool ImGui::IsRectVisible(const ImVec2& size) 6818 { 6819 ImGuiWindow* window = GImGui->CurrentWindow; 6820 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); 6821 } 6822 6823 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) 6824 { 6825 ImGuiWindow* window = GImGui->CurrentWindow; 6826 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); 6827 } 6828 6829 6830 //----------------------------------------------------------------------------- 6831 // [SECTION] ERROR CHECKING 6832 //----------------------------------------------------------------------------- 6833 6834 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. 6835 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit 6836 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code 6837 // may see different structures than what imgui.cpp sees, which is problematic. 6838 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. 6839 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) 6840 { 6841 bool error = false; 6842 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } 6843 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } 6844 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } 6845 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } 6846 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } 6847 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } 6848 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } 6849 return !error; 6850 } 6851 6852 static void ImGui::ErrorCheckNewFrameSanityChecks() 6853 { 6854 ImGuiContext& g = *GImGui; 6855 6856 // Check user IM_ASSERT macro 6857 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined! 6858 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. 6859 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) 6860 // #define IM_ASSERT(EXPR) SomeCode(EXPR); SomeMoreCode(); // Wrong! 6861 // #define IM_ASSERT(EXPR) do { SomeCode(EXPR); SomeMoreCode(); } while (0) // Correct! 6862 if (true) IM_ASSERT(1); else IM_ASSERT(0); 6863 6864 // Check user data 6865 // (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) 6866 IM_ASSERT(g.Initialized); 6867 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); 6868 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); 6869 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); 6870 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 6871 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 6872 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); 6873 IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!"); 6874 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!"); 6875 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); 6876 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); 6877 for (int n = 0; n < ImGuiKey_COUNT; n++) 6878 IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); 6879 6880 // Perform simple 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 recently added in 1.60 WIP) 6881 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) 6882 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); 6883 6884 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. 6885 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) 6886 g.IO.ConfigWindowsResizeFromEdges = false; 6887 } 6888 6889 static void ImGui::ErrorCheckEndFrameSanityChecks() 6890 { 6891 ImGuiContext& g = *GImGui; 6892 6893 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() 6894 // One possible reason leading to this assert is that your back-ends update inputs _AFTER_ NewFrame(). 6895 const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags(); 6896 IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); 6897 IM_UNUSED(expected_key_mod_flags); 6898 6899 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you 6900 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). 6901 if (g.CurrentWindowStack.Size != 1) 6902 { 6903 if (g.CurrentWindowStack.Size > 1) 6904 { 6905 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); 6906 while (g.CurrentWindowStack.Size > 1) 6907 End(); 6908 } 6909 else 6910 { 6911 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); 6912 } 6913 } 6914 } 6915 6916 // Save and compare stack sizes on Begin()/End() to detect usage errors 6917 // Begin() calls this with write=true 6918 // End() calls this with write=false 6919 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write) 6920 { 6921 ImGuiContext& g = *GImGui; 6922 short* p = &window->DC.StackSizesBackup[0]; 6923 6924 // Window stacks 6925 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) 6926 { int n = window->IDStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!"); p++; } // Too few or too many PopID()/TreePop() 6927 { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!"); p++; } // Too few or too many EndGroup() 6928 6929 // Global stacks 6930 // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. 6931 { int n = g.BeginPopupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup() 6932 { int n = g.ColorModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!"); p++; } // Too few or too many PopStyleColor() 6933 { int n = g.StyleModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!"); p++; } // Too few or too many PopStyleVar() 6934 { int n = g.FontStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!"); p++; } // Too few or too many PopFont() 6935 IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); 6936 } 6937 6938 6939 //----------------------------------------------------------------------------- 6940 // [SECTION] LAYOUT 6941 //----------------------------------------------------------------------------- 6942 // - ItemSize() 6943 // - ItemAdd() 6944 // - SameLine() 6945 // - GetCursorScreenPos() 6946 // - SetCursorScreenPos() 6947 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY() 6948 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY() 6949 // - GetCursorStartPos() 6950 // - Indent() 6951 // - Unindent() 6952 // - SetNextItemWidth() 6953 // - PushItemWidth() 6954 // - PushMultiItemsWidths() 6955 // - PopItemWidth() 6956 // - CalcItemWidth() 6957 // - CalcItemSize() 6958 // - GetTextLineHeight() 6959 // - GetTextLineHeightWithSpacing() 6960 // - GetFrameHeight() 6961 // - GetFrameHeightWithSpacing() 6962 // - GetContentRegionMax() 6963 // - GetContentRegionMaxAbs() [Internal] 6964 // - GetContentRegionAvail(), 6965 // - GetWindowContentRegionMin(), GetWindowContentRegionMax() 6966 // - GetWindowContentRegionWidth() 6967 // - BeginGroup() 6968 // - EndGroup() 6969 // Also see in imgui_widgets: tab bars, columns. 6970 //----------------------------------------------------------------------------- 6971 6972 // Advance cursor given item size for layout. 6973 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. 6974 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. 6975 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) 6976 { 6977 ImGuiContext& g = *GImGui; 6978 ImGuiWindow* window = g.CurrentWindow; 6979 if (window->SkipItems) 6980 return; 6981 6982 // We increase the height in this function to accommodate for baseline offset. 6983 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor, 6984 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect. 6985 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f; 6986 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y); 6987 6988 // Always align ourselves on pixel boundaries 6989 //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] 6990 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; 6991 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y; 6992 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line 6993 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line 6994 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); 6995 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); 6996 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] 6997 6998 window->DC.PrevLineSize.y = line_height; 6999 window->DC.CurrLineSize.y = 0.0f; 7000 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); 7001 window->DC.CurrLineTextBaseOffset = 0.0f; 7002 7003 // Horizontal layout mode 7004 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 7005 SameLine(); 7006 } 7007 7008 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) 7009 { 7010 ItemSize(bb.GetSize(), text_baseline_y); 7011 } 7012 7013 // Declare item bounding box for clipping and interaction. 7014 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface 7015 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. 7016 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) 7017 { 7018 ImGuiContext& g = *GImGui; 7019 ImGuiWindow* window = g.CurrentWindow; 7020 7021 if (id != 0) 7022 { 7023 // Navigation processing runs prior to clipping early-out 7024 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget 7025 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests 7026 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of 7027 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. 7028 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able 7029 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). 7030 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. 7031 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. 7032 window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); 7033 if (g.NavId == id || g.NavAnyRequest) 7034 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) 7035 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) 7036 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); 7037 7038 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() 7039 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 7040 if (id == g.DebugItemPickerBreakId) 7041 { 7042 IM_DEBUG_BREAK(); 7043 g.DebugItemPickerBreakId = 0; 7044 } 7045 #endif 7046 } 7047 7048 // Equivalent to calling SetLastItemData() 7049 window->DC.LastItemId = id; 7050 window->DC.LastItemRect = bb; 7051 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; 7052 g.NextItemData.Flags = ImGuiNextItemDataFlags_None; 7053 7054 #ifdef IMGUI_ENABLE_TEST_ENGINE 7055 if (id != 0) 7056 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); 7057 #endif 7058 7059 // Clipping test 7060 const bool is_clipped = IsClippedEx(bb, id, false); 7061 if (is_clipped) 7062 return false; 7063 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] 7064 7065 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) 7066 if (IsMouseHoveringRect(bb.Min, bb.Max)) 7067 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; 7068 return true; 7069 } 7070 7071 // Gets back to previous line and continue with horizontal layout 7072 // offset_from_start_x == 0 : follow right after previous item 7073 // offset_from_start_x != 0 : align to specified x position (relative to window/group left) 7074 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 7075 // spacing_w >= 0 : enforce spacing amount 7076 void ImGui::SameLine(float offset_from_start_x, float spacing_w) 7077 { 7078 ImGuiWindow* window = GetCurrentWindow(); 7079 if (window->SkipItems) 7080 return; 7081 7082 ImGuiContext& g = *GImGui; 7083 if (offset_from_start_x != 0.0f) 7084 { 7085 if (spacing_w < 0.0f) spacing_w = 0.0f; 7086 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; 7087 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 7088 } 7089 else 7090 { 7091 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; 7092 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; 7093 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 7094 } 7095 window->DC.CurrLineSize = window->DC.PrevLineSize; 7096 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; 7097 } 7098 7099 ImVec2 ImGui::GetCursorScreenPos() 7100 { 7101 ImGuiWindow* window = GetCurrentWindowRead(); 7102 return window->DC.CursorPos; 7103 } 7104 7105 void ImGui::SetCursorScreenPos(const ImVec2& pos) 7106 { 7107 ImGuiWindow* window = GetCurrentWindow(); 7108 window->DC.CursorPos = pos; 7109 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7181 7110 } 7182 7111 … … 7185 7114 ImVec2 ImGui::GetCursorPos() 7186 7115 { 7187 ImGuiWindow* window = GetCurrentWindowRead();7188 return window->DC.CursorPos - window->Pos + window->Scroll;7116 ImGuiWindow* window = GetCurrentWindowRead(); 7117 return window->DC.CursorPos - window->Pos + window->Scroll; 7189 7118 } 7190 7119 7191 7120 float ImGui::GetCursorPosX() 7192 7121 { 7193 ImGuiWindow* window = GetCurrentWindowRead();7194 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;7122 ImGuiWindow* window = GetCurrentWindowRead(); 7123 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; 7195 7124 } 7196 7125 7197 7126 float ImGui::GetCursorPosY() 7198 7127 { 7199 ImGuiWindow* window = GetCurrentWindowRead();7200 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;7128 ImGuiWindow* window = GetCurrentWindowRead(); 7129 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; 7201 7130 } 7202 7131 7203 7132 void ImGui::SetCursorPos(const ImVec2& local_pos) 7204 7133 { 7205 ImGuiWindow* window = GetCurrentWindow();7206 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;7207 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);7134 ImGuiWindow* window = GetCurrentWindow(); 7135 window->DC.CursorPos = window->Pos - window->Scroll + local_pos; 7136 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7208 7137 } 7209 7138 7210 7139 void ImGui::SetCursorPosX(float x) 7211 7140 { 7212 ImGuiWindow* window = GetCurrentWindow();7213 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;7214 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);7141 ImGuiWindow* window = GetCurrentWindow(); 7142 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; 7143 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); 7215 7144 } 7216 7145 7217 7146 void ImGui::SetCursorPosY(float y) 7218 7147 { 7219 ImGuiWindow* window = GetCurrentWindow();7220 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;7221 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);7148 ImGuiWindow* window = GetCurrentWindow(); 7149 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; 7150 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); 7222 7151 } 7223 7152 7224 7153 ImVec2 ImGui::GetCursorStartPos() 7225 7154 { 7226 ImGuiWindow* window = GetCurrentWindowRead(); 7227 return window->DC.CursorStartPos - window->Pos; 7228 } 7229 7230 ImVec2 ImGui::GetCursorScreenPos() 7231 { 7232 ImGuiWindow* window = GetCurrentWindowRead(); 7233 return window->DC.CursorPos; 7234 } 7235 7236 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) 7237 { 7238 ImGuiWindow* window = GetCurrentWindow(); 7239 window->DC.CursorPos = screen_pos; 7240 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7155 ImGuiWindow* window = GetCurrentWindowRead(); 7156 return window->DC.CursorStartPos - window->Pos; 7157 } 7158 7159 void ImGui::Indent(float indent_w) 7160 { 7161 ImGuiContext& g = *GImGui; 7162 ImGuiWindow* window = GetCurrentWindow(); 7163 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7164 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7165 } 7166 7167 void ImGui::Unindent(float indent_w) 7168 { 7169 ImGuiContext& g = *GImGui; 7170 ImGuiWindow* window = GetCurrentWindow(); 7171 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7172 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7173 } 7174 7175 // Affect large frame+labels widgets only. 7176 void ImGui::SetNextItemWidth(float item_width) 7177 { 7178 ImGuiContext& g = *GImGui; 7179 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth; 7180 g.NextItemData.Width = item_width; 7181 } 7182 7183 void ImGui::PushItemWidth(float item_width) 7184 { 7185 ImGuiContext& g = *GImGui; 7186 ImGuiWindow* window = g.CurrentWindow; 7187 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); 7188 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); 7189 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7190 } 7191 7192 void ImGui::PushMultiItemsWidths(int components, float w_full) 7193 { 7194 ImGuiContext& g = *GImGui; 7195 ImGuiWindow* window = g.CurrentWindow; 7196 const ImGuiStyle& style = g.Style; 7197 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); 7198 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); 7199 window->DC.ItemWidthStack.push_back(w_item_last); 7200 for (int i = 0; i < components - 1; i++) 7201 window->DC.ItemWidthStack.push_back(w_item_one); 7202 window->DC.ItemWidth = window->DC.ItemWidthStack.back(); 7203 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7204 } 7205 7206 void ImGui::PopItemWidth() 7207 { 7208 ImGuiWindow* window = GetCurrentWindow(); 7209 window->DC.ItemWidthStack.pop_back(); 7210 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); 7211 } 7212 7213 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). 7214 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() 7215 float ImGui::CalcItemWidth() 7216 { 7217 ImGuiContext& g = *GImGui; 7218 ImGuiWindow* window = g.CurrentWindow; 7219 float w; 7220 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) 7221 w = g.NextItemData.Width; 7222 else 7223 w = window->DC.ItemWidth; 7224 if (w < 0.0f) 7225 { 7226 float region_max_x = GetContentRegionMaxAbs().x; 7227 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); 7228 } 7229 w = IM_FLOOR(w); 7230 return w; 7231 } 7232 7233 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). 7234 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. 7235 // Note that only CalcItemWidth() is publicly exposed. 7236 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) 7237 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) 7238 { 7239 ImGuiWindow* window = GImGui->CurrentWindow; 7240 7241 ImVec2 region_max; 7242 if (size.x < 0.0f || size.y < 0.0f) 7243 region_max = GetContentRegionMaxAbs(); 7244 7245 if (size.x == 0.0f) 7246 size.x = default_w; 7247 else if (size.x < 0.0f) 7248 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); 7249 7250 if (size.y == 0.0f) 7251 size.y = default_h; 7252 else if (size.y < 0.0f) 7253 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); 7254 7255 return size; 7256 } 7257 7258 float ImGui::GetTextLineHeight() 7259 { 7260 ImGuiContext& g = *GImGui; 7261 return g.FontSize; 7262 } 7263 7264 float ImGui::GetTextLineHeightWithSpacing() 7265 { 7266 ImGuiContext& g = *GImGui; 7267 return g.FontSize + g.Style.ItemSpacing.y; 7268 } 7269 7270 float ImGui::GetFrameHeight() 7271 { 7272 ImGuiContext& g = *GImGui; 7273 return g.FontSize + g.Style.FramePadding.y * 2.0f; 7274 } 7275 7276 float ImGui::GetFrameHeightWithSpacing() 7277 { 7278 ImGuiContext& g = *GImGui; 7279 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; 7280 } 7281 7282 // FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! 7283 7284 // FIXME: This is in window space (not screen space!). 7285 ImVec2 ImGui::GetContentRegionMax() 7286 { 7287 ImGuiContext& g = *GImGui; 7288 ImGuiWindow* window = g.CurrentWindow; 7289 ImVec2 mx = window->ContentRegionRect.Max - window->Pos; 7290 if (window->DC.CurrentColumns) 7291 mx.x = window->WorkRect.Max.x - window->Pos.x; 7292 return mx; 7293 } 7294 7295 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. 7296 ImVec2 ImGui::GetContentRegionMaxAbs() 7297 { 7298 ImGuiContext& g = *GImGui; 7299 ImGuiWindow* window = g.CurrentWindow; 7300 ImVec2 mx = window->ContentRegionRect.Max; 7301 if (window->DC.CurrentColumns) 7302 mx.x = window->WorkRect.Max.x; 7303 return mx; 7304 } 7305 7306 ImVec2 ImGui::GetContentRegionAvail() 7307 { 7308 ImGuiWindow* window = GImGui->CurrentWindow; 7309 return GetContentRegionMaxAbs() - window->DC.CursorPos; 7310 } 7311 7312 // In window space (not screen space!) 7313 ImVec2 ImGui::GetWindowContentRegionMin() 7314 { 7315 ImGuiWindow* window = GImGui->CurrentWindow; 7316 return window->ContentRegionRect.Min - window->Pos; 7317 } 7318 7319 ImVec2 ImGui::GetWindowContentRegionMax() 7320 { 7321 ImGuiWindow* window = GImGui->CurrentWindow; 7322 return window->ContentRegionRect.Max - window->Pos; 7323 } 7324 7325 float ImGui::GetWindowContentRegionWidth() 7326 { 7327 ImGuiWindow* window = GImGui->CurrentWindow; 7328 return window->ContentRegionRect.GetWidth(); 7329 } 7330 7331 // 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.) 7332 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. 7333 void ImGui::BeginGroup() 7334 { 7335 ImGuiContext& g = *GImGui; 7336 ImGuiWindow* window = g.CurrentWindow; 7337 7338 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); 7339 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 7340 group_data.BackupCursorPos = window->DC.CursorPos; 7341 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; 7342 group_data.BackupIndent = window->DC.Indent; 7343 group_data.BackupGroupOffset = window->DC.GroupOffset; 7344 group_data.BackupCurrLineSize = window->DC.CurrLineSize; 7345 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; 7346 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; 7347 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; 7348 group_data.EmitItem = true; 7349 7350 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; 7351 window->DC.Indent = window->DC.GroupOffset; 7352 window->DC.CursorMaxPos = window->DC.CursorPos; 7353 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); 7354 if (g.LogEnabled) 7355 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return 7356 } 7357 7358 void ImGui::EndGroup() 7359 { 7360 ImGuiContext& g = *GImGui; 7361 ImGuiWindow* window = g.CurrentWindow; 7362 IM_ASSERT(window->DC.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls 7363 7364 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 7365 7366 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); 7367 7368 window->DC.CursorPos = group_data.BackupCursorPos; 7369 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); 7370 window->DC.Indent = group_data.BackupIndent; 7371 window->DC.GroupOffset = group_data.BackupGroupOffset; 7372 window->DC.CurrLineSize = group_data.BackupCurrLineSize; 7373 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; 7374 if (g.LogEnabled) 7375 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return 7376 7377 if (!group_data.EmitItem) 7378 { 7379 window->DC.GroupStack.pop_back(); 7380 return; 7381 } 7382 7383 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. 7384 ItemSize(group_bb.GetSize()); 7385 ItemAdd(group_bb, 0); 7386 7387 // 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. 7388 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. 7389 // Also if you grep for LastItemId you'll notice it is only used in that context. 7390 // (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.) 7391 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; 7392 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); 7393 if (group_contains_curr_active_id) 7394 window->DC.LastItemId = g.ActiveId; 7395 else if (group_contains_prev_active_id) 7396 window->DC.LastItemId = g.ActiveIdPreviousFrame; 7397 window->DC.LastItemRect = group_bb; 7398 7399 // Forward Edited flag 7400 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) 7401 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 7402 7403 // Forward Deactivated flag 7404 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; 7405 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) 7406 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; 7407 7408 window->DC.GroupStack.pop_back(); 7409 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] 7410 } 7411 7412 7413 //----------------------------------------------------------------------------- 7414 // [SECTION] SCROLLING 7415 //----------------------------------------------------------------------------- 7416 7417 // Helper to snap on edges when aiming at an item very close to the edge, 7418 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. 7419 // When we refactor the scrolling API this may be configurable with a flag? 7420 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. 7421 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) 7422 { 7423 if (target <= snap_min + snap_threshold) 7424 return ImLerp(snap_min, target, center_ratio); 7425 if (target >= snap_max - snap_threshold) 7426 return ImLerp(target, snap_max, center_ratio); 7427 return target; 7428 } 7429 7430 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) 7431 { 7432 ImVec2 scroll = window->Scroll; 7433 if (window->ScrollTarget.x < FLT_MAX) 7434 { 7435 float center_x_ratio = window->ScrollTargetCenterRatio.x; 7436 float scroll_target_x = window->ScrollTarget.x; 7437 float snap_x_min = 0.0f; 7438 float snap_x_max = window->ScrollMax.x + window->Size.x; 7439 if (window->ScrollTargetEdgeSnapDist.x > 0.0f) 7440 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); 7441 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x); 7442 } 7443 if (window->ScrollTarget.y < FLT_MAX) 7444 { 7445 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 7446 float center_y_ratio = window->ScrollTargetCenterRatio.y; 7447 float scroll_target_y = window->ScrollTarget.y; 7448 float snap_y_min = 0.0f; 7449 float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height; 7450 if (window->ScrollTargetEdgeSnapDist.y > 0.0f) 7451 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); 7452 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); 7453 } 7454 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); 7455 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); 7456 if (!window->Collapsed && !window->SkipItems) 7457 { 7458 scroll.x = ImMin(scroll.x, window->ScrollMax.x); 7459 scroll.y = ImMin(scroll.y, window->ScrollMax.y); 7460 } 7461 return scroll; 7462 } 7463 7464 // Scroll to keep newly navigated item fully into view 7465 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) 7466 { 7467 ImGuiContext& g = *GImGui; 7468 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); 7469 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] 7470 7471 ImVec2 delta_scroll; 7472 if (!window_rect.Contains(item_rect)) 7473 { 7474 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) 7475 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); 7476 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) 7477 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); 7478 if (item_rect.Min.y < window_rect.Min.y) 7479 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); 7480 else if (item_rect.Max.y >= window_rect.Max.y) 7481 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); 7482 7483 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); 7484 delta_scroll = next_scroll - window->Scroll; 7485 } 7486 7487 // Also scroll parent window to keep us into view if necessary 7488 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7489 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); 7490 7491 return delta_scroll; 7241 7492 } 7242 7493 7243 7494 float ImGui::GetScrollX() 7244 7495 { 7245 return GImGui->CurrentWindow->Scroll.x; 7496 ImGuiWindow* window = GImGui->CurrentWindow; 7497 return window->Scroll.x; 7246 7498 } 7247 7499 7248 7500 float ImGui::GetScrollY() 7249 7501 { 7250 return GImGui->CurrentWindow->Scroll.y; 7502 ImGuiWindow* window = GImGui->CurrentWindow; 7503 return window->Scroll.y; 7251 7504 } 7252 7505 7253 7506 float ImGui::GetScrollMaxX() 7254 7507 { 7255 return GetScrollMaxX(GImGui->CurrentWindow); 7508 ImGuiWindow* window = GImGui->CurrentWindow; 7509 return window->ScrollMax.x; 7256 7510 } 7257 7511 7258 7512 float ImGui::GetScrollMaxY() 7259 7513 { 7260 return GetScrollMaxY(GImGui->CurrentWindow); 7514 ImGuiWindow* window = GImGui->CurrentWindow; 7515 return window->ScrollMax.y; 7516 } 7517 7518 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x) 7519 { 7520 window->ScrollTarget.x = scroll_x; 7521 window->ScrollTargetCenterRatio.x = 0.0f; 7522 window->ScrollTargetEdgeSnapDist.x = 0.0f; 7523 } 7524 7525 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y) 7526 { 7527 window->ScrollTarget.y = scroll_y; 7528 window->ScrollTargetCenterRatio.y = 0.0f; 7529 window->ScrollTargetEdgeSnapDist.y = 0.0f; 7261 7530 } 7262 7531 7263 7532 void ImGui::SetScrollX(float scroll_x) 7264 7533 { 7265 ImGuiWindow* window = GetCurrentWindow(); 7266 window->ScrollTarget.x = scroll_x; 7267 window->ScrollTargetCenterRatio.x = 0.0f; 7534 ImGuiContext& g = *GImGui; 7535 SetScrollX(g.CurrentWindow, scroll_x); 7268 7536 } 7269 7537 7270 7538 void ImGui::SetScrollY(float scroll_y) 7271 7539 { 7272 ImGuiWindow* window = GetCurrentWindow(); 7273 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY 7274 window->ScrollTargetCenterRatio.y = 0.0f; 7275 } 7276 7277 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) 7278 { 7279 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size 7280 ImGuiWindow* window = GetCurrentWindow(); 7281 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); 7282 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); 7283 window->ScrollTargetCenterRatio.y = center_y_ratio; 7284 7285 // Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way 7286 if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) 7287 window->ScrollTarget.y = 0.0f; 7288 else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y) 7289 window->ScrollTarget.y = window->SizeContents.y; 7540 ImGuiContext& g = *GImGui; 7541 SetScrollY(g.CurrentWindow, scroll_y); 7542 } 7543 7544 // Note that a local position will vary depending on initial scroll value, 7545 // This is a little bit confusing so bear with us: 7546 // - local_pos = (absolution_pos - window->Pos) 7547 // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, 7548 // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. 7549 // - They mostly exists because of legacy API. 7550 // Following the rules above, when trying to work with scrolling code, consider that: 7551 // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! 7552 // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense 7553 // 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 7554 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) 7555 { 7556 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); 7557 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset 7558 window->ScrollTargetCenterRatio.x = center_x_ratio; 7559 window->ScrollTargetEdgeSnapDist.x = 0.0f; 7560 } 7561 7562 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) 7563 { 7564 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); 7565 local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect 7566 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset 7567 window->ScrollTargetCenterRatio.y = center_y_ratio; 7568 window->ScrollTargetEdgeSnapDist.y = 0.0f; 7569 } 7570 7571 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) 7572 { 7573 ImGuiContext& g = *GImGui; 7574 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio); 7575 } 7576 7577 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) 7578 { 7579 ImGuiContext& g = *GImGui; 7580 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); 7581 } 7582 7583 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. 7584 void ImGui::SetScrollHereX(float center_x_ratio) 7585 { 7586 ImGuiContext& g = *GImGui; 7587 ImGuiWindow* window = g.CurrentWindow; 7588 float spacing_x = g.Style.ItemSpacing.x; 7589 float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); 7590 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos 7591 7592 // Tweak: snap on edges when aiming at an item very close to the edge 7593 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x); 7290 7594 } 7291 7595 7292 7596 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. 7293 void ImGui::SetScrollHere(float center_y_ratio) 7294 { 7295 ImGuiWindow* window = GetCurrentWindow(); 7296 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space 7297 target_y += (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. 7298 SetScrollFromPosY(target_y, center_y_ratio); 7299 } 7300 7301 void ImGui::ActivateItem(ImGuiID id) 7302 { 7303 ImGuiContext& g = *GImGui; 7304 g.NavNextActivateId = id; 7305 } 7306 7307 void ImGui::SetKeyboardFocusHere(int offset) 7308 { 7309 IM_ASSERT(offset >= -1); // -1 is allowed but not below 7310 ImGuiWindow* window = GetCurrentWindow(); 7311 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; 7312 window->FocusIdxTabRequestNext = INT_MAX; 7313 } 7314 7315 void ImGui::SetItemDefaultFocus() 7316 { 7317 ImGuiContext& g = *GImGui; 7318 ImGuiWindow* window = g.CurrentWindow; 7319 if (!window->Appearing) 7320 return; 7321 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) 7322 { 7323 g.NavInitRequest = false; 7324 g.NavInitResultId = g.NavWindow->DC.LastItemId; 7325 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); 7326 NavUpdateAnyRequestFlag(); 7327 if (!IsItemVisible()) 7328 SetScrollHere(); 7329 } 7330 } 7331 7332 void ImGui::SetStateStorage(ImGuiStorage* tree) 7333 { 7334 ImGuiWindow* window = GetCurrentWindow(); 7335 window->DC.StateStorage = tree ? tree : &window->StateStorage; 7336 } 7337 7338 ImGuiStorage* ImGui::GetStateStorage() 7339 { 7340 ImGuiWindow* window = GetCurrentWindowRead(); 7341 return window->DC.StateStorage; 7342 } 7343 7344 void ImGui::TextV(const char* fmt, va_list args) 7345 { 7346 ImGuiWindow* window = GetCurrentWindow(); 7347 if (window->SkipItems) 7348 return; 7349 7350 ImGuiContext& g = *GImGui; 7351 const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); 7352 TextUnformatted(g.TempBuffer, text_end); 7353 } 7354 7355 void ImGui::Text(const char* fmt, ...) 7356 { 7357 va_list args; 7358 va_start(args, fmt); 7359 TextV(fmt, args); 7360 va_end(args); 7361 } 7362 7363 void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) 7364 { 7365 PushStyleColor(ImGuiCol_Text, col); 7366 TextV(fmt, args); 7367 PopStyleColor(); 7368 } 7369 7370 void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) 7371 { 7372 va_list args; 7373 va_start(args, fmt); 7374 TextColoredV(col, fmt, args); 7375 va_end(args); 7376 } 7377 7378 void ImGui::TextDisabledV(const char* fmt, va_list args) 7379 { 7380 PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); 7381 TextV(fmt, args); 7382 PopStyleColor(); 7383 } 7384 7385 void ImGui::TextDisabled(const char* fmt, ...) 7386 { 7387 va_list args; 7388 va_start(args, fmt); 7389 TextDisabledV(fmt, args); 7390 va_end(args); 7391 } 7392 7393 void ImGui::TextWrappedV(const char* fmt, va_list args) 7394 { 7395 bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set 7396 if (need_wrap) PushTextWrapPos(0.0f); 7397 TextV(fmt, args); 7398 if (need_wrap) PopTextWrapPos(); 7399 } 7400 7401 void ImGui::TextWrapped(const char* fmt, ...) 7402 { 7403 va_list args; 7404 va_start(args, fmt); 7405 TextWrappedV(fmt, args); 7406 va_end(args); 7407 } 7408 7409 void ImGui::TextUnformatted(const char* text, const char* text_end) 7410 { 7411 ImGuiWindow* window = GetCurrentWindow(); 7412 if (window->SkipItems) 7413 return; 7414 7415 ImGuiContext& g = *GImGui; 7416 IM_ASSERT(text != NULL); 7417 const char* text_begin = text; 7418 if (text_end == NULL) 7419 text_end = text + strlen(text); // FIXME-OPT 7420 7421 const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); 7422 const float wrap_pos_x = window->DC.TextWrapPos; 7423 const bool wrap_enabled = wrap_pos_x >= 0.0f; 7424 if (text_end - text > 2000 && !wrap_enabled) 7425 { 7426 // Long text! 7427 // Perform manual coarse clipping to optimize for long multi-line text 7428 // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. 7429 // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. 7430 const char* line = text; 7431 const float line_height = GetTextLineHeight(); 7432 const ImRect clip_rect = window->ClipRect; 7433 ImVec2 text_size(0, 0); 7434 7435 if (text_pos.y <= clip_rect.Max.y) 7436 { 7437 ImVec2 pos = text_pos; 7438 7439 // Lines to skip (can't skip when logging text) 7440 if (!g.LogEnabled) 7441 { 7442 int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); 7443 if (lines_skippable > 0) 7597 void ImGui::SetScrollHereY(float center_y_ratio) 7598 { 7599 ImGuiContext& g = *GImGui; 7600 ImGuiWindow* window = g.CurrentWindow; 7601 float spacing_y = g.Style.ItemSpacing.y; 7602 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); 7603 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos 7604 7605 // Tweak: snap on edges when aiming at an item very close to the edge 7606 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y); 7607 } 7608 7609 //----------------------------------------------------------------------------- 7610 // [SECTION] TOOLTIPS 7611 //----------------------------------------------------------------------------- 7612 7613 void ImGui::BeginTooltip() 7614 { 7615 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); 7616 } 7617 7618 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) 7619 { 7620 ImGuiContext& g = *GImGui; 7621 7622 if (g.DragDropWithinSource || g.DragDropWithinTarget) 7623 { 7624 // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) 7625 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. 7626 // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. 7627 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; 7628 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); 7629 SetNextWindowPos(tooltip_pos); 7630 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); 7631 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( 7632 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; 7633 } 7634 7635 char window_name[16]; 7636 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); 7637 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) 7638 if (ImGuiWindow* window = FindWindowByName(window_name)) 7639 if (window->Active) 7444 7640 { 7445 int lines_skipped = 0; 7446 while (line < text_end && lines_skipped < lines_skippable) 7447 { 7448 const char* line_end = strchr(line, '\n'); 7449 if (!line_end) 7450 line_end = text_end; 7451 line = line_end + 1; 7452 lines_skipped++; 7453 } 7454 pos.y += lines_skipped * line_height; 7641 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. 7642 window->Hidden = true; 7643 window->HiddenFramesCanSkipItems = 1; 7644 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); 7455 7645 } 7456 } 7457 7458 // Lines to render 7459 if (line < text_end) 7460 { 7461 ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); 7462 while (line < text_end) 7646 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; 7647 Begin(window_name, NULL, flags | extra_flags); 7648 } 7649 7650 void ImGui::EndTooltip() 7651 { 7652 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls 7653 End(); 7654 } 7655 7656 void ImGui::SetTooltipV(const char* fmt, va_list args) 7657 { 7658 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); 7659 TextV(fmt, args); 7660 EndTooltip(); 7661 } 7662 7663 void ImGui::SetTooltip(const char* fmt, ...) 7664 { 7665 va_list args; 7666 va_start(args, fmt); 7667 SetTooltipV(fmt, args); 7668 va_end(args); 7669 } 7670 7671 //----------------------------------------------------------------------------- 7672 // [SECTION] POPUPS 7673 //----------------------------------------------------------------------------- 7674 7675 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel 7676 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags) 7677 { 7678 ImGuiContext& g = *GImGui; 7679 if (popup_flags & ImGuiPopupFlags_AnyPopupId) 7680 { 7681 // Return true if any popup is open at the current BeginPopup() level of the popup stack 7682 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level. 7683 IM_ASSERT(id == 0); 7684 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) 7685 return g.OpenPopupStack.Size > 0; 7686 else 7687 return g.OpenPopupStack.Size > g.BeginPopupStack.Size; 7688 } 7689 else 7690 { 7691 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) 7692 { 7693 // Return true if the popup is open anywhere in the popup stack 7694 for (int n = 0; n < g.OpenPopupStack.Size; n++) 7695 if (g.OpenPopupStack[n].PopupId == id) 7696 return true; 7697 return false; 7698 } 7699 else 7700 { 7701 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query) 7702 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; 7703 } 7704 } 7705 } 7706 7707 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags) 7708 { 7709 ImGuiContext& g = *GImGui; 7710 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id); 7711 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0) 7712 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally 7713 return IsPopupOpen(id, popup_flags); 7714 } 7715 7716 ImGuiWindow* ImGui::GetTopMostPopupModal() 7717 { 7718 ImGuiContext& g = *GImGui; 7719 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) 7720 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) 7721 if (popup->Flags & ImGuiWindowFlags_Modal) 7722 return popup; 7723 return NULL; 7724 } 7725 7726 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) 7727 { 7728 ImGuiContext& g = *GImGui; 7729 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags); 7730 } 7731 7732 // Mark popup as open (toggle toward open state). 7733 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. 7734 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). 7735 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) 7736 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) 7737 { 7738 ImGuiContext& g = *GImGui; 7739 ImGuiWindow* parent_window = g.CurrentWindow; 7740 const int current_stack_size = g.BeginPopupStack.Size; 7741 7742 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) 7743 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId)) 7744 return; 7745 7746 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. 7747 popup_ref.PopupId = id; 7748 popup_ref.Window = NULL; 7749 popup_ref.SourceWindow = g.NavWindow; 7750 popup_ref.OpenFrameCount = g.FrameCount; 7751 popup_ref.OpenParentId = parent_window->IDStack.back(); 7752 popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); 7753 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; 7754 7755 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id); 7756 if (g.OpenPopupStack.Size < current_stack_size + 1) 7757 { 7758 g.OpenPopupStack.push_back(popup_ref); 7759 } 7760 else 7761 { 7762 // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui 7763 // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing 7764 // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. 7765 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) 7766 { 7767 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; 7768 } 7769 else 7770 { 7771 // Close child popups if any, then flag popup for open/reopen 7772 ClosePopupToLevel(current_stack_size, false); 7773 g.OpenPopupStack.push_back(popup_ref); 7774 } 7775 7776 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). 7777 // This is equivalent to what ClosePopupToLevel() does. 7778 //if (g.OpenPopupStack[current_stack_size].PopupId == id) 7779 // FocusWindow(parent_window); 7780 } 7781 } 7782 7783 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. 7784 // This function closes any popups that are over 'ref_window'. 7785 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) 7786 { 7787 ImGuiContext& g = *GImGui; 7788 if (g.OpenPopupStack.Size == 0) 7789 return; 7790 7791 // Don't close our own child popup windows. 7792 int popup_count_to_keep = 0; 7793 if (ref_window) 7794 { 7795 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) 7796 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) 7797 { 7798 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; 7799 if (!popup.Window) 7800 continue; 7801 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); 7802 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) 7803 continue; 7804 7805 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) 7806 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3: 7807 // Window -> Popup1 -> Popup2 -> Popup3 7808 // - Each popups may contain child windows, which is why we compare ->RootWindow! 7809 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child 7810 bool ref_window_is_descendent_of_popup = false; 7811 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) 7812 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) 7813 if (popup_window->RootWindow == ref_window->RootWindow) 7814 { 7815 ref_window_is_descendent_of_popup = true; 7816 break; 7817 } 7818 if (!ref_window_is_descendent_of_popup) 7819 break; 7820 } 7821 } 7822 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 7823 { 7824 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); 7825 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); 7826 } 7827 } 7828 7829 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) 7830 { 7831 ImGuiContext& g = *GImGui; 7832 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); 7833 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); 7834 7835 // Trim open popup stack 7836 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; 7837 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; 7838 g.OpenPopupStack.resize(remaining); 7839 7840 if (restore_focus_to_window_under_popup) 7841 { 7842 if (focus_window && !focus_window->WasActive && popup_window) 7843 { 7844 // Fallback 7845 FocusTopMostWindowUnderOne(popup_window, NULL); 7846 } 7847 else 7848 { 7849 if (g.NavLayer == ImGuiNavLayer_Main && focus_window) 7850 focus_window = NavRestoreLastChildNavWindow(focus_window); 7851 FocusWindow(focus_window); 7852 } 7853 } 7854 } 7855 7856 // Close the popup we have begin-ed into. 7857 void ImGui::CloseCurrentPopup() 7858 { 7859 ImGuiContext& g = *GImGui; 7860 int popup_idx = g.BeginPopupStack.Size - 1; 7861 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) 7862 return; 7863 7864 // Closing a menu closes its top-most parent popup (unless a modal) 7865 while (popup_idx > 0) 7866 { 7867 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; 7868 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; 7869 bool close_parent = false; 7870 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) 7871 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) 7872 close_parent = true; 7873 if (!close_parent) 7874 break; 7875 popup_idx--; 7876 } 7877 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); 7878 ClosePopupToLevel(popup_idx, true); 7879 7880 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. 7881 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. 7882 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. 7883 if (ImGuiWindow* window = g.NavWindow) 7884 window->DC.NavHideHighlightOneFrame = true; 7885 } 7886 7887 // Attention! BeginPopup() adds default flags which BeginPopupEx()! 7888 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) 7889 { 7890 ImGuiContext& g = *GImGui; 7891 if (!IsPopupOpen(id, ImGuiPopupFlags_None)) 7892 { 7893 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7894 return false; 7895 } 7896 7897 char name[20]; 7898 if (flags & ImGuiWindowFlags_ChildMenu) 7899 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth 7900 else 7901 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame 7902 7903 flags |= ImGuiWindowFlags_Popup; 7904 bool is_open = Begin(name, NULL, flags); 7905 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) 7906 EndPopup(); 7907 7908 return is_open; 7909 } 7910 7911 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) 7912 { 7913 ImGuiContext& g = *GImGui; 7914 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance 7915 { 7916 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7917 return false; 7918 } 7919 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; 7920 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); 7921 } 7922 7923 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. 7924 // Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. 7925 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) 7926 { 7927 ImGuiContext& g = *GImGui; 7928 ImGuiWindow* window = g.CurrentWindow; 7929 const ImGuiID id = window->GetID(name); 7930 if (!IsPopupOpen(id, ImGuiPopupFlags_None)) 7931 { 7932 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7933 return false; 7934 } 7935 7936 // Center modal windows by default for increased visibility 7937 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) 7938 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. 7939 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) 7940 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); 7941 7942 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; 7943 const bool is_open = Begin(name, p_open, flags); 7944 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) 7945 { 7946 EndPopup(); 7947 if (is_open) 7948 ClosePopupToLevel(g.BeginPopupStack.Size, true); 7949 return false; 7950 } 7951 return is_open; 7952 } 7953 7954 void ImGui::EndPopup() 7955 { 7956 ImGuiContext& g = *GImGui; 7957 ImGuiWindow* window = g.CurrentWindow; 7958 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls 7959 IM_ASSERT(g.BeginPopupStack.Size > 0); 7960 7961 // Make all menus and popups wrap around for now, may need to expose that policy. 7962 if (g.NavWindow == window) 7963 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); 7964 7965 // Child-popups don't need to be laid out 7966 IM_ASSERT(g.WithinEndChild == false); 7967 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7968 g.WithinEndChild = true; 7969 End(); 7970 g.WithinEndChild = false; 7971 } 7972 7973 // Helper to open a popup if mouse button is released over the item 7974 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() 7975 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) 7976 { 7977 ImGuiWindow* window = GImGui->CurrentWindow; 7978 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 7979 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 7980 { 7981 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! 7982 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 7983 OpenPopupEx(id, popup_flags); 7984 } 7985 } 7986 7987 // This is a helper to handle the simplest case of associating one named popup to one given widget. 7988 // - You can pass a NULL str_id to use the identifier of the last item. 7989 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). 7990 // - This is essentially the same as calling OpenPopupOnItemClick() + BeginPopup() but written to avoid 7991 // computing the ID twice because BeginPopupContextXXX functions may be called very frequently. 7992 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) 7993 { 7994 ImGuiWindow* window = GImGui->CurrentWindow; 7995 if (window->SkipItems) 7996 return false; 7997 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! 7998 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 7999 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8000 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 8001 OpenPopupEx(id, popup_flags); 8002 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8003 } 8004 8005 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) 8006 { 8007 ImGuiWindow* window = GImGui->CurrentWindow; 8008 if (!str_id) 8009 str_id = "window_context"; 8010 ImGuiID id = window->GetID(str_id); 8011 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8012 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 8013 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) 8014 OpenPopupEx(id, popup_flags); 8015 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8016 } 8017 8018 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) 8019 { 8020 ImGuiWindow* window = GImGui->CurrentWindow; 8021 if (!str_id) 8022 str_id = "void_context"; 8023 ImGuiID id = window->GetID(str_id); 8024 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8025 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) 8026 if (GetTopMostPopupModal() == NULL) 8027 OpenPopupEx(id, popup_flags); 8028 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8029 } 8030 8031 // 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.) 8032 // 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. 8033 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) 8034 { 8035 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); 8036 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); 8037 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); 8038 8039 // Combo Box policy (we want a connecting edge) 8040 if (policy == ImGuiPopupPositionPolicy_ComboBox) 8041 { 8042 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; 8043 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 8044 { 8045 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 8046 if (n != -1 && dir == *last_dir) // Already tried this direction? 8047 continue; 8048 ImVec2 pos; 8049 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) 8050 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right 8051 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left 8052 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left 8053 if (!r_outer.Contains(ImRect(pos, pos + size))) 8054 continue; 8055 *last_dir = dir; 8056 return pos; 8057 } 8058 } 8059 8060 // Tooltip and Default popup policy 8061 // (Always first try the direction we used on the last frame, if any) 8062 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default) 8063 { 8064 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; 8065 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 8066 { 8067 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 8068 if (n != -1 && dir == *last_dir) // Already tried this direction? 8069 continue; 8070 8071 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); 8072 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); 8073 8074 // If there 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) 8075 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right)) 8076 continue; 8077 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down)) 8078 continue; 8079 8080 ImVec2 pos; 8081 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; 8082 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; 8083 8084 // Clamp top-left corner of popup 8085 pos.x = ImMax(pos.x, r_outer.Min.x); 8086 pos.y = ImMax(pos.y, r_outer.Min.y); 8087 8088 *last_dir = dir; 8089 return pos; 8090 } 8091 } 8092 8093 // Fallback when not enough room: 8094 *last_dir = ImGuiDir_None; 8095 8096 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. 8097 if (policy == ImGuiPopupPositionPolicy_Tooltip) 8098 return ref_pos + ImVec2(2, 2); 8099 8100 // Otherwise try to keep within display 8101 ImVec2 pos = ref_pos; 8102 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); 8103 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); 8104 return pos; 8105 } 8106 8107 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) 8108 { 8109 IM_UNUSED(window); 8110 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; 8111 ImRect r_screen = GetViewportRect(); 8112 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); 8113 return r_screen; 8114 } 8115 8116 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) 8117 { 8118 ImGuiContext& g = *GImGui; 8119 8120 ImRect r_outer = GetWindowAllowedExtentRect(window); 8121 if (window->Flags & ImGuiWindowFlags_ChildMenu) 8122 { 8123 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. 8124 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. 8125 IM_ASSERT(g.CurrentWindow == window); 8126 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; 8127 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). 8128 ImRect r_avoid; 8129 if (parent_window->DC.MenuBarAppending) 8130 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 8131 else 8132 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); 8133 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); 8134 } 8135 if (window->Flags & ImGuiWindowFlags_Popup) 8136 { 8137 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); 8138 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); 8139 } 8140 if (window->Flags & ImGuiWindowFlags_Tooltip) 8141 { 8142 // Position tooltip (always follows mouse) 8143 float sc = g.Style.MouseCursorScale; 8144 ImVec2 ref_pos = NavCalcPreferredRefPos(); 8145 ImRect r_avoid; 8146 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) 8147 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); 8148 else 8149 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. 8150 return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); 8151 } 8152 IM_ASSERT(0); 8153 return window->Pos; 8154 } 8155 8156 //----------------------------------------------------------------------------- 8157 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION 8158 //----------------------------------------------------------------------------- 8159 8160 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing, 8161 // and needs some explanation or serious refactoring. 8162 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id) 8163 { 8164 ImGuiContext& g = *GImGui; 8165 IM_ASSERT(g.NavWindow); 8166 IM_ASSERT(nav_layer == 0 || nav_layer == 1); 8167 g.NavId = id; 8168 g.NavFocusScopeId = focus_scope_id; 8169 g.NavWindow->NavLastIds[nav_layer] = id; 8170 } 8171 8172 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) 8173 { 8174 ImGuiContext& g = *GImGui; 8175 SetNavID(id, nav_layer, focus_scope_id); 8176 g.NavWindow->NavRectRel[nav_layer] = rect_rel; 8177 g.NavMousePosDirty = true; 8178 g.NavDisableHighlight = false; 8179 g.NavDisableMouseHover = true; 8180 } 8181 8182 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) 8183 { 8184 ImGuiContext& g = *GImGui; 8185 IM_ASSERT(id != 0); 8186 8187 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. 8188 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) 8189 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; 8190 if (g.NavWindow != window) 8191 g.NavInitRequest = false; 8192 g.NavWindow = window; 8193 g.NavId = id; 8194 g.NavLayer = nav_layer; 8195 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 8196 window->NavLastIds[nav_layer] = id; 8197 if (window->DC.LastItemId == id) 8198 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); 8199 8200 if (g.ActiveIdSource == ImGuiInputSource_Nav) 8201 g.NavDisableMouseHover = true; 8202 else 8203 g.NavDisableHighlight = true; 8204 } 8205 8206 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) 8207 { 8208 if (ImFabs(dx) > ImFabs(dy)) 8209 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; 8210 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; 8211 } 8212 8213 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) 8214 { 8215 if (a1 < b0) 8216 return a1 - b0; 8217 if (b1 < a0) 8218 return a0 - b1; 8219 return 0.0f; 8220 } 8221 8222 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) 8223 { 8224 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) 8225 { 8226 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); 8227 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); 8228 } 8229 else 8230 { 8231 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); 8232 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); 8233 } 8234 } 8235 8236 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 8237 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) 8238 { 8239 ImGuiContext& g = *GImGui; 8240 ImGuiWindow* window = g.CurrentWindow; 8241 if (g.NavLayer != window->DC.NavLayerCurrent) 8242 return false; 8243 8244 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) 8245 g.NavScoringCount++; 8246 8247 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring 8248 if (window->ParentWindow == g.NavWindow) 8249 { 8250 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); 8251 if (!window->ClipRect.Overlaps(cand)) 8252 return false; 8253 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window 8254 } 8255 8256 // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) 8257 // For example, this ensure that items in one column are not reached when moving vertically from items in another column. 8258 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); 8259 8260 // Compute distance between boxes 8261 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. 8262 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); 8263 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 8264 if (dby != 0.0f && dbx != 0.0f) 8265 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); 8266 float dist_box = ImFabs(dbx) + ImFabs(dby); 8267 8268 // 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) 8269 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); 8270 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); 8271 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) 8272 8273 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance 8274 ImGuiDir quadrant; 8275 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; 8276 if (dbx != 0.0f || dby != 0.0f) 8277 { 8278 // For non-overlapping boxes, use distance between boxes 8279 dax = dbx; 8280 day = dby; 8281 dist_axial = dist_box; 8282 quadrant = ImGetDirQuadrantFromDelta(dbx, dby); 8283 } 8284 else if (dcx != 0.0f || dcy != 0.0f) 8285 { 8286 // For overlapping boxes with different centers, use distance between centers 8287 dax = dcx; 8288 day = dcy; 8289 dist_axial = dist_center; 8290 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); 8291 } 8292 else 8293 { 8294 // 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) 8295 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; 8296 } 8297 8298 #if IMGUI_DEBUG_NAV_SCORING 8299 char buf[128]; 8300 if (IsMouseHoveringRect(cand.Min, cand.Max)) 8301 { 8302 ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); 8303 ImDrawList* draw_list = GetForegroundDrawList(window); 8304 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); 8305 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); 8306 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); 8307 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); 8308 } 8309 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. 8310 { 8311 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } 8312 if (quadrant == g.NavMoveDir) 8313 { 8314 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); 8315 ImDrawList* draw_list = GetForegroundDrawList(window); 8316 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); 8317 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); 8318 } 8319 } 8320 #endif 8321 8322 // Is it in the quadrant we're interesting in moving to? 8323 bool new_best = false; 8324 if (quadrant == g.NavMoveDir) 8325 { 8326 // Does it beat the current best candidate? 8327 if (dist_box < result->DistBox) 8328 { 8329 result->DistBox = dist_box; 8330 result->DistCenter = dist_center; 8331 return true; 8332 } 8333 if (dist_box == result->DistBox) 8334 { 8335 // Try using distance between center points to break ties 8336 if (dist_center < result->DistCenter) 7463 8337 { 7464 const char* line_end = strchr(line, '\n'); 7465 if (IsClippedEx(line_rect, 0, false)) 7466 break; 7467 7468 const ImVec2 line_size = CalcTextSize(line, line_end, false); 7469 text_size.x = ImMax(text_size.x, line_size.x); 7470 RenderText(pos, line, line_end, false); 7471 if (!line_end) 7472 line_end = text_end; 7473 line = line_end + 1; 7474 line_rect.Min.y += line_height; 7475 line_rect.Max.y += line_height; 7476 pos.y += line_height; 8338 result->DistCenter = dist_center; 8339 new_best = true; 7477 8340 } 7478 7479 // Count remaining lines 7480 int lines_skipped = 0; 7481 while (line < text_end) 8341 else if (dist_center == result->DistCenter) 7482 8342 { 7483 const char* line_end = strchr(line, '\n');7484 if (!line_end)7485 line_end = text_end;7486 line = line_end + 1;7487 lines_skipped++;8343 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items 8344 // (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), 8345 // 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. 8346 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance 8347 new_best = true; 7488 8348 } 7489 pos.y += lines_skipped * line_height; 7490 } 7491 7492 text_size.y += (pos - text_pos).y; 7493 } 7494 7495 ImRect bb(text_pos, text_pos + text_size); 7496 ItemSize(bb); 7497 ItemAdd(bb, 0); 7498 } 7499 else 7500 { 7501 const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; 7502 const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); 7503 7504 // Account of baseline offset 7505 ImRect bb(text_pos, text_pos + text_size); 7506 ItemSize(text_size); 7507 if (!ItemAdd(bb, 0)) 7508 return; 7509 7510 // Render (we don't hide text after ## in this end-user function) 7511 RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); 7512 } 7513 } 7514 7515 void ImGui::AlignTextToFramePadding() 7516 { 7517 ImGuiWindow* window = GetCurrentWindow(); 7518 if (window->SkipItems) 7519 return; 7520 7521 ImGuiContext& g = *GImGui; 7522 window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2); 7523 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); 7524 } 7525 7526 // Add a label+text combo aligned to other label+value widgets 7527 void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) 7528 { 7529 ImGuiWindow* window = GetCurrentWindow(); 7530 if (window->SkipItems) 7531 return; 7532 7533 ImGuiContext& g = *GImGui; 7534 const ImGuiStyle& style = g.Style; 7535 const float w = CalcItemWidth(); 7536 7537 const ImVec2 label_size = CalcTextSize(label, NULL, true); 7538 const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2)); 7539 const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y * 2) + label_size); 7540 ItemSize(total_bb, style.FramePadding.y); 7541 if (!ItemAdd(total_bb, 0)) 7542 return; 7543 7544 // Render 7545 const char* value_text_begin = &g.TempBuffer[0]; 7546 const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); 7547 RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f, 0.5f)); 7548 if (label_size.x > 0.0f) 7549 RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); 7550 } 7551 7552 void ImGui::LabelText(const char* label, const char* fmt, ...) 7553 { 7554 va_list args; 7555 va_start(args, fmt); 7556 LabelTextV(label, fmt, args); 7557 va_end(args); 7558 } 7559 7560 bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) 7561 { 7562 ImGuiContext& g = *GImGui; 7563 ImGuiWindow* window = GetCurrentWindow(); 7564 7565 if (flags & ImGuiButtonFlags_Disabled) 7566 { 7567 if (out_hovered) *out_hovered = false; 7568 if (out_held) *out_held = false; 7569 if (g.ActiveId == id) ClearActiveID(); 7570 return false; 7571 } 7572 7573 // Default behavior requires click+release on same spot 7574 if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) 7575 flags |= ImGuiButtonFlags_PressedOnClickRelease; 7576 7577 ImGuiWindow* backup_hovered_window = g.HoveredWindow; 7578 if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) 7579 g.HoveredWindow = window; 7580 7581 bool pressed = false; 7582 bool hovered = ItemHoverable(bb, id); 7583 7584 // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button 7585 if ((flags & ImGuiButtonFlags_PressedOnDragDropHold) && g.DragDropActive && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) 7586 if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 7587 { 7588 hovered = true; 7589 SetHoveredID(id); 7590 if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy 7591 { 7592 pressed = true; 7593 FocusWindow(window); 7594 } 7595 } 7596 7597 if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) 7598 g.HoveredWindow = backup_hovered_window; 7599 7600 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. 7601 if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) 7602 hovered = false; 7603 7604 // Mouse 7605 if (hovered) 7606 { 7607 if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) 7608 { 7609 // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat 7610 // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds 7611 // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> .. 7612 // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release) 7613 // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> .. 7614 // FIXME-NAV: We don't honor those different behaviors. 7615 if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) 7616 { 7617 SetActiveID(id, window); 7618 if (!(flags & ImGuiButtonFlags_NoNavFocus)) 7619 SetFocusID(id, window); 7620 FocusWindow(window); 7621 } 7622 if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) 7623 { 7624 pressed = true; 7625 if (flags & ImGuiButtonFlags_NoHoldingActiveID) 7626 ClearActiveID(); 7627 else 7628 SetActiveID(id, window); // Hold on ID 7629 FocusWindow(window); 7630 } 7631 if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) 7632 { 7633 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release> 7634 pressed = true; 7635 ClearActiveID(); 7636 } 7637 7638 // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). 7639 // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. 7640 if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) 7641 pressed = true; 7642 } 7643 7644 if (pressed) 7645 g.NavDisableHighlight = true; 7646 } 7647 7648 // Gamepad/Keyboard navigation 7649 // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. 7650 if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) 7651 hovered = true; 7652 7653 if (g.NavActivateDownId == id) 7654 { 7655 bool nav_activated_by_code = (g.NavActivateId == id); 7656 bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); 7657 if (nav_activated_by_code || nav_activated_by_inputs) 7658 pressed = true; 7659 if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) 7660 { 7661 // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. 7662 g.NavActivateId = id; // This is so SetActiveId assign a Nav source 7663 SetActiveID(id, window); 7664 if (!(flags & ImGuiButtonFlags_NoNavFocus)) 7665 SetFocusID(id, window); 7666 g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); 7667 } 7668 } 7669 7670 bool held = false; 7671 if (g.ActiveId == id) 7672 { 7673 if (g.ActiveIdSource == ImGuiInputSource_Mouse) 7674 { 7675 if (g.ActiveIdIsJustActivated) 7676 g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; 7677 if (g.IO.MouseDown[0]) 7678 { 7679 held = true; 7680 } 7681 else 7682 { 7683 if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) 7684 if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps <on release> 7685 if (!g.DragDropActive) 7686 pressed = true; 7687 ClearActiveID(); 7688 } 7689 if (!(flags & ImGuiButtonFlags_NoNavFocus)) 7690 g.NavDisableHighlight = true; 7691 } 7692 else if (g.ActiveIdSource == ImGuiInputSource_Nav) 7693 { 7694 if (g.NavActivateDownId != id) 7695 ClearActiveID(); 7696 } 7697 } 7698 7699 if (out_hovered) *out_hovered = hovered; 7700 if (out_held) *out_held = held; 7701 7702 return pressed; 7703 } 7704 7705 bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) 7706 { 7707 ImGuiWindow* window = GetCurrentWindow(); 7708 if (window->SkipItems) 7709 return false; 7710 7711 ImGuiContext& g = *GImGui; 7712 const ImGuiStyle& style = g.Style; 7713 const ImGuiID id = window->GetID(label); 7714 const ImVec2 label_size = CalcTextSize(label, NULL, true); 7715 7716 ImVec2 pos = window->DC.CursorPos; 7717 if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) 7718 pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; 7719 ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); 7720 7721 const ImRect bb(pos, pos + size); 7722 ItemSize(bb, style.FramePadding.y); 7723 if (!ItemAdd(bb, id)) 7724 return false; 7725 7726 if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) 7727 flags |= ImGuiButtonFlags_Repeat; 7728 bool hovered, held; 7729 bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); 7730 7731 // Render 7732 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 7733 RenderNavHighlight(bb, id); 7734 RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); 7735 RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); 7736 7737 // Automatically close popups 7738 //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) 7739 // CloseCurrentPopup(); 7740 7741 return pressed; 7742 } 7743 7744 bool ImGui::Button(const char* label, const ImVec2& size_arg) 7745 { 7746 return ButtonEx(label, size_arg, 0); 7747 } 7748 7749 // Small buttons fits within text without additional vertical spacing. 7750 bool ImGui::SmallButton(const char* label) 7751 { 7752 ImGuiContext& g = *GImGui; 7753 float backup_padding_y = g.Style.FramePadding.y; 7754 g.Style.FramePadding.y = 0.0f; 7755 bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); 7756 g.Style.FramePadding.y = backup_padding_y; 7757 return pressed; 7758 } 7759 7760 bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) 7761 { 7762 ImGuiWindow* window = GetCurrentWindow(); 7763 if (window->SkipItems) 7764 return false; 7765 7766 ImGuiContext& g = *GImGui; 7767 const ImGuiID id = window->GetID(str_id); 7768 float sz = ImGui::GetFrameHeight(); 7769 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(sz, sz)); 7770 ItemSize(bb); 7771 if (!ItemAdd(bb, id)) 7772 return false; 7773 7774 bool hovered, held; 7775 bool pressed = ButtonBehavior(bb, id, &hovered, &held); 7776 7777 // Render 7778 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 7779 RenderNavHighlight(bb, id); 7780 RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); 7781 RenderArrow(bb.Min + g.Style.FramePadding, dir); 7782 7783 return pressed; 7784 } 7785 7786 // Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. 7787 // Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) 7788 bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) 7789 { 7790 ImGuiWindow* window = GetCurrentWindow(); 7791 if (window->SkipItems) 7792 return false; 7793 7794 const ImGuiID id = window->GetID(str_id); 7795 ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); 7796 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 7797 ItemSize(bb); 7798 if (!ItemAdd(bb, id)) 7799 return false; 7800 7801 bool hovered, held; 7802 bool pressed = ButtonBehavior(bb, id, &hovered, &held); 7803 7804 return pressed; 7805 } 7806 7807 // Button to close a window 7808 bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) 7809 { 7810 ImGuiContext& g = *GImGui; 7811 ImGuiWindow* window = g.CurrentWindow; 7812 7813 // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. 7814 // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). 7815 const ImRect bb(pos - ImVec2(radius, radius), pos + ImVec2(radius, radius)); 7816 bool is_clipped = !ItemAdd(bb, id); 7817 7818 bool hovered, held; 7819 bool pressed = ButtonBehavior(bb, id, &hovered, &held); 7820 if (is_clipped) 7821 return pressed; 7822 7823 // Render 7824 ImVec2 center = bb.GetCenter(); 7825 if (hovered) 7826 window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); 7827 7828 float cross_extent = (radius * 0.7071f) - 1.0f; 7829 ImU32 cross_col = GetColorU32(ImGuiCol_Text); 7830 center -= ImVec2(0.5f, 0.5f); 7831 window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f); 7832 window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f); 7833 7834 return pressed; 7835 } 7836 7837 void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) 7838 { 7839 ImGuiWindow* window = GetCurrentWindow(); 7840 if (window->SkipItems) 7841 return; 7842 7843 ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 7844 if (border_col.w > 0.0f) 7845 bb.Max += ImVec2(2, 2); 7846 ItemSize(bb); 7847 if (!ItemAdd(bb, 0)) 7848 return; 7849 7850 if (border_col.w > 0.0f) 7851 { 7852 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); 7853 window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col)); 7854 } 7855 else 7856 { 7857 window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); 7858 } 7859 } 7860 7861 // frame_padding < 0: uses FramePadding from style (default) 7862 // frame_padding = 0: no framing 7863 // frame_padding > 0: set framing size 7864 // The color used are the button colors. 7865 bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) 7866 { 7867 ImGuiWindow* window = GetCurrentWindow(); 7868 if (window->SkipItems) 7869 return false; 7870 7871 ImGuiContext& g = *GImGui; 7872 const ImGuiStyle& style = g.Style; 7873 7874 // Default to using texture ID as ID. User can still push string/integer prefixes. 7875 // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. 7876 PushID((void *)user_texture_id); 7877 const ImGuiID id = window->GetID("#image"); 7878 PopID(); 7879 7880 const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; 7881 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); 7882 const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); 7883 ItemSize(bb); 7884 if (!ItemAdd(bb, id)) 7885 return false; 7886 7887 bool hovered, held; 7888 bool pressed = ButtonBehavior(bb, id, &hovered, &held); 7889 7890 // Render 7891 const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); 7892 RenderNavHighlight(bb, id); 7893 RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); 7894 if (bg_col.w > 0.0f) 7895 window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); 7896 window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); 7897 7898 return pressed; 7899 } 7900 7901 // Start logging ImGui output to TTY 7902 void ImGui::LogToTTY(int max_depth) 7903 { 7904 ImGuiContext& g = *GImGui; 7905 if (g.LogEnabled) 7906 return; 7907 ImGuiWindow* window = g.CurrentWindow; 7908 7909 IM_ASSERT(g.LogFile == NULL); 7910 g.LogFile = stdout; 7911 g.LogEnabled = true; 7912 g.LogStartDepth = window->DC.TreeDepth; 7913 if (max_depth >= 0) 7914 g.LogAutoExpandMaxDepth = max_depth; 7915 } 7916 7917 // Start logging ImGui output to given file 7918 void ImGui::LogToFile(int max_depth, const char* filename) 7919 { 7920 ImGuiContext& g = *GImGui; 7921 if (g.LogEnabled) 7922 return; 7923 ImGuiWindow* window = g.CurrentWindow; 7924 7925 if (!filename) 7926 { 7927 filename = g.IO.LogFilename; 7928 if (!filename) 7929 return; 7930 } 7931 7932 IM_ASSERT(g.LogFile == NULL); 7933 g.LogFile = ImFileOpen(filename, "ab"); 7934 if (!g.LogFile) 7935 { 7936 IM_ASSERT(g.LogFile != NULL); // Consider this an error 7937 return; 7938 } 7939 g.LogEnabled = true; 7940 g.LogStartDepth = window->DC.TreeDepth; 7941 if (max_depth >= 0) 7942 g.LogAutoExpandMaxDepth = max_depth; 7943 } 7944 7945 // Start logging ImGui output to clipboard 7946 void ImGui::LogToClipboard(int max_depth) 7947 { 7948 ImGuiContext& g = *GImGui; 7949 if (g.LogEnabled) 7950 return; 7951 ImGuiWindow* window = g.CurrentWindow; 7952 7953 IM_ASSERT(g.LogFile == NULL); 7954 g.LogFile = NULL; 7955 g.LogEnabled = true; 7956 g.LogStartDepth = window->DC.TreeDepth; 7957 if (max_depth >= 0) 7958 g.LogAutoExpandMaxDepth = max_depth; 7959 } 7960 7961 void ImGui::LogFinish() 7962 { 7963 ImGuiContext& g = *GImGui; 7964 if (!g.LogEnabled) 7965 return; 7966 7967 LogText(IM_NEWLINE); 7968 if (g.LogFile != NULL) 7969 { 7970 if (g.LogFile == stdout) 7971 fflush(g.LogFile); 7972 else 7973 fclose(g.LogFile); 7974 g.LogFile = NULL; 7975 } 7976 if (g.LogClipboard->size() > 1) 7977 { 7978 SetClipboardText(g.LogClipboard->begin()); 7979 g.LogClipboard->clear(); 7980 } 7981 g.LogEnabled = false; 7982 } 7983 7984 // Helper to display logging buttons 7985 void ImGui::LogButtons() 7986 { 7987 ImGuiContext& g = *GImGui; 7988 7989 PushID("LogButtons"); 7990 const bool log_to_tty = Button("Log To TTY"); SameLine(); 7991 const bool log_to_file = Button("Log To File"); SameLine(); 7992 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); 7993 PushItemWidth(80.0f); 7994 PushAllowKeyboardFocus(false); 7995 SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); 7996 PopAllowKeyboardFocus(); 7997 PopItemWidth(); 7998 PopID(); 7999 8000 // Start logging at the end of the function so that the buttons don't appear in the log 8001 if (log_to_tty) 8002 LogToTTY(g.LogAutoExpandMaxDepth); 8003 if (log_to_file) 8004 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); 8005 if (log_to_clipboard) 8006 LogToClipboard(g.LogAutoExpandMaxDepth); 8007 } 8008 8009 bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) 8010 { 8011 if (flags & ImGuiTreeNodeFlags_Leaf) 8012 return true; 8013 8014 // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions) 8015 ImGuiContext& g = *GImGui; 8016 ImGuiWindow* window = g.CurrentWindow; 8017 ImGuiStorage* storage = window->DC.StateStorage; 8018 8019 bool is_open; 8020 if (g.NextTreeNodeOpenCond != 0) 8021 { 8022 if (g.NextTreeNodeOpenCond & ImGuiCond_Always) 8023 { 8024 is_open = g.NextTreeNodeOpenVal; 8025 storage->SetInt(id, is_open); 8026 } 8027 else 8028 { 8029 // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. 8030 const int stored_value = storage->GetInt(id, -1); 8031 if (stored_value == -1) 8032 { 8033 is_open = g.NextTreeNodeOpenVal; 8034 storage->SetInt(id, is_open); 8035 } 8036 else 8037 { 8038 is_open = stored_value != 0; 8039 } 8040 } 8041 g.NextTreeNodeOpenCond = 0; 8042 } 8043 else 8044 { 8045 is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; 8046 } 8047 8048 // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). 8049 // NB- If we are above max depth we still allow manually opened nodes to be logged. 8050 if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) 8051 is_open = true; 8052 8053 return is_open; 8054 } 8055 8056 bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) 8057 { 8058 ImGuiWindow* window = GetCurrentWindow(); 8059 if (window->SkipItems) 8060 return false; 8061 8062 ImGuiContext& g = *GImGui; 8063 const ImGuiStyle& style = g.Style; 8064 const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; 8065 const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); 8066 8067 if (!label_end) 8068 label_end = FindRenderedTextEnd(label); 8069 const ImVec2 label_size = CalcTextSize(label, label_end, false); 8070 8071 // We vertically grow up to current line height up the typical widget height. 8072 const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it 8073 const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); 8074 ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); 8075 if (display_frame) 8076 { 8077 // Framed header expand a little outside the default padding 8078 frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; 8079 frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; 8080 } 8081 8082 const float text_offset_x = (g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2)); // Collapser arrow width + Spacing 8083 const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser 8084 ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); 8085 8086 // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing 8087 // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) 8088 const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x * 2, frame_bb.Max.y); 8089 bool is_open = TreeNodeBehaviorIsOpen(id, flags); 8090 8091 // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. 8092 // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). 8093 // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. 8094 if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) 8095 window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); 8096 8097 bool item_add = ItemAdd(interact_bb, id); 8098 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; 8099 window->DC.LastItemDisplayRect = frame_bb; 8100 8101 if (!item_add) 8102 { 8103 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) 8104 TreePushRawID(id); 8105 return is_open; 8106 } 8107 8108 // Flags that affects opening behavior: 8109 // - 0(default) ..................... single-click anywhere to open 8110 // - OpenOnDoubleClick .............. double-click anywhere to open 8111 // - OpenOnArrow .................... single-click on arrow to open 8112 // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open 8113 ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); 8114 if (!(flags & ImGuiTreeNodeFlags_Leaf)) 8115 button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; 8116 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) 8117 button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); 8118 8119 bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); 8120 if (!(flags & ImGuiTreeNodeFlags_Leaf)) 8121 { 8122 bool toggled = false; 8123 if (pressed) 8124 { 8125 toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); 8126 if (flags & ImGuiTreeNodeFlags_OpenOnArrow) 8127 toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); 8128 if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) 8129 toggled |= g.IO.MouseDoubleClicked[0]; 8130 if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. 8131 toggled = false; 8132 } 8133 8134 if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) 8135 { 8136 toggled = true; 8137 NavMoveRequestCancel(); 8138 } 8139 if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? 8140 { 8141 toggled = true; 8142 NavMoveRequestCancel(); 8143 } 8144 8145 if (toggled) 8146 { 8147 is_open = !is_open; 8148 window->DC.StateStorage->SetInt(id, is_open); 8149 } 8150 } 8151 if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) 8152 SetItemAllowOverlap(); 8153 8154 // Render 8155 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); 8156 const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); 8157 if (display_frame) 8158 { 8159 // Framed type 8160 RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); 8161 RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); 8162 RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); 8163 if (g.LogEnabled) 8164 { 8165 // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. 8166 const char log_prefix[] = "\n##"; 8167 const char log_suffix[] = "##"; 8168 LogRenderedText(&text_pos, log_prefix, log_prefix + 3); 8169 RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); 8170 LogRenderedText(&text_pos, log_suffix + 1, log_suffix + 3); 8171 } 8172 else 8173 { 8174 RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); 8175 } 8176 } 8177 else 8178 { 8179 // Unframed typed for tree nodes 8180 if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) 8181 { 8182 RenderFrame(frame_bb.Min, frame_bb.Max, col, false); 8183 RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); 8184 } 8185 8186 if (flags & ImGuiTreeNodeFlags_Bullet) 8187 RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); 8188 else if (!(flags & ImGuiTreeNodeFlags_Leaf)) 8189 RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); 8190 if (g.LogEnabled) 8191 LogRenderedText(&text_pos, ">"); 8192 RenderText(text_pos, label, label_end, false); 8193 } 8194 8195 if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) 8196 TreePushRawID(id); 8197 return is_open; 8198 } 8199 8200 // CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). 8201 // This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). 8202 bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) 8203 { 8204 ImGuiWindow* window = GetCurrentWindow(); 8205 if (window->SkipItems) 8206 return false; 8207 8208 return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label); 8209 } 8210 8211 bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) 8212 { 8213 ImGuiWindow* window = GetCurrentWindow(); 8214 if (window->SkipItems) 8215 return false; 8216 8217 if (p_open && !*p_open) 8218 return false; 8219 8220 ImGuiID id = window->GetID(label); 8221 bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); 8222 if (p_open) 8223 { 8224 // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. 8225 ImGuiContext& g = *GImGui; 8226 float button_sz = g.FontSize * 0.5f; 8227 ImGuiItemHoveredDataBackup last_item_backup; 8228 if (CloseButton(window->GetID((void*)(intptr_t)(id + 1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) 8229 *p_open = false; 8230 last_item_backup.Restore(); 8231 } 8232 8233 return is_open; 8234 } 8235 8236 bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) 8237 { 8238 ImGuiWindow* window = GetCurrentWindow(); 8239 if (window->SkipItems) 8240 return false; 8241 8242 return TreeNodeBehavior(window->GetID(label), flags, label, NULL); 8243 } 8244 8245 bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) 8246 { 8247 ImGuiWindow* window = GetCurrentWindow(); 8248 if (window->SkipItems) 8249 return false; 8250 8251 ImGuiContext& g = *GImGui; 8252 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); 8253 return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); 8254 } 8255 8256 bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) 8257 { 8258 ImGuiWindow* window = GetCurrentWindow(); 8259 if (window->SkipItems) 8260 return false; 8261 8262 ImGuiContext& g = *GImGui; 8263 const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); 8264 return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); 8265 } 8266 8267 bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) 8268 { 8269 return TreeNodeExV(str_id, 0, fmt, args); 8270 } 8271 8272 bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) 8273 { 8274 return TreeNodeExV(ptr_id, 0, fmt, args); 8275 } 8276 8277 bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) 8278 { 8279 va_list args; 8280 va_start(args, fmt); 8281 bool is_open = TreeNodeExV(str_id, flags, fmt, args); 8282 va_end(args); 8283 return is_open; 8284 } 8285 8286 bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) 8287 { 8288 va_list args; 8289 va_start(args, fmt); 8290 bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); 8291 va_end(args); 8292 return is_open; 8293 } 8294 8295 bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) 8296 { 8297 va_list args; 8298 va_start(args, fmt); 8299 bool is_open = TreeNodeExV(str_id, 0, fmt, args); 8300 va_end(args); 8301 return is_open; 8302 } 8303 8304 bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) 8305 { 8306 va_list args; 8307 va_start(args, fmt); 8308 bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); 8309 va_end(args); 8310 return is_open; 8311 } 8312 8313 bool ImGui::TreeNode(const char* label) 8314 { 8315 ImGuiWindow* window = GetCurrentWindow(); 8316 if (window->SkipItems) 8317 return false; 8318 return TreeNodeBehavior(window->GetID(label), 0, label, NULL); 8319 } 8320 8321 void ImGui::TreeAdvanceToLabelPos() 8322 { 8323 ImGuiContext& g = *GImGui; 8324 g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); 8325 } 8326 8327 // Horizontal distance preceding label when using TreeNode() or Bullet() 8328 float ImGui::GetTreeNodeToLabelSpacing() 8329 { 8330 ImGuiContext& g = *GImGui; 8331 return g.FontSize + (g.Style.FramePadding.x * 2.0f); 8332 } 8333 8334 void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) 8335 { 8336 ImGuiContext& g = *GImGui; 8337 if (g.CurrentWindow->SkipItems) 8338 return; 8339 g.NextTreeNodeOpenVal = is_open; 8340 g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always; 8341 } 8342 8343 void ImGui::PushID(const char* str_id) 8344 { 8345 ImGuiWindow* window = GetCurrentWindowRead(); 8346 window->IDStack.push_back(window->GetID(str_id)); 8347 } 8348 8349 void ImGui::PushID(const char* str_id_begin, const char* str_id_end) 8350 { 8351 ImGuiWindow* window = GetCurrentWindowRead(); 8352 window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); 8353 } 8354 8355 void ImGui::PushID(const void* ptr_id) 8356 { 8357 ImGuiWindow* window = GetCurrentWindowRead(); 8358 window->IDStack.push_back(window->GetID(ptr_id)); 8359 } 8360 8361 void ImGui::PushID(int int_id) 8362 { 8363 const void* ptr_id = (void*)(intptr_t)int_id; 8364 ImGuiWindow* window = GetCurrentWindowRead(); 8365 window->IDStack.push_back(window->GetID(ptr_id)); 8366 } 8367 8368 void ImGui::PopID() 8369 { 8370 ImGuiWindow* window = GetCurrentWindowRead(); 8371 window->IDStack.pop_back(); 8372 } 8373 8374 ImGuiID ImGui::GetID(const char* str_id) 8375 { 8376 return GImGui->CurrentWindow->GetID(str_id); 8377 } 8378 8379 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) 8380 { 8381 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); 8382 } 8383 8384 ImGuiID ImGui::GetID(const void* ptr_id) 8385 { 8386 return GImGui->CurrentWindow->GetID(ptr_id); 8387 } 8388 8389 void ImGui::Bullet() 8390 { 8391 ImGuiWindow* window = GetCurrentWindow(); 8392 if (window->SkipItems) 8393 return; 8394 8395 ImGuiContext& g = *GImGui; 8396 const ImGuiStyle& style = g.Style; 8397 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize); 8398 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); 8399 ItemSize(bb); 8400 if (!ItemAdd(bb, 0)) 8401 { 8402 SameLine(0, style.FramePadding.x * 2); 8403 return; 8404 } 8405 8406 // Render and stay on same line 8407 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); 8408 SameLine(0, style.FramePadding.x * 2); 8409 } 8410 8411 // Text with a little bullet aligned to the typical tree node. 8412 void ImGui::BulletTextV(const char* fmt, va_list args) 8413 { 8414 ImGuiWindow* window = GetCurrentWindow(); 8415 if (window->SkipItems) 8416 return; 8417 8418 ImGuiContext& g = *GImGui; 8419 const ImGuiStyle& style = g.Style; 8420 8421 const char* text_begin = g.TempBuffer; 8422 const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); 8423 const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); 8424 const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it 8425 const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2), g.FontSize); 8426 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding 8427 ItemSize(bb); 8428 if (!ItemAdd(bb, 0)) 8429 return; 8430 8431 // Render 8432 RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); 8433 RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, text_base_offset_y), text_begin, text_end, false); 8434 } 8435 8436 void ImGui::BulletText(const char* fmt, ...) 8437 { 8438 va_list args; 8439 va_start(args, fmt); 8440 BulletTextV(fmt, args); 8441 va_end(args); 8442 } 8443 8444 static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) 8445 { 8446 if (data_type == ImGuiDataType_Int32 || data_type == ImGuiDataType_Uint32) 8447 return ImFormatString(buf, buf_size, format, *(const int*)data_ptr); 8448 if (data_type == ImGuiDataType_Float) 8449 return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); 8450 if (data_type == ImGuiDataType_Double) 8451 return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); 8452 IM_ASSERT(0); 8453 return 0; 8454 } 8455 8456 static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) 8457 { 8458 IM_ASSERT(op == '+' || op == '-'); 8459 if (data_type == ImGuiDataType_Int32) 8460 { 8461 if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; 8462 else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; 8463 } 8464 else if (data_type == ImGuiDataType_Uint32) 8465 { 8466 if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const unsigned int*)arg2; 8467 else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const unsigned int*)arg2; 8468 } 8469 else if (data_type == ImGuiDataType_Float) 8470 { 8471 if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; 8472 else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; 8473 } 8474 else if (data_type == ImGuiDataType_Double) 8475 { 8476 if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; 8477 else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; 8478 } 8479 } 8480 8481 static size_t GDataTypeSize[ImGuiDataType_COUNT] = 8482 { 8483 sizeof(int), 8484 sizeof(unsigned int), 8485 sizeof(float), 8486 sizeof(double) 8487 }; 8488 8489 // User can input math operators (e.g. +100) to edit a numerical values. 8490 // NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. 8491 static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format) 8492 { 8493 while (ImCharIsSpace((unsigned int)*buf)) 8494 buf++; 8495 8496 // We don't support '-' op because it would conflict with inputing negative value. 8497 // Instead you can use +-100 to subtract from an existing value 8498 char op = buf[0]; 8499 if (op == '+' || op == '*' || op == '/') 8500 { 8501 buf++; 8502 while (ImCharIsSpace((unsigned int)*buf)) 8503 buf++; 8504 } 8505 else 8506 { 8507 op = 0; 8508 } 8509 if (!buf[0]) 8510 return false; 8511 8512 IM_ASSERT(data_type < ImGuiDataType_COUNT); 8513 int data_backup[2]; 8514 IM_ASSERT(GDataTypeSize[data_type] <= sizeof(data_backup)); 8515 memcpy(data_backup, data_ptr, GDataTypeSize[data_type]); 8516 8517 int arg1i = 0; 8518 float arg1f = 0.0f; 8519 if (data_type == ImGuiDataType_Int32) 8520 { 8521 if (!scalar_format) 8522 scalar_format = "%d"; 8523 int* v = (int*)data_ptr; 8524 int arg0i = *v; 8525 if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1) 8526 return false; 8527 // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision 8528 if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) 8529 else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply 8530 else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide 8531 else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign integer constant 8532 } 8533 else if (data_type == ImGuiDataType_Uint32) 8534 { 8535 if (!scalar_format) 8536 scalar_format = "%u"; 8537 ImU32* v = (unsigned int*)data_ptr; 8538 ImU32 arg0i = *v; 8539 if (op && sscanf(initial_value_buf, scalar_format, &arg0i) < 1) 8540 return false; 8541 // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision 8542 if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (ImU32)(arg0i + arg1f); } // Add (use "+-" to subtract) 8543 else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (ImU32)(arg0i * arg1f); } // Multiply 8544 else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (ImU32)(arg0i / arg1f); }// Divide 8545 else { if (sscanf(buf, scalar_format, &arg0i) == 1) *v = arg0i; } // Assign integer constant 8546 } 8547 else if (data_type == ImGuiDataType_Float) 8548 { 8549 // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in 8550 scalar_format = "%f"; 8551 float* v = (float*)data_ptr; 8552 float arg0f = *v; 8553 if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1) 8554 return false; 8555 if (sscanf(buf, scalar_format, &arg1f) < 1) 8556 return false; 8557 if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) 8558 else if (op == '*') { *v = arg0f * arg1f; } // Multiply 8559 else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide 8560 else { *v = arg1f; } // Assign constant 8561 } 8562 else if (data_type == ImGuiDataType_Double) 8563 { 8564 scalar_format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis 8565 double* v = (double*)data_ptr; 8566 double arg0f = *v; 8567 if (op && sscanf(initial_value_buf, scalar_format, &arg0f) < 1) 8568 return false; 8569 if (sscanf(buf, scalar_format, &arg1f) < 1) 8570 return false; 8571 if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) 8572 else if (op == '*') { *v = arg0f * arg1f; } // Multiply 8573 else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide 8574 else { *v = arg1f; } // Assign constant 8575 } 8576 return memcmp(data_backup, data_ptr, GDataTypeSize[data_type]) != 0; 8577 } 8578 8579 // Create text input in place of a slider (when CTRL+Clicking on slider) 8580 // FIXME: Logic is messy and confusing. 8581 bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) 8582 { 8583 ImGuiContext& g = *GImGui; 8584 ImGuiWindow* window = GetCurrentWindow(); 8585 8586 // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) 8587 // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id 8588 SetActiveID(g.ScalarAsInputTextId, window); 8589 g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); 8590 SetHoveredID(0); 8591 FocusableItemUnregister(window); 8592 8593 char fmt_buf[32]; 8594 char data_buf[32]; 8595 format = ParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); 8596 DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); 8597 ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); 8598 bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); 8599 if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget 8600 { 8601 IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID 8602 g.ScalarAsInputTextId = g.ActiveId; 8603 SetHoveredID(id); 8604 } 8605 if (value_changed) 8606 return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.begin(), data_type, data_ptr, NULL); 8607 return false; 8608 } 8609 8610 const char* ImGui::ParseFormatTrimDecorationsLeading(const char* fmt) 8611 { 8612 while (char c = fmt[0]) 8613 { 8614 if (c == '%' && fmt[1] != '%') 8615 return fmt; 8616 else if (c == '%') 8617 fmt++; 8618 fmt++; 8619 } 8620 return fmt; 8621 } 8622 8623 // Extract the format out of a format string with leading or trailing decorations 8624 // fmt = "blah blah" -> return fmt 8625 // fmt = "%.3f" -> return fmt 8626 // fmt = "hello %.3f" -> return fmt + 6 8627 // fmt = "%.3f hello" -> return buf written with "%.3f" 8628 const char* ImGui::ParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) 8629 { 8630 // We don't use strchr() because our strings are usually very short and often start with '%' 8631 const char* fmt_start = ParseFormatTrimDecorationsLeading(fmt); 8632 if (fmt_start[0] != '%') 8633 return fmt; 8634 fmt = fmt_start; 8635 while (char c = *fmt++) 8636 { 8637 if (c >= 'A' && c <= 'Z' && (c != 'L')) // L is a type modifier, other letters qualify as types aka end of the format 8638 break; 8639 if (c >= 'a' && c <= 'z' && (c != 'h' && c != 'j' && c != 'l' && c != 't' && c != 'w' && c != 'z')) // h/j/l/t/w/z are type modifiers, other letters qualify as types aka end of the format 8640 break; 8641 } 8642 if (fmt[0] == 0) // If we only have leading decoration, we don't need to copy the data. 8643 return fmt_start; 8644 ImStrncpy(buf, fmt_start, ImMin((int)(fmt + 1 - fmt_start), buf_size)); 8645 return buf; 8646 } 8647 8648 // Parse display precision back from the display format string 8649 // FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. 8650 int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) 8651 { 8652 fmt = ParseFormatTrimDecorationsLeading(fmt); 8653 if (fmt[0] != '%') 8654 return default_precision; 8655 fmt++; 8656 while (*fmt >= '0' && *fmt <= '9') 8657 fmt++; 8658 int precision = INT_MAX; 8659 if (*fmt == '.') 8660 { 8661 fmt = ImAtoi(fmt + 1, &precision); 8662 if (precision < 0 || precision > 99) 8663 precision = default_precision; 8664 } 8665 if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation 8666 precision = -1; 8667 if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) 8668 precision = -1; 8669 return (precision == INT_MAX) ? default_precision : precision; 8670 } 8671 8672 static float GetMinimumStepAtDecimalPrecision(int decimal_precision) 8673 { 8674 static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; 8675 return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); 8676 } 8677 8678 float ImGui::RoundScalarWithFormat(const char* format, float value) 8679 { 8680 char buf[64]; 8681 ImFormatString(buf, IM_ARRAYSIZE(buf), ParseFormatTrimDecorationsLeading(format), value); 8682 return (float)atof(buf); 8683 } 8684 8685 static inline float SliderBehaviorCalcRatioFromValue(float v, float v_min, float v_max, float power, float linear_zero_pos) 8686 { 8687 if (v_min == v_max) 8688 return 0.0f; 8689 8690 const bool is_non_linear = (power < 1.0f - 0.00001f) || (power > 1.0f + 0.00001f); 8691 const float v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); 8692 if (is_non_linear) 8693 { 8694 if (v_clamped < 0.0f) 8695 { 8696 const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f, v_max) - v_min); 8697 return (1.0f - powf(f, 1.0f / power)) * linear_zero_pos; 8698 } 8699 else 8700 { 8701 const float f = (v_clamped - ImMax(0.0f, v_min)) / (v_max - ImMax(0.0f, v_min)); 8702 return linear_zero_pos + powf(f, 1.0f / power) * (1.0f - linear_zero_pos); 8703 } 8704 } 8705 8706 // Linear slider 8707 return (v_clamped - v_min) / (v_max - v_min); 8708 } 8709 8710 bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, const char* format, float power, ImGuiSliderFlags flags) 8711 { 8712 ImGuiContext& g = *GImGui; 8713 ImGuiWindow* window = GetCurrentWindow(); 8714 const ImGuiStyle& style = g.Style; 8715 8716 // Draw frame 8717 const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 8718 RenderNavHighlight(frame_bb, id); 8719 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); 8720 8721 const bool is_non_linear = (power < 1.0f - 0.00001f) || (power > 1.0f + 0.00001f); 8722 const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; 8723 const bool is_decimal = ParseFormatPrecision(format, 3) > 0; 8724 8725 const float grab_padding = 2.0f; 8726 const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); 8727 float grab_sz; 8728 if (is_decimal) 8729 grab_sz = ImMin(style.GrabMinSize, slider_sz); 8730 else 8731 grab_sz = ImMin(ImMax(1.0f * (slider_sz / ((v_min < v_max ? v_max - v_min : v_min - v_max) + 1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit 8732 const float slider_usable_sz = slider_sz - grab_sz; 8733 const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz * 0.5f; 8734 const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz * 0.5f; 8735 8736 // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f 8737 float linear_zero_pos = 0.0f; // 0.0->1.0f 8738 if (v_min * v_max < 0.0f) 8739 { 8740 // Different sign 8741 const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f / power); 8742 const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f / power); 8743 linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0); 8744 } 8745 else 8746 { 8747 // Same sign 8748 linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; 8749 } 8750 8751 // Process interacting with the slider 8752 bool value_changed = false; 8753 if (g.ActiveId == id) 8754 { 8755 bool set_new_value = false; 8756 float clicked_t = 0.0f; 8757 if (g.ActiveIdSource == ImGuiInputSource_Mouse) 8758 { 8759 if (!g.IO.MouseDown[0]) 8760 { 8761 ClearActiveID(); 8762 } 8763 else 8764 { 8765 const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; 8766 clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; 8767 if (!is_horizontal) 8768 clicked_t = 1.0f - clicked_t; 8769 set_new_value = true; 8770 } 8771 } 8772 else if (g.ActiveIdSource == ImGuiInputSource_Nav) 8773 { 8774 const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); 8775 float delta = is_horizontal ? delta2.x : -delta2.y; 8776 if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) 8777 { 8778 ClearActiveID(); 8779 } 8780 else if (delta != 0.0f) 8781 { 8782 clicked_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); 8783 if (!is_decimal && !is_non_linear) 8349 } 8350 } 8351 8352 // 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 8353 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) 8354 // 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. 8355 // 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. 8356 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? 8357 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match 8358 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) 8359 if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) 8784 8360 { 8785 if (fabsf(v_max - v_min) <= 100.0f || IsNavInputDown(ImGuiNavInput_TweakSlow)) 8786 delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (v_max - v_min); // Gamepad/keyboard tweak speeds in integer steps 8787 else 8788 delta /= 100.0f; 8361 result->DistAxial = dist_axial; 8362 new_best = true; 8789 8363 } 8790 else 8364 8365 return new_best; 8366 } 8367 8368 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) 8369 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) 8370 { 8371 ImGuiContext& g = *GImGui; 8372 //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. 8373 // return; 8374 8375 const ImGuiItemFlags item_flags = window->DC.ItemFlags; 8376 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); 8377 8378 // Process Init Request 8379 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) 8380 { 8381 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback 8382 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) 8383 { 8384 g.NavInitResultId = id; 8385 g.NavInitResultRectRel = nav_bb_rel; 8386 } 8387 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) 8388 { 8389 g.NavInitRequest = false; // Found a match, clear request 8390 NavUpdateAnyRequestFlag(); 8391 } 8392 } 8393 8394 // Process Move Request (scoring for navigation) 8395 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) 8396 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) 8397 { 8398 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 8399 #if IMGUI_DEBUG_NAV_SCORING 8400 // [DEBUG] Score all items in NavWindow at all times 8401 if (!g.NavMoveRequest) 8402 g.NavMoveDir = g.NavMoveDirLast; 8403 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; 8404 #else 8405 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); 8406 #endif 8407 if (new_best) 8408 { 8409 result->Window = window; 8410 result->ID = id; 8411 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; 8412 result->RectRel = nav_bb_rel; 8413 } 8414 8415 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. 8416 const float VISIBLE_RATIO = 0.70f; 8417 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) 8418 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) 8419 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) 8420 { 8421 result = &g.NavMoveResultLocalVisibleSet; 8422 result->Window = window; 8423 result->ID = id; 8424 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; 8425 result->RectRel = nav_bb_rel; 8426 } 8427 } 8428 8429 // Update window-relative bounding box of navigated item 8430 if (g.NavId == id) 8431 { 8432 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. 8433 g.NavLayer = window->DC.NavLayerCurrent; 8434 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 8435 g.NavIdIsAlive = true; 8436 g.NavIdTabCounter = window->DC.FocusCounterTabStop; 8437 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) 8438 } 8439 } 8440 8441 bool ImGui::NavMoveRequestButNoResultYet() 8442 { 8443 ImGuiContext& g = *GImGui; 8444 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; 8445 } 8446 8447 void ImGui::NavMoveRequestCancel() 8448 { 8449 ImGuiContext& g = *GImGui; 8450 g.NavMoveRequest = false; 8451 NavUpdateAnyRequestFlag(); 8452 } 8453 8454 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) 8455 { 8456 ImGuiContext& g = *GImGui; 8457 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); 8458 NavMoveRequestCancel(); 8459 g.NavMoveDir = move_dir; 8460 g.NavMoveClipDir = clip_dir; 8461 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 8462 g.NavMoveRequestFlags = move_flags; 8463 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; 8464 } 8465 8466 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) 8467 { 8468 ImGuiContext& g = *GImGui; 8469 8470 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire 8471 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. 8472 g.NavWrapRequestWindow = window; 8473 g.NavWrapRequestFlags = move_flags; 8474 } 8475 8476 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). 8477 // This way we could find the last focused window among our children. It would be much less confusing this way? 8478 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) 8479 { 8480 ImGuiWindow* parent = nav_window; 8481 while (parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 8482 parent = parent->ParentWindow; 8483 if (parent && parent != nav_window) 8484 parent->NavLastChildNavWindow = nav_window; 8485 } 8486 8487 // Restore the last focused child. 8488 // Call when we are expected to land on the Main Layer (0) after FocusWindow() 8489 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) 8490 { 8491 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) 8492 return window->NavLastChildNavWindow; 8493 return window; 8494 } 8495 8496 static void NavRestoreLayer(ImGuiNavLayer layer) 8497 { 8498 ImGuiContext& g = *GImGui; 8499 g.NavLayer = layer; 8500 if (layer == 0) 8501 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); 8502 ImGuiWindow* window = g.NavWindow; 8503 if (layer == 0 && window->NavLastIds[0] != 0) 8504 ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]); 8505 else 8506 ImGui::NavInitWindow(window, true); 8507 } 8508 8509 static inline void ImGui::NavUpdateAnyRequestFlag() 8510 { 8511 ImGuiContext& g = *GImGui; 8512 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); 8513 if (g.NavAnyRequest) 8514 IM_ASSERT(g.NavWindow != NULL); 8515 } 8516 8517 // This needs to be called before we submit any widget (aka in or before Begin) 8518 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) 8519 { 8520 ImGuiContext& g = *GImGui; 8521 IM_ASSERT(window == g.NavWindow); 8522 bool init_for_nav = false; 8523 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) 8524 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) 8525 init_for_nav = true; 8526 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); 8527 if (init_for_nav) 8528 { 8529 SetNavID(0, g.NavLayer, 0); 8530 g.NavInitRequest = true; 8531 g.NavInitRequestFromMove = false; 8532 g.NavInitResultId = 0; 8533 g.NavInitResultRectRel = ImRect(); 8534 NavUpdateAnyRequestFlag(); 8535 } 8536 else 8537 { 8538 g.NavId = window->NavLastIds[0]; 8539 g.NavFocusScopeId = 0; 8540 } 8541 } 8542 8543 static ImVec2 ImGui::NavCalcPreferredRefPos() 8544 { 8545 ImGuiContext& g = *GImGui; 8546 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) 8547 { 8548 // Mouse (we need a fallback in case the mouse becomes invalid after being used) 8549 if (IsMousePosValid(&g.IO.MousePos)) 8550 return g.IO.MousePos; 8551 return g.LastValidMousePos; 8552 } 8553 else 8554 { 8555 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. 8556 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; 8557 ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); 8558 ImRect visible_rect = GetViewportRect(); 8559 return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. 8560 } 8561 } 8562 8563 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) 8564 { 8565 ImGuiContext& g = *GImGui; 8566 if (mode == ImGuiInputReadMode_Down) 8567 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) 8568 8569 const float t = g.IO.NavInputsDownDuration[n]; 8570 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. 8571 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); 8572 if (t < 0.0f) 8573 return 0.0f; 8574 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. 8575 return (t == 0.0f) ? 1.0f : 0.0f; 8576 if (mode == ImGuiInputReadMode_Repeat) 8577 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); 8578 if (mode == ImGuiInputReadMode_RepeatSlow) 8579 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); 8580 if (mode == ImGuiInputReadMode_RepeatFast) 8581 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); 8582 return 0.0f; 8583 } 8584 8585 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) 8586 { 8587 ImVec2 delta(0.0f, 0.0f); 8588 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) 8589 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); 8590 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) 8591 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); 8592 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) 8593 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); 8594 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) 8595 delta *= slow_factor; 8596 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) 8597 delta *= fast_factor; 8598 return delta; 8599 } 8600 8601 static void ImGui::NavUpdate() 8602 { 8603 ImGuiContext& g = *GImGui; 8604 ImGuiIO& io = g.IO; 8605 8606 io.WantSetMousePos = false; 8607 g.NavWrapRequestWindow = NULL; 8608 g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; 8609 #if 0 8610 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); 8611 #endif 8612 8613 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) 8614 // (do it before we map Keyboard input!) 8615 bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; 8616 bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; 8617 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad) 8618 { 8619 if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f 8620 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f) 8621 g.NavInputSource = ImGuiInputSource_NavGamepad; 8622 } 8623 8624 // Update Keyboard->Nav inputs mapping 8625 if (nav_keyboard_active) 8626 { 8627 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0) 8628 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); 8629 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); 8630 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); 8631 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); 8632 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); 8633 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); 8634 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); 8635 if (io.KeyCtrl) 8636 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; 8637 if (io.KeyShift) 8638 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; 8639 if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. 8640 io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; 8641 #undef NAV_MAP_KEY 8642 } 8643 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); 8644 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) 8645 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; 8646 8647 // Process navigation init request (select first/default focus) 8648 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) 8649 NavUpdateInitResult(); 8650 g.NavInitRequest = false; 8651 g.NavInitRequestFromMove = false; 8652 g.NavInitResultId = 0; 8653 g.NavJustMovedToId = 0; 8654 8655 // Process navigation move request 8656 if (g.NavMoveRequest) 8657 NavUpdateMoveResult(); 8658 8659 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame 8660 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) 8661 { 8662 IM_ASSERT(g.NavMoveRequest); 8663 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 8664 g.NavDisableHighlight = false; 8665 g.NavMoveRequestForward = ImGuiNavForward_None; 8666 } 8667 8668 // Apply application mouse position movement, after we had a chance to process move request result. 8669 if (g.NavMousePosDirty && g.NavIdIsAlive) 8670 { 8671 // Set mouse position given our knowledge of the navigated item position from last frame 8672 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) 8673 { 8674 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) 8791 8675 { 8792 delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds 8793 if (IsNavInputDown(ImGuiNavInput_TweakSlow)) 8794 delta /= 10.0f; 8676 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); 8677 io.WantSetMousePos = true; 8795 8678 } 8796 if (IsNavInputDown(ImGuiNavInput_TweakFast)) 8797 delta *= 10.0f; 8798 set_new_value = true; 8799 if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits 8800 set_new_value = false; 8801 else 8802 clicked_t = ImSaturate(clicked_t + delta); 8803 } 8804 } 8805 8806 if (set_new_value) 8807 { 8808 float new_value; 8809 if (is_non_linear) 8810 { 8811 // Account for logarithmic scale on both sides of the zero 8812 if (clicked_t < linear_zero_pos) 8679 } 8680 g.NavMousePosDirty = false; 8681 } 8682 g.NavIdIsAlive = false; 8683 g.NavJustTabbedId = 0; 8684 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); 8685 8686 // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 8687 if (g.NavWindow) 8688 NavSaveLastChildNavWindowIntoParent(g.NavWindow); 8689 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) 8690 g.NavWindow->NavLastChildNavWindow = NULL; 8691 8692 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) 8693 NavUpdateWindowing(); 8694 8695 // Set output flags for user application 8696 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); 8697 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); 8698 8699 // Process NavCancel input (to close a popup, get back to parent, clear focus) 8700 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) 8701 { 8702 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); 8703 if (g.ActiveId != 0) 8704 { 8705 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) 8706 ClearActiveID(); 8707 } 8708 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) 8709 { 8710 // Exit child window 8711 ImGuiWindow* child_window = g.NavWindow; 8712 ImGuiWindow* parent_window = g.NavWindow->ParentWindow; 8713 IM_ASSERT(child_window->ChildId != 0); 8714 FocusWindow(parent_window); 8715 SetNavID(child_window->ChildId, 0, 0); 8716 // Reassigning with same value, we're being explicit here. 8717 g.NavIdIsAlive = false; // -V1048 8718 if (g.NavDisableMouseHover) 8719 g.NavMousePosDirty = true; 8720 } 8721 else if (g.OpenPopupStack.Size > 0) 8722 { 8723 // Close open popup/menu 8724 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) 8725 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); 8726 } 8727 else if (g.NavLayer != ImGuiNavLayer_Main) 8728 { 8729 // Leave the "menu" layer 8730 NavRestoreLayer(ImGuiNavLayer_Main); 8731 } 8732 else 8733 { 8734 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were 8735 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) 8736 g.NavWindow->NavLastIds[0] = 0; 8737 g.NavId = g.NavFocusScopeId = 0; 8738 } 8739 } 8740 8741 // Process manual activation request 8742 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; 8743 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8744 { 8745 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); 8746 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); 8747 if (g.ActiveId == 0 && activate_pressed) 8748 g.NavActivateId = g.NavId; 8749 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) 8750 g.NavActivateDownId = g.NavId; 8751 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) 8752 g.NavActivatePressedId = g.NavId; 8753 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) 8754 g.NavInputId = g.NavId; 8755 } 8756 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8757 g.NavDisableHighlight = true; 8758 if (g.NavActivateId != 0) 8759 IM_ASSERT(g.NavActivateDownId == g.NavActivateId); 8760 g.NavMoveRequest = false; 8761 8762 // Process programmatic activation request 8763 if (g.NavNextActivateId != 0) 8764 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; 8765 g.NavNextActivateId = 0; 8766 8767 // Initiate directional inputs request 8768 if (g.NavMoveRequestForward == ImGuiNavForward_None) 8769 { 8770 g.NavMoveDir = ImGuiDir_None; 8771 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; 8772 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8773 { 8774 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; 8775 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } 8776 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } 8777 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } 8778 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } 8779 } 8780 g.NavMoveClipDir = g.NavMoveDir; 8781 } 8782 else 8783 { 8784 // 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) 8785 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) 8786 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); 8787 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); 8788 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); 8789 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; 8790 } 8791 8792 // Update PageUp/PageDown/Home/End scroll 8793 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? 8794 float nav_scoring_rect_offset_y = 0.0f; 8795 if (nav_keyboard_active) 8796 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); 8797 8798 // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match 8799 if (g.NavMoveDir != ImGuiDir_None) 8800 { 8801 g.NavMoveRequest = true; 8802 g.NavMoveRequestKeyMods = io.KeyMods; 8803 g.NavMoveDirLast = g.NavMoveDir; 8804 } 8805 if (g.NavMoveRequest && g.NavId == 0) 8806 { 8807 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); 8808 g.NavInitRequest = g.NavInitRequestFromMove = true; 8809 // Reassigning with same value, we're being explicit here. 8810 g.NavInitResultId = 0; // -V1048 8811 g.NavDisableHighlight = false; 8812 } 8813 NavUpdateAnyRequestFlag(); 8814 8815 // Scrolling 8816 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) 8817 { 8818 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item 8819 ImGuiWindow* window = g.NavWindow; 8820 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. 8821 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) 8822 { 8823 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) 8824 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); 8825 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) 8826 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); 8827 } 8828 8829 // *Normal* Manual scroll with NavScrollXXX keys 8830 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. 8831 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); 8832 if (scroll_dir.x != 0.0f && window->ScrollbarX) 8833 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); 8834 if (scroll_dir.y != 0.0f) 8835 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); 8836 } 8837 8838 // Reset search results 8839 g.NavMoveResultLocal.Clear(); 8840 g.NavMoveResultLocalVisibleSet.Clear(); 8841 g.NavMoveResultOther.Clear(); 8842 8843 // When using gamepad, we project the reference nav bounding box into window visible area. 8844 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative 8845 // (can't focus a visible object like we can with the mouse). 8846 if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main) 8847 { 8848 ImGuiWindow* window = g.NavWindow; 8849 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); 8850 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) 8851 { 8852 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); 8853 float pad = window->CalcFontSize() * 0.5f; 8854 window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item 8855 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel); 8856 g.NavId = g.NavFocusScopeId = 0; 8857 } 8858 } 8859 8860 // 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) 8861 ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); 8862 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); 8863 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); 8864 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); 8865 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; 8866 IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). 8867 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] 8868 g.NavScoringCount = 0; 8869 #if IMGUI_DEBUG_NAV_RECTS 8870 if (g.NavWindow) 8871 { 8872 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); 8873 if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] 8874 if (1) { ImU32 col = (!g.NavWindow->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); } 8875 } 8876 #endif 8877 } 8878 8879 static void ImGui::NavUpdateInitResult() 8880 { 8881 // 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) 8882 ImGuiContext& g = *GImGui; 8883 if (!g.NavWindow) 8884 return; 8885 8886 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) 8887 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); 8888 if (g.NavInitRequestFromMove) 8889 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); 8890 else 8891 SetNavID(g.NavInitResultId, g.NavLayer, 0); 8892 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; 8893 } 8894 8895 // Apply result from previous frame navigation directional move request 8896 static void ImGui::NavUpdateMoveResult() 8897 { 8898 ImGuiContext& g = *GImGui; 8899 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 8900 { 8901 // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) 8902 if (g.NavId != 0) 8903 { 8904 g.NavDisableHighlight = false; 8905 g.NavDisableMouseHover = true; 8906 } 8907 return; 8908 } 8909 8910 // Select which result to use 8911 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 8912 8913 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. 8914 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) 8915 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) 8916 result = &g.NavMoveResultLocalVisibleSet; 8917 8918 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. 8919 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) 8920 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) 8921 result = &g.NavMoveResultOther; 8922 IM_ASSERT(g.NavWindow && result->Window); 8923 8924 // Scroll to keep newly navigated item fully into view. 8925 if (g.NavLayer == ImGuiNavLayer_Main) 8926 { 8927 ImVec2 delta_scroll; 8928 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) 8929 { 8930 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; 8931 delta_scroll.y = result->Window->Scroll.y - scroll_target; 8932 SetScrollY(result->Window, scroll_target); 8933 } 8934 else 8935 { 8936 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); 8937 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); 8938 } 8939 8940 // Offset our result position so mouse position can be applied immediately after in NavUpdate() 8941 result->RectRel.TranslateX(-delta_scroll.x); 8942 result->RectRel.TranslateY(-delta_scroll.y); 8943 } 8944 8945 ClearActiveID(); 8946 g.NavWindow = result->Window; 8947 if (g.NavId != result->ID) 8948 { 8949 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) 8950 g.NavJustMovedToId = result->ID; 8951 g.NavJustMovedToFocusScopeId = result->FocusScopeId; 8952 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; 8953 } 8954 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); 8955 SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); 8956 } 8957 8958 // Handle PageUp/PageDown/Home/End keys 8959 static float ImGui::NavUpdatePageUpPageDown() 8960 { 8961 ImGuiContext& g = *GImGui; 8962 ImGuiIO& io = g.IO; 8963 8964 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) 8965 return 0.0f; 8966 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) 8967 return 0.0f; 8968 8969 ImGuiWindow* window = g.NavWindow; 8970 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); 8971 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); 8972 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); 8973 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); 8974 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed 8975 { 8976 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) 8977 { 8978 // Fallback manual-scroll when window has no navigable item 8979 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) 8980 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); 8981 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) 8982 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); 8983 else if (home_pressed) 8984 SetScrollY(window, 0.0f); 8985 else if (end_pressed) 8986 SetScrollY(window, window->ScrollMax.y); 8987 } 8988 else 8989 { 8990 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; 8991 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); 8992 float nav_scoring_rect_offset_y = 0.0f; 8993 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) 8813 8994 { 8814 // Negative: rescale to the negative range before powering8815 float a = 1.0f - (clicked_t / linear_zero_pos);8816 a = powf(a, power);8817 new_value = ImLerp(ImMin(v_max, 0.0f), v_min, a);8995 nav_scoring_rect_offset_y = -page_offset_y; 8996 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) 8997 g.NavMoveClipDir = ImGuiDir_Up; 8998 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 8818 8999 } 8819 else 9000 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) 8820 9001 { 8821 // Positive: rescale to the positive range before powering 8822 float a; 8823 if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f) 8824 a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); 8825 else 8826 a = clicked_t; 8827 a = powf(a, power); 8828 new_value = ImLerp(ImMax(v_min, 0.0f), v_max, a); 9002 nav_scoring_rect_offset_y = +page_offset_y; 9003 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) 9004 g.NavMoveClipDir = ImGuiDir_Down; 9005 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 8829 9006 } 8830 } 8831 else 8832 { 8833 // Linear slider 8834 new_value = ImLerp(v_min, v_max, clicked_t); 8835 } 8836 8837 // Round past decimal precision 8838 new_value = RoundScalarWithFormat(format, new_value); 8839 if (*v != new_value) 8840 { 8841 *v = new_value; 8842 value_changed = true; 8843 } 8844 } 8845 } 8846 8847 // Draw 8848 float grab_t = SliderBehaviorCalcRatioFromValue(*v, v_min, v_max, power, linear_zero_pos); 8849 if (!is_horizontal) 8850 grab_t = 1.0f - grab_t; 8851 const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); 8852 ImRect grab_bb; 8853 if (is_horizontal) 8854 grab_bb = ImRect(ImVec2(grab_pos - grab_sz * 0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz * 0.5f, frame_bb.Max.y - grab_padding)); 8855 else 8856 grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f)); 8857 window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); 8858 8859 return value_changed; 8860 } 8861 8862 // Use power!=1.0 for logarithmic sliders. 8863 // Adjust format to decorate the value with a prefix or a suffix. 8864 // "%.3f" 1.234 8865 // "%5.2f secs" 01.23 secs 8866 // "Gold: %.0f" Gold: 1 8867 bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) 8868 { 8869 ImGuiWindow* window = GetCurrentWindow(); 8870 if (window->SkipItems) 8871 return false; 8872 8873 ImGuiContext& g = *GImGui; 8874 const ImGuiStyle& style = g.Style; 8875 const ImGuiID id = window->GetID(label); 8876 const float w = CalcItemWidth(); 8877 8878 const ImVec2 label_size = CalcTextSize(label, NULL, true); 8879 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); 8880 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 8881 8882 // NB- we don't call ItemSize() yet because we may turn into a text edit box below 8883 if (!ItemAdd(total_bb, id, &frame_bb)) 8884 { 8885 ItemSize(total_bb, style.FramePadding.y); 8886 return false; 8887 } 8888 const bool hovered = ItemHoverable(frame_bb, id); 8889 8890 if (!format) 8891 format = "%.3f"; 8892 8893 // Tabbing or CTRL-clicking on Slider turns it into an input box 8894 bool start_text_input = false; 8895 const bool tab_focus_requested = FocusableItemRegister(window, id); 8896 if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) 8897 { 8898 SetActiveID(id, window); 8899 SetFocusID(id, window); 8900 FocusWindow(window); 8901 g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); 8902 if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) 8903 { 8904 start_text_input = true; 8905 g.ScalarAsInputTextId = 0; 8906 } 8907 } 8908 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) 8909 return InputScalarAsWidgetReplacement(frame_bb, id, label, ImGuiDataType_Float, v, format); 8910 8911 // Actual slider behavior + render grab 8912 ItemSize(total_bb, style.FramePadding.y); 8913 const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, format, power); 8914 8915 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. 8916 char value_buf[64]; 8917 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), format, *v); 8918 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); 8919 8920 if (label_size.x > 0.0f) 8921 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 8922 8923 return value_changed; 8924 } 8925 8926 bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power) 8927 { 8928 ImGuiWindow* window = GetCurrentWindow(); 8929 if (window->SkipItems) 8930 return false; 8931 8932 ImGuiContext& g = *GImGui; 8933 const ImGuiStyle& style = g.Style; 8934 const ImGuiID id = window->GetID(label); 8935 8936 const ImVec2 label_size = CalcTextSize(label, NULL, true); 8937 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); 8938 const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 8939 8940 ItemSize(bb, style.FramePadding.y); 8941 if (!ItemAdd(frame_bb, id)) 8942 return false; 8943 const bool hovered = ItemHoverable(frame_bb, id); 8944 8945 if (!format) 8946 format = "%.3f"; 8947 8948 if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) 8949 { 8950 SetActiveID(id, window); 8951 SetFocusID(id, window); 8952 FocusWindow(window); 8953 g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); 8954 } 8955 8956 // Actual slider behavior + render grab 8957 bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical); 8958 8959 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. 8960 // For the vertical slider we allow centered text to overlap the frame padding 8961 char value_buf[64]; 8962 char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), format, *v); 8963 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f)); 8964 if (label_size.x > 0.0f) 8965 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 8966 8967 return value_changed; 8968 } 8969 8970 bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) 8971 { 8972 float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); 8973 bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); 8974 *v_rad = v_deg * (2 * IM_PI) / 360.0f; 8975 return value_changed; 8976 } 8977 8978 bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format) 8979 { 8980 if (!format) 8981 format = "%.0f"; 8982 float v_f = (float)*v; 8983 bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, format, 1.0f); 8984 *v = (int)v_f; 8985 return value_changed; 8986 } 8987 8988 bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format) 8989 { 8990 if (!format) 8991 format = "%.0f"; 8992 float v_f = (float)*v; 8993 bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, format, 1.0f); 8994 *v = (int)v_f; 8995 return value_changed; 8996 } 8997 8998 // Add multiple sliders on 1 line for compact edition of multiple components 8999 bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* format, float power) 9000 { 9001 ImGuiWindow* window = GetCurrentWindow(); 9002 if (window->SkipItems) 9003 return false; 9004 9005 ImGuiContext& g = *GImGui; 9006 bool value_changed = false; 9007 BeginGroup(); 9008 PushID(label); 9009 PushMultiItemsWidths(components); 9010 for (int i = 0; i < components; i++) 9011 { 9012 PushID(i); 9013 value_changed |= SliderFloat("##v", &v[i], v_min, v_max, format, power); 9014 SameLine(0, g.Style.ItemInnerSpacing.x); 9015 PopID(); 9016 PopItemWidth(); 9017 } 9018 PopID(); 9019 9020 TextUnformatted(label, FindRenderedTextEnd(label)); 9021 EndGroup(); 9022 9023 return value_changed; 9024 } 9025 9026 bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) 9027 { 9028 return SliderFloatN(label, v, 2, v_min, v_max, format, power); 9029 } 9030 9031 bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) 9032 { 9033 return SliderFloatN(label, v, 3, v_min, v_max, format, power); 9034 } 9035 9036 bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) 9037 { 9038 return SliderFloatN(label, v, 4, v_min, v_max, format, power); 9039 } 9040 9041 bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* format) 9042 { 9043 ImGuiWindow* window = GetCurrentWindow(); 9044 if (window->SkipItems) 9045 return false; 9046 9047 ImGuiContext& g = *GImGui; 9048 bool value_changed = false; 9049 BeginGroup(); 9050 PushID(label); 9051 PushMultiItemsWidths(components); 9052 for (int i = 0; i < components; i++) 9053 { 9054 PushID(i); 9055 value_changed |= SliderInt("##v", &v[i], v_min, v_max, format); 9056 SameLine(0, g.Style.ItemInnerSpacing.x); 9057 PopID(); 9058 PopItemWidth(); 9059 } 9060 PopID(); 9061 9062 TextUnformatted(label, FindRenderedTextEnd(label)); 9063 EndGroup(); 9064 9065 return value_changed; 9066 } 9067 9068 bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format) 9069 { 9070 return SliderIntN(label, v, 2, v_min, v_max, format); 9071 } 9072 9073 bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format) 9074 { 9075 return SliderIntN(label, v, 3, v_min, v_max, format); 9076 } 9077 9078 bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format) 9079 { 9080 return SliderIntN(label, v, 4, v_min, v_max, format); 9081 } 9082 9083 bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, const char* format, float power) 9084 { 9085 ImGuiContext& g = *GImGui; 9086 const ImGuiStyle& style = g.Style; 9087 9088 // Draw frame 9089 const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 9090 RenderNavHighlight(frame_bb, id); 9091 RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); 9092 9093 // Process interacting with the drag 9094 if (g.ActiveId == id) 9095 { 9096 if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) 9097 ClearActiveID(); 9098 else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) 9099 ClearActiveID(); 9100 } 9101 if (g.ActiveId != id) 9102 return false; 9103 9104 // Default tweak speed 9105 if (v_speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) 9106 v_speed = (v_max - v_min) * g.DragSpeedDefaultRatio; 9107 9108 if (g.ActiveIdIsJustActivated) 9109 { 9110 // Lock current value on click 9111 g.DragCurrentValue = *v; 9112 g.DragLastMouseDelta = ImVec2(0.f, 0.f); 9113 } 9114 9115 const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); 9116 float adjust_delta = 0.0f; 9117 if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid()) 9118 { 9119 adjust_delta = mouse_drag_delta.x - g.DragLastMouseDelta.x; 9120 if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) 9121 adjust_delta *= g.DragSpeedScaleFast; 9122 if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) 9123 adjust_delta *= g.DragSpeedScaleSlow; 9124 g.DragLastMouseDelta.x = mouse_drag_delta.x; 9125 } 9126 if (g.ActiveIdSource == ImGuiInputSource_Nav) 9127 { 9128 int decimal_precision = ParseFormatPrecision(format, 3); 9129 adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f).x; 9130 v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); 9131 } 9132 adjust_delta *= v_speed; 9133 9134 // Avoid applying the saturation when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300 9135 float v_cur = g.DragCurrentValue; 9136 if (v_min < v_max && ((v_cur >= v_max && adjust_delta > 0.0f) || (v_cur <= v_min && adjust_delta < 0.0f))) 9137 adjust_delta = 0.0f; 9138 9139 if (fabsf(adjust_delta) > 0.0f) 9140 { 9141 if (fabsf(power - 1.0f) > 0.001f) 9142 { 9143 // Logarithmic curve on both side of 0.0 9144 float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; 9145 float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; 9146 float v1 = powf(v0_abs, 1.0f / power) + (adjust_delta * v0_sign); 9147 float v1_abs = v1 >= 0.0f ? v1 : -v1; 9148 float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line 9149 v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign 9150 } 9151 else 9152 { 9153 v_cur += adjust_delta; 9154 } 9155 9156 // Clamp 9157 if (v_min < v_max) 9158 v_cur = ImClamp(v_cur, v_min, v_max); 9159 g.DragCurrentValue = v_cur; 9160 } 9161 9162 // Round to user desired precision, then apply 9163 bool value_changed = false; 9164 v_cur = RoundScalarWithFormat(format, v_cur); 9165 if (*v != v_cur) 9166 { 9167 *v = v_cur; 9168 value_changed = true; 9169 } 9170 9171 return value_changed; 9172 } 9173 9174 bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) 9175 { 9176 ImGuiWindow* window = GetCurrentWindow(); 9177 if (window->SkipItems) 9178 return false; 9179 9180 ImGuiContext& g = *GImGui; 9181 const ImGuiStyle& style = g.Style; 9182 const ImGuiID id = window->GetID(label); 9183 const float w = CalcItemWidth(); 9184 9185 const ImVec2 label_size = CalcTextSize(label, NULL, true); 9186 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); 9187 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); 9188 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 9189 9190 // NB- we don't call ItemSize() yet because we may turn into a text edit box below 9191 if (!ItemAdd(total_bb, id, &frame_bb)) 9192 { 9193 ItemSize(total_bb, style.FramePadding.y); 9194 return false; 9195 } 9196 const bool hovered = ItemHoverable(frame_bb, id); 9197 9198 if (!format) 9199 format = "%.3f"; 9200 9201 // Tabbing or CTRL-clicking on Drag turns it into an input box 9202 bool start_text_input = false; 9203 const bool tab_focus_requested = FocusableItemRegister(window, id); 9204 if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) 9205 { 9206 SetActiveID(id, window); 9207 SetFocusID(id, window); 9208 FocusWindow(window); 9209 g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); 9210 if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) 9211 { 9212 start_text_input = true; 9213 g.ScalarAsInputTextId = 0; 9214 } 9215 } 9216 if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) 9217 return InputScalarAsWidgetReplacement(frame_bb, id, label, ImGuiDataType_Float, v, format); 9218 9219 // Actual drag behavior 9220 ItemSize(total_bb, style.FramePadding.y); 9221 const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, format, power); 9222 9223 // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. 9224 char value_buf[64]; 9225 const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), format, *v); 9226 RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); 9227 9228 if (label_size.x > 0.0f) 9229 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); 9230 9231 return value_changed; 9232 } 9233 9234 bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* format, float power) 9235 { 9236 ImGuiWindow* window = GetCurrentWindow(); 9237 if (window->SkipItems) 9238 return false; 9239 9240 ImGuiContext& g = *GImGui; 9241 bool value_changed = false; 9242 BeginGroup(); 9243 PushID(label); 9244 PushMultiItemsWidths(components); 9245 for (int i = 0; i < components; i++) 9246 { 9247 PushID(i); 9248 value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, format, power); 9249 SameLine(0, g.Style.ItemInnerSpacing.x); 9250 PopID(); 9251 PopItemWidth(); 9252 } 9253 PopID(); 9254 9255 TextUnformatted(label, FindRenderedTextEnd(label)); 9256 EndGroup(); 9257 9258 return value_changed; 9259 } 9260 9261 bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) 9262 { 9263 return DragFloatN(label, v, 2, v_speed, v_min, v_max, format, power); 9264 } 9265 9266 bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) 9267 { 9268 return DragFloatN(label, v, 3, v_speed, v_min, v_max, format, power); 9269 } 9270 9271 bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) 9272 { 9273 return DragFloatN(label, v, 4, v_speed, v_min, v_max, format, power); 9274 } 9275 9276 bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power) 9277 { 9278 ImGuiWindow* window = GetCurrentWindow(); 9279 if (window->SkipItems) 9280 return false; 9281 9282 ImGuiContext& g = *GImGui; 9283 PushID(label); 9284 BeginGroup(); 9285 PushMultiItemsWidths(2); 9286 9287 bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power); 9288 PopItemWidth(); 9289 SameLine(0, g.Style.ItemInnerSpacing.x); 9290 value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power); 9291 PopItemWidth(); 9292 SameLine(0, g.Style.ItemInnerSpacing.x); 9293 9294 TextUnformatted(label, FindRenderedTextEnd(label)); 9295 EndGroup(); 9296 PopID(); 9297 9298 return value_changed; 9299 } 9300 9301 // NB: v_speed is float to allow adjusting the drag speed with more precision 9302 bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format) 9303 { 9304 if (!format) 9305 format = "%.0f"; 9306 float v_f = (float)*v; 9307 bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, format); 9308 *v = (int)v_f; 9309 return value_changed; 9310 } 9311 9312 bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* format) 9313 { 9314 ImGuiWindow* window = GetCurrentWindow(); 9315 if (window->SkipItems) 9316 return false; 9317 9318 ImGuiContext& g = *GImGui; 9319 bool value_changed = false; 9320 BeginGroup(); 9321 PushID(label); 9322 PushMultiItemsWidths(components); 9323 for (int i = 0; i < components; i++) 9324 { 9325 PushID(i); 9326 value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, format); 9327 SameLine(0, g.Style.ItemInnerSpacing.x); 9328 PopID(); 9329 PopItemWidth(); 9330 } 9331 PopID(); 9332 9333 TextUnformatted(label, FindRenderedTextEnd(label)); 9334 EndGroup(); 9335 9336 return value_changed; 9337 } 9338 9339 bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format) 9340 { 9341 return DragIntN(label, v, 2, v_speed, v_min, v_max, format); 9342 } 9343 9344 bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format) 9345 { 9346 return DragIntN(label, v, 3, v_speed, v_min, v_max, format); 9347 } 9348 9349 bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format) 9350 { 9351 return DragIntN(label, v, 4, v_speed, v_min, v_max, format); 9352 } 9353 9354 bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max) 9355 { 9356 ImGuiWindow* window = GetCurrentWindow(); 9357 if (window->SkipItems) 9358 return false; 9359 9360 ImGuiContext& g = *GImGui; 9361 PushID(label); 9362 BeginGroup(); 9363 PushMultiItemsWidths(2); 9364 9365 bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format); 9366 PopItemWidth(); 9367 SameLine(0, g.Style.ItemInnerSpacing.x); 9368 value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format); 9369 PopItemWidth(); 9370 SameLine(0, g.Style.ItemInnerSpacing.x); 9371 9372 TextUnformatted(label, FindRenderedTextEnd(label)); 9373 EndGroup(); 9374 PopID(); 9375 9376 return value_changed; 9377 } 9378 9379 void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) 9380 { 9381 ImGuiWindow* window = GetCurrentWindow(); 9382 if (window->SkipItems) 9383 return; 9384 9385 ImGuiContext& g = *GImGui; 9386 const ImGuiStyle& style = g.Style; 9387 9388 const ImVec2 label_size = CalcTextSize(label, NULL, true); 9389 if (graph_size.x == 0.0f) 9390 graph_size.x = CalcItemWidth(); 9391 if (graph_size.y == 0.0f) 9392 graph_size.y = label_size.y + (style.FramePadding.y * 2); 9393 9394 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); 9395 const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); 9396 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); 9397 ItemSize(total_bb, style.FramePadding.y); 9398 if (!ItemAdd(total_bb, 0, &frame_bb)) 9399 return; 9400 const bool hovered = ItemHoverable(inner_bb, 0); 9401 9402 // Determine scale from values if not specified 9403 if (scale_min == FLT_MAX || scale_max == FLT_MAX) 9404 { 9405 float v_min = FLT_MAX; 9406 float v_max = -FLT_MAX; 9407 for (int i = 0; i < values_count; i++) 9408 { 9409 const float v = values_getter(data, i); 9410 v_min = ImMin(v_min, v); 9411 v_max = ImMax(v_max, v); 9412 } 9413 if (scale_min == FLT_MAX) 9414 scale_min = v_min; 9415 if (scale_max == FLT_MAX) 9416 scale_max = v_max; 9417 } 9418 9419 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); 9420 9421 if (values_count > 0) 9422 { 9423 int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 9424 int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 9425 9426 // Tooltip on hover 9427 int v_hovered = -1; 9428 if (hovered) 9429 { 9430 const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); 9431 const int v_idx = (int)(t * item_count); 9432 IM_ASSERT(v_idx >= 0 && v_idx < values_count); 9433 9434 const float v0 = values_getter(data, (v_idx + values_offset) % values_count); 9435 const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); 9436 if (plot_type == ImGuiPlotType_Lines) 9437 SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); 9438 else if (plot_type == ImGuiPlotType_Histogram) 9439 SetTooltip("%d: %8.4g", v_idx, v0); 9440 v_hovered = v_idx; 9441 } 9442 9443 const float t_step = 1.0f / (float)res_w; 9444 const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); 9445 9446 float v0 = values_getter(data, (0 + values_offset) % values_count); 9447 float t0 = 0.0f; 9448 ImVec2 tp0 = ImVec2(t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale)); // Point in the normalized space of our target rectangle 9449 float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands 9450 9451 const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); 9452 const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); 9453 9454 for (int n = 0; n < res_w; n++) 9455 { 9456 const float t1 = t0 + t_step; 9457 const int v1_idx = (int)(t0 * item_count + 0.5f); 9458 IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); 9459 const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); 9460 const ImVec2 tp1 = ImVec2(t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale)); 9461 9462 // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. 9463 ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); 9464 ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); 9465 if (plot_type == ImGuiPlotType_Lines) 9466 { 9467 window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); 9468 } 9469 else if (plot_type == ImGuiPlotType_Histogram) 9470 { 9471 if (pos1.x >= pos0.x + 2.0f) 9472 pos1.x -= 1.0f; 9473 window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); 9474 } 9475 9476 t0 = t1; 9477 tp0 = tp1; 9478 } 9479 } 9480 9481 // Text overlay 9482 if (overlay_text) 9483 RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); 9484 9485 if (label_size.x > 0.0f) 9486 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); 9487 } 9488 9489 struct ImGuiPlotArrayGetterData 9490 { 9491 const float* Values; 9492 int Stride; 9493 9494 ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } 9495 }; 9496 9497 static float Plot_ArrayGetter(void* data, int idx) 9498 { 9499 ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; 9500 const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); 9501 return v; 9502 } 9503 9504 void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) 9505 { 9506 ImGuiPlotArrayGetterData data(values, stride); 9507 PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); 9508 } 9509 9510 void ImGui::PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) 9511 { 9512 PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); 9513 } 9514 9515 void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) 9516 { 9517 ImGuiPlotArrayGetterData data(values, stride); 9518 PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); 9519 } 9520 9521 void ImGui::PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) 9522 { 9523 PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); 9524 } 9525 9526 // size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size 9527 void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) 9528 { 9529 ImGuiWindow* window = GetCurrentWindow(); 9530 if (window->SkipItems) 9531 return; 9532 9533 ImGuiContext& g = *GImGui; 9534 const ImGuiStyle& style = g.Style; 9535 9536 ImVec2 pos = window->DC.CursorPos; 9537 ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); 9538 ItemSize(bb, style.FramePadding.y); 9539 if (!ItemAdd(bb, 0)) 9540 return; 9541 9542 // Render 9543 fraction = ImSaturate(fraction); 9544 RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); 9545 bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); 9546 const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); 9547 RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); 9548 9549 // Default displaying the fraction as percentage string, but user can override it 9550 char overlay_buf[32]; 9551 if (!overlay) 9552 { 9553 ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f); 9554 overlay = overlay_buf; 9555 } 9556 9557 ImVec2 overlay_size = CalcTextSize(overlay, NULL); 9558 if (overlay_size.x > 0.0f) 9559 RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb); 9560 } 9561 9562 bool ImGui::Checkbox(const char* label, bool* v) 9563 { 9564 ImGuiWindow* window = GetCurrentWindow(); 9565 if (window->SkipItems) 9566 return false; 9567 9568 ImGuiContext& g = *GImGui; 9569 const ImGuiStyle& style = g.Style; 9570 const ImGuiID id = window->GetID(label); 9571 const ImVec2 label_size = CalcTextSize(label, NULL, true); 9572 9573 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y * 2, label_size.y + style.FramePadding.y * 2)); // We want a square shape to we use Y twice 9574 ItemSize(check_bb, style.FramePadding.y); 9575 9576 ImRect total_bb = check_bb; 9577 if (label_size.x > 0) 9578 SameLine(0, style.ItemInnerSpacing.x); 9579 const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); 9580 if (label_size.x > 0) 9581 { 9582 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); 9583 total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); 9584 } 9585 9586 if (!ItemAdd(total_bb, id)) 9587 return false; 9588 9589 bool hovered, held; 9590 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); 9591 if (pressed) 9592 *v = !(*v); 9593 9594 RenderNavHighlight(total_bb, id); 9595 RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); 9596 if (*v) 9597 { 9598 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); 9599 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); 9600 RenderCheckMark(check_bb.Min + ImVec2(pad, pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad * 2.0f); 9601 } 9602 9603 if (g.LogEnabled) 9604 LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); 9605 if (label_size.x > 0.0f) 9606 RenderText(text_bb.Min, label); 9607 9608 return pressed; 9609 } 9610 9611 bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) 9612 { 9613 bool v = ((*flags & flags_value) == flags_value); 9614 bool pressed = Checkbox(label, &v); 9615 if (pressed) 9616 { 9617 if (v) 9618 *flags |= flags_value; 9619 else 9620 *flags &= ~flags_value; 9621 } 9622 9623 return pressed; 9624 } 9625 9626 bool ImGui::RadioButton(const char* label, bool active) 9627 { 9628 ImGuiWindow* window = GetCurrentWindow(); 9629 if (window->SkipItems) 9630 return false; 9631 9632 ImGuiContext& g = *GImGui; 9633 const ImGuiStyle& style = g.Style; 9634 const ImGuiID id = window->GetID(label); 9635 const ImVec2 label_size = CalcTextSize(label, NULL, true); 9636 9637 const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y * 2 - 1, label_size.y + style.FramePadding.y * 2 - 1)); 9638 ItemSize(check_bb, style.FramePadding.y); 9639 9640 ImRect total_bb = check_bb; 9641 if (label_size.x > 0) 9642 SameLine(0, style.ItemInnerSpacing.x); 9643 const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); 9644 if (label_size.x > 0) 9645 { 9646 ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); 9647 total_bb.Add(text_bb); 9648 } 9649 9650 if (!ItemAdd(total_bb, id)) 9651 return false; 9652 9653 ImVec2 center = check_bb.GetCenter(); 9654 center.x = (float)(int)center.x + 0.5f; 9655 center.y = (float)(int)center.y + 0.5f; 9656 const float radius = check_bb.GetHeight() * 0.5f; 9657 9658 bool hovered, held; 9659 bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); 9660 9661 RenderNavHighlight(total_bb, id); 9662 window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); 9663 if (active) 9664 { 9665 const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); 9666 const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); 9667 window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); 9668 } 9669 9670 if (style.FrameBorderSize > 0.0f) 9671 { 9672 window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); 9673 window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); 9674 } 9675 9676 if (g.LogEnabled) 9677 LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); 9678 if (label_size.x > 0.0f) 9679 RenderText(text_bb.Min, label); 9680 9681 return pressed; 9682 } 9683 9684 bool ImGui::RadioButton(const char* label, int* v, int v_button) 9685 { 9686 const bool pressed = RadioButton(label, *v == v_button); 9687 if (pressed) 9688 { 9689 *v = v_button; 9690 } 9691 return pressed; 9692 } 9693 9694 static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) 9695 { 9696 int line_count = 0; 9697 const char* s = text_begin; 9698 while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding 9699 if (c == '\n') 9700 line_count++; 9701 s--; 9702 if (s[0] != '\n' && s[0] != '\r') 9703 line_count++; 9704 *out_text_end = s; 9705 return line_count; 9706 } 9707 9708 static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) 9709 { 9710 ImFont* font = GImGui->Font; 9711 const float line_height = GImGui->FontSize; 9712 const float scale = line_height / font->FontSize; 9713 9714 ImVec2 text_size = ImVec2(0, 0); 9715 float line_width = 0.0f; 9716 9717 const ImWchar* s = text_begin; 9718 while (s < text_end) 9719 { 9720 unsigned int c = (unsigned int)(*s++); 9721 if (c == '\n') 9722 { 9723 text_size.x = ImMax(text_size.x, line_width); 9724 text_size.y += line_height; 9725 line_width = 0.0f; 9726 if (stop_on_new_line) 9727 break; 9728 continue; 9729 } 9730 if (c == '\r') 9731 continue; 9732 9733 const float char_width = font->GetCharAdvance((unsigned short)c) * scale; 9734 line_width += char_width; 9735 } 9736 9737 if (text_size.x < line_width) 9738 text_size.x = line_width; 9739 9740 if (out_offset) 9741 *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n 9742 9743 if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n 9744 text_size.y += line_height; 9745 9746 if (remaining) 9747 *remaining = s; 9748 9749 return text_size; 9750 } 9751 9752 // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) 9753 namespace ImGuiStb 9754 { 9755 9756 static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } 9757 static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } 9758 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } 9759 static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } 9760 static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; 9761 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) 9762 { 9763 const ImWchar* text = obj->Text.Data; 9764 const ImWchar* text_remaining = NULL; 9765 const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); 9766 r->x0 = 0.0f; 9767 r->x1 = size.x; 9768 r->baseline_y_delta = size.y; 9769 r->ymin = 0.0f; 9770 r->ymax = size.y; 9771 r->num_chars = (int)(text_remaining - (text + line_start_idx)); 9772 } 9773 9774 static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c == ',' || c == ';' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '|'; } 9775 static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->Text[idx - 1]) && !is_separator(obj->Text[idx])) : 1; } 9776 static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } 9777 #ifdef __APPLE__ // FIXME: Move setting to IO structure 9778 static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->Text[idx - 1]) && is_separator(obj->Text[idx])) : 1; } 9779 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } 9780 #else 9781 static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } 9782 #endif 9783 #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h 9784 #define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL 9785 9786 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) 9787 { 9788 ImWchar* dst = obj->Text.Data + pos; 9789 9790 // We maintain our buffer length in both UTF-8 and wchar formats 9791 obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); 9792 obj->CurLenW -= n; 9793 9794 // Offset remaining text 9795 const ImWchar* src = obj->Text.Data + pos + n; 9796 while (ImWchar c = *src++) 9797 *dst++ = c; 9798 *dst = '\0'; 9799 } 9800 9801 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) 9802 { 9803 const int text_len = obj->CurLenW; 9804 IM_ASSERT(pos <= text_len); 9805 if (new_text_len + text_len + 1 > obj->Text.Size) 9806 return false; 9807 9808 const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); 9809 if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) 9810 return false; 9811 9812 ImWchar* text = obj->Text.Data; 9813 if (pos != text_len) 9814 memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); 9815 memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); 9816 9817 obj->CurLenW += new_text_len; 9818 obj->CurLenA += new_text_len_utf8; 9819 obj->Text[obj->CurLenW] = '\0'; 9820 9821 return true; 9822 } 9823 9824 // We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) 9825 #define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left 9826 #define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right 9827 #define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up 9828 #define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down 9829 #define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line 9830 #define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line 9831 #define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text 9832 #define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text 9833 #define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor 9834 #define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor 9835 #define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo 9836 #define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo 9837 #define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word 9838 #define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word 9839 #define STB_TEXTEDIT_K_SHIFT 0x20000 9840 9841 #define STB_TEXTEDIT_IMPLEMENTATION 9842 #include "stb_textedit.h" 9843 9844 } 9845 9846 void ImGuiTextEditState::OnKeyPressed(int key) 9847 { 9848 stb_textedit_key(this, &StbState, key); 9849 CursorFollow = true; 9850 CursorAnimReset(); 9851 } 9852 9853 // Public API to manipulate UTF-8 text 9854 // We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) 9855 // FIXME: The existence of this rarely exercised code path is a bit of a nuisance. 9856 void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) 9857 { 9858 IM_ASSERT(pos + bytes_count <= BufTextLen); 9859 char* dst = Buf + pos; 9860 const char* src = Buf + pos + bytes_count; 9861 while (char c = *src++) 9862 *dst++ = c; 9863 *dst = '\0'; 9864 9865 if (CursorPos + bytes_count >= pos) 9866 CursorPos -= bytes_count; 9867 else if (CursorPos >= pos) 9868 CursorPos = pos; 9869 SelectionStart = SelectionEnd = CursorPos; 9870 BufDirty = true; 9871 BufTextLen -= bytes_count; 9872 } 9873 9874 void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) 9875 { 9876 const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); 9877 if (new_text_len + BufTextLen + 1 >= BufSize) 9878 return; 9879 9880 if (BufTextLen != pos) 9881 memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); 9882 memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); 9883 Buf[BufTextLen + new_text_len] = '\0'; 9884 9885 if (CursorPos >= pos) 9886 CursorPos += new_text_len; 9887 SelectionStart = SelectionEnd = CursorPos; 9888 BufDirty = true; 9889 BufTextLen += new_text_len; 9890 } 9891 9892 // Return false to discard a character. 9893 static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) 9894 { 9895 unsigned int c = *p_char; 9896 9897 if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) 9898 { 9899 bool pass = false; 9900 pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); 9901 pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); 9902 if (!pass) 9903 return false; 9904 } 9905 9906 if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. 9907 return false; 9908 9909 if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) 9910 { 9911 if (flags & ImGuiInputTextFlags_CharsDecimal) 9912 if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) 9913 return false; 9914 9915 if (flags & ImGuiInputTextFlags_CharsScientific) 9916 if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) 9917 return false; 9918 9919 if (flags & ImGuiInputTextFlags_CharsHexadecimal) 9920 if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) 9921 return false; 9922 9923 if (flags & ImGuiInputTextFlags_CharsUppercase) 9924 if (c >= 'a' && c <= 'z') 9925 *p_char = (c += (unsigned int)('A' - 'a')); 9926 9927 if (flags & ImGuiInputTextFlags_CharsNoBlank) 9928 if (ImCharIsSpace(c)) 9929 return false; 9930 } 9931 9932 if (flags & ImGuiInputTextFlags_CallbackCharFilter) 9933 { 9934 ImGuiTextEditCallbackData callback_data; 9935 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); 9936 callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; 9937 callback_data.EventChar = (ImWchar)c; 9938 callback_data.Flags = flags; 9939 callback_data.UserData = user_data; 9940 if (callback(&callback_data) != 0) 9941 return false; 9942 *p_char = callback_data.EventChar; 9943 if (!callback_data.EventChar) 9944 return false; 9945 } 9946 9947 return true; 9948 } 9949 9950 // Edit a string of text 9951 // NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. 9952 // FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 9953 bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) 9954 { 9955 ImGuiWindow* window = GetCurrentWindow(); 9956 if (window->SkipItems) 9957 return false; 9958 9959 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) 9960 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) 9961 9962 ImGuiContext& g = *GImGui; 9963 const ImGuiIO& io = g.IO; 9964 const ImGuiStyle& style = g.Style; 9965 9966 const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; 9967 const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; 9968 const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; 9969 const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; 9970 9971 if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn 9972 BeginGroup(); 9973 const ImGuiID id = window->GetID(label); 9974 const ImVec2 label_size = CalcTextSize(label, NULL, true); 9975 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line 9976 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); 9977 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); 9978 9979 ImGuiWindow* draw_window = window; 9980 if (is_multiline) 9981 { 9982 ItemAdd(total_bb, id, &frame_bb); 9983 if (!BeginChildFrame(id, frame_bb.GetSize())) 9984 { 9985 EndChildFrame(); 9986 EndGroup(); 9987 return false; 9988 } 9989 draw_window = GetCurrentWindow(); 9990 size.x -= draw_window->ScrollbarSizes.x; 9991 } 9992 else 9993 { 9994 ItemSize(total_bb, style.FramePadding.y); 9995 if (!ItemAdd(total_bb, id, &frame_bb)) 9996 return false; 9997 } 9998 const bool hovered = ItemHoverable(frame_bb, id); 9999 if (hovered) 10000 g.MouseCursor = ImGuiMouseCursor_TextInput; 10001 10002 // Password pushes a temporary font with only a fallback glyph 10003 if (is_password) 10004 { 10005 const ImFontGlyph* glyph = g.Font->FindGlyph('*'); 10006 ImFont* password_font = &g.InputTextPasswordFont; 10007 password_font->FontSize = g.Font->FontSize; 10008 password_font->Scale = g.Font->Scale; 10009 password_font->DisplayOffset = g.Font->DisplayOffset; 10010 password_font->Ascent = g.Font->Ascent; 10011 password_font->Descent = g.Font->Descent; 10012 password_font->ContainerAtlas = g.Font->ContainerAtlas; 10013 password_font->FallbackGlyph = glyph; 10014 password_font->FallbackAdvanceX = glyph->AdvanceX; 10015 IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); 10016 PushFont(password_font); 10017 } 10018 10019 // NB: we are only allowed to access 'edit_state' if we are the active widget. 10020 ImGuiTextEditState& edit_state = g.InputTextState; 10021 10022 const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing 10023 const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); 10024 const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; 10025 10026 const bool user_clicked = hovered && io.MouseClicked[0]; 10027 const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); 10028 const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); 10029 10030 bool clear_active_id = false; 10031 10032 bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); 10033 if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) 10034 { 10035 if (g.ActiveId != id) 10036 { 10037 // Start edition 10038 // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) 10039 // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) 10040 const int prev_len_w = edit_state.CurLenW; 10041 edit_state.Text.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. 10042 edit_state.InitialText.resize(buf_size + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. 10043 ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); 10044 const char* buf_end = NULL; 10045 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); 10046 edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. 10047 edit_state.CursorAnimReset(); 10048 10049 // Preserve cursor position and undo/redo stack if we come back to same widget 10050 // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). 10051 const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); 10052 if (recycle_state) 10053 { 10054 // Recycle existing cursor/selection/undo stack but clamp position 10055 // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. 10056 edit_state.CursorClamp(); 10057 } 10058 else 10059 { 10060 edit_state.Id = id; 10061 edit_state.ScrollX = 0.0f; 10062 stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); 10063 if (!is_multiline && focus_requested_by_code) 10064 select_all = true; 10065 } 10066 if (flags & ImGuiInputTextFlags_AlwaysInsertMode) 10067 edit_state.StbState.insert_mode = true; 10068 if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) 10069 select_all = true; 10070 } 10071 SetActiveID(id, window); 10072 SetFocusID(id, window); 10073 FocusWindow(window); 10074 if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) 10075 g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); 10076 } 10077 else if (io.MouseClicked[0]) 10078 { 10079 // Release focus when we click outside 10080 clear_active_id = true; 10081 } 10082 10083 bool value_changed = false; 10084 bool enter_pressed = false; 10085 10086 if (g.ActiveId == id) 10087 { 10088 if (!is_editable && !g.ActiveIdIsJustActivated) 10089 { 10090 // When read-only we always use the live data passed to the function 10091 edit_state.Text.resize(buf_size + 1); 10092 const char* buf_end = NULL; 10093 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); 10094 edit_state.CurLenA = (int)(buf_end - buf); 10095 edit_state.CursorClamp(); 10096 } 10097 10098 edit_state.BufSizeA = buf_size; 10099 10100 // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. 10101 // Down the line we should have a cleaner library-wide concept of Selected vs Active. 10102 g.ActiveIdAllowOverlap = !io.MouseDown[0]; 10103 g.WantTextInputNextFrame = 1; 10104 10105 // Edit in progress 10106 const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; 10107 const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); 10108 10109 const bool is_osx = io.OptMacOSXBehaviors; 10110 if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) 10111 { 10112 edit_state.SelectAll(); 10113 edit_state.SelectedAllMouseLock = true; 10114 } 10115 else if (hovered && is_osx && io.MouseDoubleClicked[0]) 10116 { 10117 // Double-click select a word only, OS X style (by simulating keystrokes) 10118 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); 10119 edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); 10120 } 10121 else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) 10122 { 10123 if (hovered) 10124 { 10125 stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); 10126 edit_state.CursorAnimReset(); 10127 } 10128 } 10129 else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) 10130 { 10131 stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); 10132 edit_state.CursorAnimReset(); 10133 edit_state.CursorFollow = true; 10134 } 10135 if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) 10136 edit_state.SelectedAllMouseLock = false; 10137 10138 if (io.InputCharacters[0]) 10139 { 10140 // Process text input (before we check for Return because using some IME will effectively send a Return?) 10141 // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. 10142 bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); 10143 if (!ignore_inputs && is_editable && !user_nav_input_start) 10144 for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) 9007 else if (home_pressed) 10145 9008 { 10146 // Insert character if they pass filtering 10147 unsigned int c = (unsigned int)io.InputCharacters[n]; 10148 if (InputTextFilterCharacter(&c, flags, callback, user_data)) 10149 edit_state.OnKeyPressed((int)c); 9009 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y 9010 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. 9011 // Preserve current horizontal position if we have any. 9012 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; 9013 if (nav_rect_rel.IsInverted()) 9014 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 9015 g.NavMoveDir = ImGuiDir_Down; 9016 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 10150 9017 } 10151 10152 // Consume characters 10153 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); 10154 } 10155 } 10156 10157 bool cancel_edit = false; 10158 if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) 10159 { 10160 // Handle key-presses 10161 const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); 10162 const bool is_osx = io.OptMacOSXBehaviors; 10163 const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl 10164 const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt; 10165 const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl 10166 const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End 10167 const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; 10168 const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; 10169 10170 const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); 10171 const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); 10172 const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; 10173 const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); 10174 const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; 10175 10176 if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } 10177 else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } 10178 else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } 10179 else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } 10180 else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } 10181 else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } 10182 else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } 10183 else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) 10184 { 10185 if (!edit_state.HasSelection()) 10186 { 10187 if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT); 10188 else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT); 10189 } 10190 edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); 10191 } 10192 else if (IsKeyPressedMap(ImGuiKey_Enter)) 10193 { 10194 bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; 10195 if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) 10196 { 10197 enter_pressed = clear_active_id = true; 10198 } 10199 else if (is_editable) 10200 { 10201 unsigned int c = '\n'; // Insert new line 10202 if (InputTextFilterCharacter(&c, flags, callback, user_data)) 10203 edit_state.OnKeyPressed((int)c); 10204 } 10205 } 10206 else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) 10207 { 10208 unsigned int c = '\t'; // Insert TAB 10209 if (InputTextFilterCharacter(&c, flags, callback, user_data)) 10210 edit_state.OnKeyPressed((int)c); 10211 } 10212 else if (IsKeyPressedMap(ImGuiKey_Escape)) 10213 { 10214 clear_active_id = cancel_edit = true; 10215 } 10216 else if (is_undo || is_redo) 10217 { 10218 edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); 10219 edit_state.ClearSelection(); 10220 } 10221 else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) 10222 { 10223 edit_state.SelectAll(); 10224 edit_state.CursorFollow = true; 10225 } 10226 else if (is_cut || is_copy) 10227 { 10228 // Cut, Copy 10229 if (io.SetClipboardTextFn) 10230 { 10231 const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; 10232 const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; 10233 edit_state.TempTextBuffer.resize((ie - ib) * 4 + 1); 10234 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data + ib, edit_state.Text.Data + ie); 10235 SetClipboardText(edit_state.TempTextBuffer.Data); 10236 } 10237 if (is_cut) 10238 { 10239 if (!edit_state.HasSelection()) 10240 edit_state.SelectAll(); 10241 edit_state.CursorFollow = true; 10242 stb_textedit_cut(&edit_state, &edit_state.StbState); 10243 } 10244 } 10245 else if (is_paste) 10246 { 10247 if (const char* clipboard = GetClipboardText()) 10248 { 10249 // Filter pasted buffer 10250 const int clipboard_len = (int)strlen(clipboard); 10251 ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len + 1) * sizeof(ImWchar)); 10252 int clipboard_filtered_len = 0; 10253 for (const char* s = clipboard; *s; ) 9018 else if (end_pressed) 10254 9019 { 10255 unsigned int c; 10256 s += ImTextCharFromUtf8(&c, s, NULL); 10257 if (c == 0) 10258 break; 10259 if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) 10260 continue; 10261 clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; 9020 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; 9021 if (nav_rect_rel.IsInverted()) 9022 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 9023 g.NavMoveDir = ImGuiDir_Up; 9024 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 10262 9025 } 10263 clipboard_filtered[clipboard_filtered_len] = 0; 10264 if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation 9026 return nav_scoring_rect_offset_y; 9027 } 9028 } 9029 return 0.0f; 9030 } 9031 9032 static void ImGui::NavEndFrame() 9033 { 9034 ImGuiContext& g = *GImGui; 9035 9036 // Show CTRL+TAB list window 9037 if (g.NavWindowingTarget != NULL) 9038 NavUpdateWindowingOverlay(); 9039 9040 // Perform wrap-around in menus 9041 ImGuiWindow* window = g.NavWrapRequestWindow; 9042 ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; 9043 if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) 9044 { 9045 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping 9046 ImRect bb_rel = window->NavRectRel[0]; 9047 9048 ImGuiDir clip_dir = g.NavMoveDir; 9049 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 9050 { 9051 bb_rel.Min.x = bb_rel.Max.x = 9052 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; 9053 if (move_flags & ImGuiNavMoveFlags_WrapX) 10265 9054 { 10266 stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len);10267 edit_state.CursorFollow = true;9055 bb_rel.TranslateY(-bb_rel.GetHeight()); 9056 clip_dir = ImGuiDir_Up; 10268 9057 } 10269 ImGui::MemFree(clipboard_filtered); 10270 } 10271 } 10272 } 10273 10274 if (g.ActiveId == id) 10275 { 10276 if (cancel_edit) 10277 { 10278 // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. 10279 if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0) 10280 { 10281 ImStrncpy(buf, edit_state.InitialText.Data, buf_size); 10282 value_changed = true; 10283 } 10284 } 10285 10286 // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. 10287 // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. 10288 bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); 10289 if (apply_edit_back_to_user_buffer) 10290 { 10291 // Apply new value immediately - copy modified buffer back 10292 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer 10293 // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. 10294 // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. 10295 if (is_editable) 10296 { 10297 edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); 10298 ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); 10299 } 10300 10301 // User callback 10302 if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) 10303 { 10304 IM_ASSERT(callback != NULL); 10305 10306 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. 10307 ImGuiInputTextFlags event_flag = 0; 10308 ImGuiKey event_key = ImGuiKey_COUNT; 10309 if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) 9058 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9059 } 9060 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 9061 { 9062 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; 9063 if (move_flags & ImGuiNavMoveFlags_WrapX) 10310 9064 { 10311 event_flag = ImGuiInputTextFlags_CallbackCompletion;10312 event_key = ImGuiKey_Tab;9065 bb_rel.TranslateY(+bb_rel.GetHeight()); 9066 clip_dir = ImGuiDir_Down; 10313 9067 } 10314 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) 9068 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9069 } 9070 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 9071 { 9072 bb_rel.Min.y = bb_rel.Max.y = 9073 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; 9074 if (move_flags & ImGuiNavMoveFlags_WrapY) 10315 9075 { 10316 event_flag = ImGuiInputTextFlags_CallbackHistory;10317 event_key = ImGuiKey_UpArrow;9076 bb_rel.TranslateX(-bb_rel.GetWidth()); 9077 clip_dir = ImGuiDir_Left; 10318 9078 } 10319 else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) 9079 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9080 } 9081 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 9082 { 9083 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; 9084 if (move_flags & ImGuiNavMoveFlags_WrapY) 10320 9085 { 10321 event_flag = ImGuiInputTextFlags_CallbackHistory;10322 event_key = ImGuiKey_DownArrow;9086 bb_rel.TranslateX(+bb_rel.GetWidth()); 9087 clip_dir = ImGuiDir_Right; 10323 9088 } 10324 else if (flags & ImGuiInputTextFlags_CallbackAlways) 10325 event_flag = ImGuiInputTextFlags_CallbackAlways; 10326 10327 if (event_flag) 10328 { 10329 ImGuiTextEditCallbackData callback_data; 10330 memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); 10331 callback_data.EventFlag = event_flag; 10332 callback_data.Flags = flags; 10333 callback_data.UserData = user_data; 10334 callback_data.ReadOnly = !is_editable; 10335 10336 callback_data.EventKey = event_key; 10337 callback_data.Buf = edit_state.TempTextBuffer.Data; 10338 callback_data.BufTextLen = edit_state.CurLenA; 10339 callback_data.BufSize = edit_state.BufSizeA; 10340 callback_data.BufDirty = false; 10341 10342 // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) 10343 ImWchar* text = edit_state.Text.Data; 10344 const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); 10345 const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); 10346 const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); 10347 10348 // Call user code 10349 callback(&callback_data); 10350 10351 // Read back what user may have modified 10352 IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields 10353 IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); 10354 IM_ASSERT(callback_data.Flags == flags); 10355 if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); 10356 if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); 10357 if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); 10358 if (callback_data.BufDirty) 10359 { 10360 IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! 10361 edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); 10362 edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() 10363 edit_state.CursorAnimReset(); 10364 } 10365 } 10366 } 10367 10368 // Copy back to user buffer 10369 if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) 10370 { 10371 ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); 10372 value_changed = true; 10373 } 10374 } 10375 } 10376 10377 // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) 10378 if (clear_active_id && g.ActiveId == id) 10379 ClearActiveID(); 10380 10381 // Render 10382 // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. 10383 const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; 10384 10385 RenderNavHighlight(frame_bb, id); 10386 if (!is_multiline) 10387 RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); 10388 10389 const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size 10390 ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; 10391 ImVec2 text_size(0.f, 0.f); 10392 const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); 10393 if (g.ActiveId == id || is_currently_scrolling) 10394 { 10395 edit_state.CursorAnim += io.DeltaTime; 10396 10397 // This is going to be messy. We need to: 10398 // - Display the text (this alone can be more easily clipped) 10399 // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) 10400 // - Measure text height (for scrollbar) 10401 // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) 10402 // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. 10403 const ImWchar* text_begin = edit_state.Text.Data; 10404 ImVec2 cursor_offset, select_start_offset; 10405 10406 { 10407 // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. 10408 const ImWchar* searches_input_ptr[2]; 10409 searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; 10410 searches_input_ptr[1] = NULL; 10411 int searches_remaining = 1; 10412 int searches_result_line_number[2] = { -1, -999 }; 10413 if (edit_state.StbState.select_start != edit_state.StbState.select_end) 10414 { 10415 searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); 10416 searches_result_line_number[1] = -1; 10417 searches_remaining++; 10418 } 10419 10420 // Iterate all lines to find our line numbers 10421 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. 10422 searches_remaining += is_multiline ? 1 : 0; 10423 int line_count = 0; 10424 for (const ImWchar* s = text_begin; *s != 0; s++) 10425 if (*s == '\n') 10426 { 10427 line_count++; 10428 if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } 10429 if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } 10430 } 10431 line_count++; 10432 if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; 10433 if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; 10434 10435 // Calculate 2d position by finding the beginning of the line and measuring distance 10436 cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; 10437 cursor_offset.y = searches_result_line_number[0] * g.FontSize; 10438 if (searches_result_line_number[1] >= 0) 10439 { 10440 select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; 10441 select_start_offset.y = searches_result_line_number[1] * g.FontSize; 10442 } 10443 10444 // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) 10445 if (is_multiline) 10446 text_size = ImVec2(size.x, line_count * g.FontSize); 10447 } 10448 10449 // Scroll 10450 if (edit_state.CursorFollow) 10451 { 10452 // Horizontal scroll in chunks of quarter width 10453 if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) 10454 { 10455 const float scroll_increment_x = size.x * 0.25f; 10456 if (cursor_offset.x < edit_state.ScrollX) 10457 edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); 10458 else if (cursor_offset.x - size.x >= edit_state.ScrollX) 10459 edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); 10460 } 10461 else 10462 { 10463 edit_state.ScrollX = 0.0f; 10464 } 10465 10466 // Vertical scroll 10467 if (is_multiline) 10468 { 10469 float scroll_y = draw_window->Scroll.y; 10470 if (cursor_offset.y - g.FontSize < scroll_y) 10471 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); 10472 else if (cursor_offset.y - size.y >= scroll_y) 10473 scroll_y = cursor_offset.y - size.y; 10474 draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag 10475 draw_window->Scroll.y = scroll_y; 10476 render_pos.y = draw_window->DC.CursorPos.y; 10477 } 10478 } 10479 edit_state.CursorFollow = false; 10480 const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); 10481 10482 // Draw selection 10483 if (edit_state.StbState.select_start != edit_state.StbState.select_end) 10484 { 10485 const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); 10486 const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); 10487 10488 float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. 10489 float bg_offy_dn = is_multiline ? 0.0f : 2.0f; 10490 ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); 10491 ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; 10492 for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) 10493 { 10494 if (rect_pos.y > clip_rect.w + g.FontSize) 10495 break; 10496 if (rect_pos.y < clip_rect.y) 10497 { 10498 while (p < text_selected_end) 10499 if (*p++ == '\n') 10500 break; 10501 } 10502 else 10503 { 10504 ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); 10505 if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines 10506 ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); 10507 rect.ClipWith(clip_rect); 10508 if (rect.Overlaps(clip_rect)) 10509 draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); 10510 } 10511 rect_pos.x = render_pos.x - render_scroll.x; 10512 rect_pos.y += g.FontSize; 10513 } 10514 } 10515 10516 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); 10517 10518 // Draw blinking cursor 10519 bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; 10520 ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; 10521 ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); 10522 if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) 10523 draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); 10524 10525 // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) 10526 if (is_editable) 10527 g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); 10528 } 10529 else 10530 { 10531 // Render text only 10532 const char* buf_end = NULL; 10533 if (is_multiline) 10534 text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width 10535 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); 10536 } 10537 10538 if (is_multiline) 10539 { 10540 Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line 10541 EndChildFrame(); 10542 EndGroup(); 10543 } 10544 10545 if (is_password) 10546 PopFont(); 10547 10548 // Log as text 10549 if (g.LogEnabled && !is_password) 10550 LogRenderedText(&render_pos, buf_display, NULL); 10551 10552 if (label_size.x > 0) 10553 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 10554 10555 if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) 10556 return enter_pressed; 10557 else 10558 return value_changed; 10559 } 10560 10561 bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) 10562 { 10563 IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() 10564 return InputTextEx(label, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); 10565 } 10566 10567 bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) 10568 { 10569 return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); 10570 } 10571 10572 // NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument) 10573 bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags) 10574 { 10575 ImGuiWindow* window = GetCurrentWindow(); 10576 if (window->SkipItems) 10577 return false; 10578 10579 ImGuiContext& g = *GImGui; 10580 const ImGuiStyle& style = g.Style; 10581 10582 char buf[64]; 10583 DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, scalar_format); 10584 10585 bool value_changed = false; 10586 if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) 10587 extra_flags |= ImGuiInputTextFlags_CharsDecimal; 10588 extra_flags |= ImGuiInputTextFlags_AutoSelectAll; 10589 10590 if (step_ptr) 10591 { 10592 const float button_size = GetFrameHeight(); 10593 10594 BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() 10595 PushID(label); 10596 PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); 10597 if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view 10598 value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, scalar_format); 10599 PopItemWidth(); 10600 10601 // Step buttons 10602 SameLine(0, style.ItemInnerSpacing.x); 10603 if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) 10604 { 10605 DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); 10606 value_changed = true; 10607 } 10608 SameLine(0, style.ItemInnerSpacing.x); 10609 if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) 10610 { 10611 DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); 10612 value_changed = true; 10613 } 10614 SameLine(0, style.ItemInnerSpacing.x); 10615 TextUnformatted(label, FindRenderedTextEnd(label)); 10616 10617 PopID(); 10618 EndGroup(); 10619 } 10620 else 10621 { 10622 if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags)) 10623 value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, scalar_format); 10624 } 10625 10626 return value_changed; 10627 } 10628 10629 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags) 10630 { 10631 extra_flags |= ImGuiInputTextFlags_CharsScientific; 10632 return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags); 10633 } 10634 10635 bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags) 10636 { 10637 extra_flags |= ImGuiInputTextFlags_CharsScientific; 10638 return InputScalarEx(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags); 10639 } 10640 10641 bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) 10642 { 10643 // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. 10644 const char* format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; 10645 return InputScalarEx(label, ImGuiDataType_Int32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, extra_flags); 10646 } 10647 10648 bool ImGui::InputFloatN(const char* label, float* v, int components, const char* format, ImGuiInputTextFlags extra_flags) 10649 { 10650 ImGuiWindow* window = GetCurrentWindow(); 10651 if (window->SkipItems) 10652 return false; 10653 10654 ImGuiContext& g = *GImGui; 10655 bool value_changed = false; 10656 BeginGroup(); 10657 PushID(label); 10658 PushMultiItemsWidths(components); 10659 for (int i = 0; i < components; i++) 10660 { 10661 PushID(i); 10662 value_changed |= InputFloat("##v", &v[i], 0, 0, format, extra_flags); 10663 SameLine(0, g.Style.ItemInnerSpacing.x); 10664 PopID(); 10665 PopItemWidth(); 10666 } 10667 PopID(); 10668 10669 TextUnformatted(label, FindRenderedTextEnd(label)); 10670 EndGroup(); 10671 10672 return value_changed; 10673 } 10674 10675 bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags) 10676 { 10677 return InputFloatN(label, v, 2, format, extra_flags); 10678 } 10679 10680 bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags) 10681 { 10682 return InputFloatN(label, v, 3, format, extra_flags); 10683 } 10684 10685 bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags) 10686 { 10687 return InputFloatN(label, v, 4, format, extra_flags); 10688 } 10689 10690 // Prefer using "const char* format" directly, which is more flexible and consistent with other API. 10691 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 10692 bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) 10693 { 10694 char format[16] = "%f"; 10695 if (decimal_precision >= 0) 10696 ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); 10697 return InputFloat(label, v, step, step_fast, format, extra_flags); 10698 } 10699 10700 bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) 10701 { 10702 char format[16] = "%f"; 10703 if (decimal_precision >= 0) 10704 ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); 10705 return InputFloatN(label, v, 2, format, extra_flags); 10706 } 10707 10708 bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) 10709 { 10710 char format[16] = "%f"; 10711 if (decimal_precision >= 0) 10712 ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); 10713 return InputFloatN(label, v, 3, format, extra_flags); 10714 } 10715 10716 bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) 10717 { 10718 char format[16] = "%f"; 10719 if (decimal_precision >= 0) 10720 ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); 10721 return InputFloatN(label, v, 4, format, extra_flags); 10722 } 10723 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS 10724 10725 bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags) 10726 { 10727 ImGuiWindow* window = GetCurrentWindow(); 10728 if (window->SkipItems) 10729 return false; 10730 10731 ImGuiContext& g = *GImGui; 10732 bool value_changed = false; 10733 BeginGroup(); 10734 PushID(label); 10735 PushMultiItemsWidths(components); 10736 for (int i = 0; i < components; i++) 10737 { 10738 PushID(i); 10739 value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags); 10740 SameLine(0, g.Style.ItemInnerSpacing.x); 10741 PopID(); 10742 PopItemWidth(); 10743 } 10744 PopID(); 10745 10746 TextUnformatted(label, FindRenderedTextEnd(label)); 10747 EndGroup(); 10748 10749 return value_changed; 10750 } 10751 10752 bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) 10753 { 10754 return InputIntN(label, v, 2, extra_flags); 10755 } 10756 10757 bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) 10758 { 10759 return InputIntN(label, v, 3, extra_flags); 10760 } 10761 10762 bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) 10763 { 10764 return InputIntN(label, v, 4, extra_flags); 10765 } 10766 10767 static float CalcMaxPopupHeightFromItemCount(int items_count) 10768 { 10769 ImGuiContext& g = *GImGui; 10770 if (items_count <= 0) 10771 return FLT_MAX; 10772 return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); 10773 } 10774 10775 bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) 10776 { 10777 // Always consume the SetNextWindowSizeConstraint() call in our early return paths 10778 ImGuiContext& g = *GImGui; 10779 ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond; 10780 g.NextWindowData.SizeConstraintCond = 0; 10781 10782 ImGuiWindow* window = GetCurrentWindow(); 10783 if (window->SkipItems) 10784 return false; 10785 10786 IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together 10787 10788 const ImGuiStyle& style = g.Style; 10789 const ImGuiID id = window->GetID(label); 10790 10791 const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); 10792 const ImVec2 label_size = CalcTextSize(label, NULL, true); 10793 const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); 10794 const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); 10795 const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 10796 ItemSize(total_bb, style.FramePadding.y); 10797 if (!ItemAdd(total_bb, id, &frame_bb)) 10798 return false; 10799 10800 bool hovered, held; 10801 bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); 10802 bool popup_open = IsPopupOpen(id); 10803 10804 const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); 10805 const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); 10806 RenderNavHighlight(frame_bb, id); 10807 if (!(flags & ImGuiComboFlags_NoPreview)) 10808 window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); 10809 if (!(flags & ImGuiComboFlags_NoArrowButton)) 10810 { 10811 window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); 10812 RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); 10813 } 10814 RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); 10815 if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) 10816 RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); 10817 if (label_size.x > 0) 10818 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 10819 10820 if ((pressed || g.NavActivateId == id) && !popup_open) 10821 { 10822 if (window->DC.NavLayerCurrent == 0) 10823 window->NavLastIds[0] = id; 10824 OpenPopupEx(id); 10825 popup_open = true; 10826 } 10827 10828 if (!popup_open) 10829 return false; 10830 10831 if (backup_next_window_size_constraint) 10832 { 10833 g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint; 10834 g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); 10835 } 10836 else 10837 { 10838 if ((flags & ImGuiComboFlags_HeightMask_) == 0) 10839 flags |= ImGuiComboFlags_HeightRegular; 10840 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one 10841 int popup_max_height_in_items = -1; 10842 if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; 10843 else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; 10844 else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; 10845 SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); 10846 } 10847 10848 char name[16]; 10849 ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth 10850 10851 // Peak into expected window size so we can position it 10852 if (ImGuiWindow* popup_window = FindWindowByName(name)) 10853 if (popup_window->WasActive) 10854 { 10855 ImVec2 size_contents = CalcSizeContents(popup_window); 10856 ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents)); 10857 if (flags & ImGuiComboFlags_PopupAlignLeft) 10858 popup_window->AutoPosLastDirection = ImGuiDir_Left; 10859 ImRect r_outer = FindAllowedExtentRectForWindow(popup_window); 10860 ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); 10861 SetNextWindowPos(pos); 10862 } 10863 10864 ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; 10865 if (!Begin(name, NULL, window_flags)) 10866 { 10867 EndPopup(); 10868 IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above 10869 return false; 10870 } 10871 10872 // Horizontally align ourselves with the framed text 10873 if (style.FramePadding.x != style.WindowPadding.x) 10874 Indent(style.FramePadding.x - style.WindowPadding.x); 10875 10876 return true; 10877 } 10878 10879 void ImGui::EndCombo() 10880 { 10881 const ImGuiStyle& style = GImGui->Style; 10882 if (style.FramePadding.x != style.WindowPadding.x) 10883 Unindent(style.FramePadding.x - style.WindowPadding.x); 10884 EndPopup(); 10885 } 10886 10887 // Old API, prefer using BeginCombo() nowadays if you can. 10888 bool ImGui::Combo(const char* label, int* current_item, bool(*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) 10889 { 10890 ImGuiContext& g = *GImGui; 10891 10892 const char* preview_text = NULL; 10893 if (*current_item >= 0 && *current_item < items_count) 10894 items_getter(data, *current_item, &preview_text); 10895 10896 // The old Combo() API exposed "popup_max_height_in_items", however the new more general BeginCombo() API doesn't, so we emulate it here. 10897 if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond) 10898 { 10899 float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); 10900 SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, popup_max_height)); 10901 } 10902 10903 if (!BeginCombo(label, preview_text, 0)) 10904 return false; 10905 10906 // Display items 10907 // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) 10908 bool value_changed = false; 10909 for (int i = 0; i < items_count; i++) 10910 { 10911 PushID((void*)(intptr_t)i); 10912 const bool item_selected = (i == *current_item); 10913 const char* item_text; 10914 if (!items_getter(data, i, &item_text)) 10915 item_text = "*Unknown item*"; 10916 if (Selectable(item_text, item_selected)) 10917 { 10918 value_changed = true; 10919 *current_item = i; 10920 } 10921 if (item_selected) 10922 SetItemDefaultFocus(); 10923 PopID(); 10924 } 10925 10926 EndCombo(); 10927 return value_changed; 10928 } 10929 10930 static bool Items_ArrayGetter(void* data, int idx, const char** out_text) 10931 { 10932 const char* const* items = (const char* const*)data; 10933 if (out_text) 10934 *out_text = items[idx]; 10935 return true; 10936 } 10937 10938 static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) 10939 { 10940 // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. 10941 const char* items_separated_by_zeros = (const char*)data; 10942 int items_count = 0; 10943 const char* p = items_separated_by_zeros; 10944 while (*p) 10945 { 10946 if (idx == items_count) 10947 break; 10948 p += strlen(p) + 1; 10949 items_count++; 10950 } 10951 if (!*p) 10952 return false; 10953 if (out_text) 10954 *out_text = p; 10955 return true; 10956 } 10957 10958 // Combo box helper allowing to pass an array of strings. 10959 bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) 10960 { 10961 const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); 10962 return value_changed; 10963 } 10964 10965 // Combo box helper allowing to pass all items in a single string. 10966 bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) 10967 { 10968 int items_count = 0; 10969 const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open 10970 while (*p) 10971 { 10972 p += strlen(p) + 1; 10973 items_count++; 10974 } 10975 bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); 10976 return value_changed; 10977 } 10978 10979 // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. 10980 // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. 10981 bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) 10982 { 10983 ImGuiWindow* window = GetCurrentWindow(); 10984 if (window->SkipItems) 10985 return false; 10986 10987 ImGuiContext& g = *GImGui; 10988 const ImGuiStyle& style = g.Style; 10989 10990 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped. 10991 PopClipRect(); 10992 10993 ImGuiID id = window->GetID(label); 10994 ImVec2 label_size = CalcTextSize(label, NULL, true); 10995 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); 10996 ImVec2 pos = window->DC.CursorPos; 10997 pos.y += window->DC.CurrentLineTextBaseOffset; 10998 ImRect bb(pos, pos + size); 10999 ItemSize(bb); 11000 11001 // Fill horizontal space. 11002 ImVec2 window_padding = window->WindowPadding; 11003 float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; 11004 float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); 11005 ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); 11006 ImRect bb_with_spacing(pos, pos + size_draw); 11007 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) 11008 bb_with_spacing.Max.x += window_padding.x; 11009 11010 // Selectables are tightly packed together, we extend the box to cover spacing between selectable. 11011 float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); 11012 float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); 11013 float spacing_R = style.ItemSpacing.x - spacing_L; 11014 float spacing_D = style.ItemSpacing.y - spacing_U; 11015 bb_with_spacing.Min.x -= spacing_L; 11016 bb_with_spacing.Min.y -= spacing_U; 11017 bb_with_spacing.Max.x += spacing_R; 11018 bb_with_spacing.Max.y += spacing_D; 11019 if (!ItemAdd(bb_with_spacing, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) 11020 { 11021 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) 11022 PushColumnClipRect(); 11023 return false; 11024 } 11025 11026 ImGuiButtonFlags button_flags = 0; 11027 if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_NoHoldingActiveID; 11028 if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnRelease; 11029 if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; 11030 if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; 11031 bool hovered, held; 11032 bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags); 11033 if (flags & ImGuiSelectableFlags_Disabled) 11034 selected = false; 11035 11036 // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) 11037 if (pressed || hovered) 11038 if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) 11039 { 11040 g.NavDisableHighlight = true; 11041 SetNavID(id, window->DC.NavLayerCurrent); 11042 } 11043 11044 // Render 11045 if (hovered || selected) 11046 { 11047 const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); 11048 RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); 11049 RenderNavHighlight(bb_with_spacing, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); 11050 } 11051 11052 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) 11053 { 11054 PushColumnClipRect(); 11055 bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x); 11056 } 11057 11058 if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); 11059 RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f, 0.0f)); 11060 if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); 11061 11062 // Automatically close popups 11063 if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) 11064 CloseCurrentPopup(); 11065 return pressed; 11066 } 11067 11068 bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) 11069 { 11070 if (Selectable(label, *p_selected, flags, size_arg)) 11071 { 11072 *p_selected = !*p_selected; 11073 return true; 11074 } 11075 return false; 11076 } 11077 11078 // Helper to calculate the size of a listbox and display a label on the right. 11079 // Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" 11080 bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) 11081 { 11082 ImGuiWindow* window = GetCurrentWindow(); 11083 if (window->SkipItems) 11084 return false; 11085 11086 const ImGuiStyle& style = GetStyle(); 11087 const ImGuiID id = GetID(label); 11088 const ImVec2 label_size = CalcTextSize(label, NULL, true); 11089 11090 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. 11091 ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); 11092 ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); 11093 ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); 11094 ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); 11095 window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. 11096 11097 BeginGroup(); 11098 if (label_size.x > 0) 11099 RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); 11100 11101 BeginChildFrame(id, frame_bb.GetSize()); 11102 return true; 11103 } 11104 11105 bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) 11106 { 11107 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. 11108 // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. 11109 // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. 11110 if (height_in_items < 0) 11111 height_in_items = ImMin(items_count, 7); 11112 float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); 11113 11114 // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). 11115 ImVec2 size; 11116 size.x = 0.0f; 11117 size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; 11118 return ListBoxHeader(label, size); 11119 } 11120 11121 void ImGui::ListBoxFooter() 11122 { 11123 ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; 11124 const ImRect bb = parent_window->DC.LastItemRect; 11125 const ImGuiStyle& style = GetStyle(); 11126 11127 EndChildFrame(); 11128 11129 // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) 11130 // We call SameLine() to restore DC.CurrentLine* data 11131 SameLine(); 11132 parent_window->DC.CursorPos = bb.Min; 11133 ItemSize(bb, style.FramePadding.y); 11134 EndGroup(); 11135 } 11136 11137 bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) 11138 { 11139 const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); 11140 return value_changed; 11141 } 11142 11143 bool ImGui::ListBox(const char* label, int* current_item, bool(*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) 11144 { 11145 if (!ListBoxHeader(label, items_count, height_in_items)) 11146 return false; 11147 11148 // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. 11149 bool value_changed = false; 11150 ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. 11151 while (clipper.Step()) 11152 for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) 11153 { 11154 const bool item_selected = (i == *current_item); 11155 const char* item_text; 11156 if (!items_getter(data, i, &item_text)) 11157 item_text = "*Unknown item*"; 11158 11159 PushID(i); 11160 if (Selectable(item_text, item_selected)) 11161 { 11162 *current_item = i; 11163 value_changed = true; 11164 } 11165 if (item_selected) 11166 SetItemDefaultFocus(); 11167 PopID(); 11168 } 11169 ListBoxFooter(); 11170 return value_changed; 11171 } 11172 11173 bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) 11174 { 11175 ImGuiWindow* window = GetCurrentWindow(); 11176 if (window->SkipItems) 11177 return false; 11178 11179 ImGuiContext& g = *GImGui; 11180 ImGuiStyle& style = g.Style; 11181 ImVec2 pos = window->DC.CursorPos; 11182 ImVec2 label_size = CalcTextSize(label, NULL, true); 11183 11184 ImGuiSelectableFlags flags = ImGuiSelectableFlags_MenuItem | (enabled ? 0 : ImGuiSelectableFlags_Disabled); 11185 bool pressed; 11186 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 11187 { 11188 // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful 11189 // Note that in this situation we render neither the shortcut neither the selected tick mark 11190 float w = label_size.x; 11191 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); 11192 PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); 11193 pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); 11194 PopStyleVar(); 11195 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). 11196 } 11197 else 11198 { 11199 ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); 11200 float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame 11201 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); 11202 pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); 11203 if (shortcut_size.x > 0.0f) 11204 { 11205 PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); 11206 RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); 11207 PopStyleColor(); 11208 } 11209 if (selected) 11210 RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); 11211 } 11212 return pressed; 11213 } 11214 11215 bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) 11216 { 11217 if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) 11218 { 11219 if (p_selected) 11220 *p_selected = !*p_selected; 11221 return true; 11222 } 11223 return false; 11224 } 11225 11226 // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. 11227 bool ImGui::BeginMainMenuBar() 11228 { 11229 ImGuiContext& g = *GImGui; 11230 g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); 11231 SetNextWindowPos(ImVec2(0.0f, 0.0f)); 11232 SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); 11233 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 11234 PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); 11235 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; 11236 bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); 11237 g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); 11238 if (!is_open) 11239 { 11240 End(); 11241 PopStyleVar(2); 11242 return false; 11243 } 11244 return true; 11245 } 11246 11247 void ImGui::EndMainMenuBar() 11248 { 11249 EndMenuBar(); 11250 11251 // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window 11252 ImGuiContext& g = *GImGui; 11253 if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) 11254 FocusFrontMostActiveWindow(g.NavWindow); 11255 11256 End(); 11257 PopStyleVar(2); 11258 } 11259 11260 bool ImGui::BeginMenuBar() 11261 { 11262 ImGuiWindow* window = GetCurrentWindow(); 11263 if (window->SkipItems) 11264 return false; 11265 if (!(window->Flags & ImGuiWindowFlags_MenuBar)) 11266 return false; 11267 11268 IM_ASSERT(!window->DC.MenuBarAppending); 11269 BeginGroup(); // Save position 11270 PushID("##menubar"); 11271 11272 // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. 11273 // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. 11274 ImRect bar_rect = window->MenuBarRect(); 11275 ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); 11276 clip_rect.ClipWith(window->WindowRectClipped); 11277 PushClipRect(clip_rect.Min, clip_rect.Max, false); 11278 11279 window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); 11280 window->DC.LayoutType = ImGuiLayoutType_Horizontal; 11281 window->DC.NavLayerCurrent++; 11282 window->DC.NavLayerCurrentMask <<= 1; 11283 window->DC.MenuBarAppending = true; 11284 AlignTextToFramePadding(); 11285 return true; 11286 } 11287 11288 void ImGui::EndMenuBar() 11289 { 11290 ImGuiWindow* window = GetCurrentWindow(); 11291 if (window->SkipItems) 11292 return; 11293 ImGuiContext& g = *GImGui; 11294 11295 // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. 11296 if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) 11297 { 11298 ImGuiWindow* nav_earliest_child = g.NavWindow; 11299 while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) 11300 nav_earliest_child = nav_earliest_child->ParentWindow; 11301 if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) 11302 { 11303 // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. 11304 // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) 11305 IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check 11306 FocusWindow(window); 11307 SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]); 11308 g.NavLayer = 1; 11309 g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. 11310 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 11311 NavMoveRequestCancel(); 11312 } 11313 } 11314 11315 IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); 11316 IM_ASSERT(window->DC.MenuBarAppending); 11317 PopClipRect(); 11318 PopID(); 11319 window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. 11320 window->DC.GroupStack.back().AdvanceCursor = false; 11321 EndGroup(); 11322 window->DC.LayoutType = ImGuiLayoutType_Vertical; 11323 window->DC.NavLayerCurrent--; 11324 window->DC.NavLayerCurrentMask >>= 1; 11325 window->DC.MenuBarAppending = false; 11326 } 11327 11328 bool ImGui::BeginMenu(const char* label, bool enabled) 11329 { 11330 ImGuiWindow* window = GetCurrentWindow(); 11331 if (window->SkipItems) 11332 return false; 11333 11334 ImGuiContext& g = *GImGui; 11335 const ImGuiStyle& style = g.Style; 11336 const ImGuiID id = window->GetID(label); 11337 11338 ImVec2 label_size = CalcTextSize(label, NULL, true); 11339 11340 bool pressed; 11341 bool menu_is_open = IsPopupOpen(id); 11342 bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); 11343 ImGuiWindow* backed_nav_window = g.NavWindow; 11344 if (menuset_is_open) 11345 g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) 11346 11347 // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). 11348 ImVec2 popup_pos, pos = window->DC.CursorPos; 11349 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 11350 { 11351 // Menu inside an horizontal menu bar 11352 // Selectable extend their highlight by half ItemSpacing in each direction. 11353 // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() 11354 popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); 11355 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); 11356 PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); 11357 float w = label_size.x; 11358 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); 11359 PopStyleVar(); 11360 window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). 11361 } 11362 else 11363 { 11364 // Menu inside a menu 11365 popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); 11366 float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame 11367 float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); 11368 pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); 11369 if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); 11370 RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right); 11371 if (!enabled) PopStyleColor(); 11372 } 11373 11374 const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); 11375 if (menuset_is_open) 11376 g.NavWindow = backed_nav_window; 11377 11378 bool want_open = false, want_close = false; 11379 if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) 11380 { 11381 // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. 11382 bool moving_within_opened_triangle = false; 11383 if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) 11384 { 11385 if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) 11386 { 11387 ImRect next_window_rect = next_window->Rect(); 11388 ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; 11389 ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); 11390 ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); 11391 float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. 11392 ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues 11393 tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? 11394 tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); 11395 moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); 11396 //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug 11397 } 11398 } 11399 11400 want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); 11401 want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); 11402 11403 if (g.NavActivateId == id) 11404 { 11405 want_close = menu_is_open; 11406 want_open = !menu_is_open; 11407 } 11408 if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open 11409 { 11410 want_open = true; 11411 NavMoveRequestCancel(); 11412 } 11413 } 11414 else 11415 { 11416 // Menu bar 11417 if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it 11418 { 11419 want_close = true; 11420 want_open = menu_is_open = false; 11421 } 11422 else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others 11423 { 11424 want_open = true; 11425 } 11426 else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open 11427 { 11428 want_open = true; 11429 NavMoveRequestCancel(); 11430 } 11431 } 11432 11433 if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' 11434 want_close = true; 11435 if (want_close && IsPopupOpen(id)) 11436 ClosePopupToLevel(g.CurrentPopupStack.Size); 11437 11438 if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) 11439 { 11440 // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. 11441 OpenPopup(label); 11442 return false; 11443 } 11444 11445 menu_is_open |= want_open; 11446 if (want_open) 11447 OpenPopup(label); 11448 11449 if (menu_is_open) 11450 { 11451 // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) 11452 SetNextWindowPos(popup_pos, ImGuiCond_Always); 11453 ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); 11454 menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) 11455 } 11456 11457 return menu_is_open; 11458 } 11459 11460 void ImGui::EndMenu() 11461 { 11462 // Nav: When a left move request _within our child menu_ failed, close the menu. 11463 // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. 11464 // However it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. 11465 ImGuiContext& g = *GImGui; 11466 ImGuiWindow* window = g.CurrentWindow; 11467 if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) 11468 { 11469 ClosePopupToLevel(g.OpenPopupStack.Size - 1); 11470 NavMoveRequestCancel(); 11471 } 11472 11473 EndPopup(); 11474 } 11475 11476 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. 11477 void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) 11478 { 11479 ImGuiContext& g = *GImGui; 11480 11481 int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); 11482 BeginTooltipEx(0, true); 11483 11484 const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; 11485 if (text_end > text) 11486 { 11487 TextUnformatted(text, text_end); 11488 Separator(); 11489 } 11490 11491 ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); 11492 ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); 11493 SameLine(); 11494 if (flags & ImGuiColorEditFlags_NoAlpha) 11495 Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); 11496 else 11497 Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); 11498 EndTooltip(); 11499 } 11500 11501 static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) 11502 { 11503 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; 11504 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); 11505 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); 11506 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); 11507 return IM_COL32(r, g, b, 0xFF); 11508 } 11509 11510 // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. 11511 // I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. 11512 void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) 11513 { 11514 ImGuiWindow* window = GetCurrentWindow(); 11515 if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) 11516 { 11517 ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204, 204, 204, 255), col)); 11518 ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128, 128, 128, 255), col)); 11519 window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); 11520 11521 int yi = 0; 11522 for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) 11523 { 11524 float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); 11525 if (y2 <= y1) 9089 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9090 } 9091 } 9092 } 9093 9094 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) 9095 { 9096 ImGuiContext& g = *GImGui; 9097 for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) 9098 if (g.WindowsFocusOrder[i] == window) 9099 return i; 9100 return -1; 9101 } 9102 9103 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) 9104 { 9105 ImGuiContext& g = *GImGui; 9106 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) 9107 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) 9108 return g.WindowsFocusOrder[i]; 9109 return NULL; 9110 } 9111 9112 static void NavUpdateWindowingHighlightWindow(int focus_change_dir) 9113 { 9114 ImGuiContext& g = *GImGui; 9115 IM_ASSERT(g.NavWindowingTarget); 9116 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) 9117 return; 9118 9119 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); 9120 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); 9121 if (!window_target) 9122 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); 9123 if (window_target) // Don't reset windowing target if there's a single window in the list 9124 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; 9125 g.NavWindowingToggleLayer = false; 9126 } 9127 9128 // Windowing management mode 9129 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) 9130 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) 9131 static void ImGui::NavUpdateWindowing() 9132 { 9133 ImGuiContext& g = *GImGui; 9134 ImGuiWindow* apply_focus_window = NULL; 9135 bool apply_toggle_layer = false; 9136 9137 ImGuiWindow* modal_window = GetTopMostPopupModal(); 9138 bool allow_windowing = (modal_window == NULL); 9139 if (!allow_windowing) 9140 g.NavWindowingTarget = NULL; 9141 9142 // Fade out 9143 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) 9144 { 9145 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); 9146 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) 9147 g.NavWindowingTargetAnim = NULL; 9148 } 9149 9150 // Start CTRL-TAB or Square+L/R window selection 9151 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); 9152 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); 9153 if (start_windowing_with_gamepad || start_windowing_with_keyboard) 9154 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) 9155 { 9156 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop 9157 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; 9158 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; 9159 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; 9160 } 9161 9162 // Gamepad update 9163 g.NavWindowingTimer += g.IO.DeltaTime; 9164 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) 9165 { 9166 // 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 9167 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); 9168 9169 // Select window to focus 9170 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); 9171 if (focus_change_dir != 0) 9172 { 9173 NavUpdateWindowingHighlightWindow(focus_change_dir); 9174 g.NavWindowingHighlightAlpha = 1.0f; 9175 } 9176 9177 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) 9178 if (!IsNavInputDown(ImGuiNavInput_Menu)) 9179 { 9180 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. 9181 if (g.NavWindowingToggleLayer && g.NavWindow) 9182 apply_toggle_layer = true; 9183 else if (!g.NavWindowingToggleLayer) 9184 apply_focus_window = g.NavWindowingTarget; 9185 g.NavWindowingTarget = NULL; 9186 } 9187 } 9188 9189 // Keyboard: Focus 9190 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) 9191 { 9192 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise 9193 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f 9194 if (IsKeyPressedMap(ImGuiKey_Tab, true)) 9195 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); 9196 if (!g.IO.KeyCtrl) 9197 apply_focus_window = g.NavWindowingTarget; 9198 } 9199 9200 // Keyboard: Press and Release ALT to toggle menu layer 9201 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB 9202 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) 9203 g.NavWindowingToggleLayer = true; 9204 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) 9205 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) 9206 apply_toggle_layer = true; 9207 9208 // Move window 9209 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) 9210 { 9211 ImVec2 move_delta; 9212 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) 9213 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 9214 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 9215 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); 9216 if (move_delta.x != 0.0f || move_delta.y != 0.0f) 9217 { 9218 const float NAV_MOVE_SPEED = 800.0f; 9219 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well 9220 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; 9221 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); 9222 MarkIniSettingsDirty(moving_window); 9223 g.NavDisableMouseHover = true; 9224 } 9225 } 9226 9227 // Apply final focus 9228 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) 9229 { 9230 ClearActiveID(); 9231 g.NavDisableHighlight = false; 9232 g.NavDisableMouseHover = true; 9233 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); 9234 ClosePopupsOverWindow(apply_focus_window, false); 9235 FocusWindow(apply_focus_window); 9236 if (apply_focus_window->NavLastIds[0] == 0) 9237 NavInitWindow(apply_focus_window, false); 9238 9239 // If the window only has a menu layer, select it directly 9240 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) 9241 g.NavLayer = ImGuiNavLayer_Menu; 9242 } 9243 if (apply_focus_window) 9244 g.NavWindowingTarget = NULL; 9245 9246 // Apply menu/layer toggle 9247 if (apply_toggle_layer && g.NavWindow) 9248 { 9249 // Move to parent menu if necessary 9250 ImGuiWindow* new_nav_window = g.NavWindow; 9251 while (new_nav_window->ParentWindow 9252 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 9253 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 9254 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 9255 new_nav_window = new_nav_window->ParentWindow; 9256 if (new_nav_window != g.NavWindow) 9257 { 9258 ImGuiWindow* old_nav_window = g.NavWindow; 9259 FocusWindow(new_nav_window); 9260 new_nav_window->NavLastChildNavWindow = old_nav_window; 9261 } 9262 g.NavDisableHighlight = false; 9263 g.NavDisableMouseHover = true; 9264 9265 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. 9266 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; 9267 NavRestoreLayer(new_nav_layer); 9268 } 9269 } 9270 9271 // Window has already passed the IsWindowNavFocusable() 9272 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) 9273 { 9274 if (window->Flags & ImGuiWindowFlags_Popup) 9275 return "(Popup)"; 9276 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) 9277 return "(Main menu bar)"; 9278 return "(Untitled)"; 9279 } 9280 9281 // Overlay displayed when using CTRL+TAB. Called by EndFrame(). 9282 void ImGui::NavUpdateWindowingOverlay() 9283 { 9284 ImGuiContext& g = *GImGui; 9285 IM_ASSERT(g.NavWindowingTarget != NULL); 9286 9287 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) 9288 return; 9289 9290 if (g.NavWindowingListWindow == NULL) 9291 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); 9292 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); 9293 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 9294 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); 9295 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); 9296 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) 9297 { 9298 ImGuiWindow* window = g.WindowsFocusOrder[n]; 9299 if (!IsWindowNavFocusable(window)) 11526 9300 continue; 11527 for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) 11528 { 11529 float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); 11530 if (x2 <= x1) 11531 continue; 11532 int rounding_corners_flags_cell = 0; 11533 if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } 11534 if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } 11535 rounding_corners_flags_cell &= rounding_corners_flags; 11536 window->DrawList->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); 11537 } 11538 } 11539 } 11540 else 11541 { 11542 window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); 11543 } 11544 } 11545 11546 void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) 11547 { 11548 ImGuiContext& g = *GImGui; 11549 if ((flags & ImGuiColorEditFlags__InputsMask) == 0) 11550 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; 11551 if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) 11552 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; 11553 if ((flags & ImGuiColorEditFlags__PickerMask) == 0) 11554 flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; 11555 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected 11556 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected 11557 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected 11558 g.ColorEditOptions = flags; 11559 } 11560 11561 // A little colored square. Return true when clicked. 11562 // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. 11563 // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. 11564 bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) 11565 { 11566 ImGuiWindow* window = GetCurrentWindow(); 11567 if (window->SkipItems) 11568 return false; 11569 11570 ImGuiContext& g = *GImGui; 11571 const ImGuiID id = window->GetID(desc_id); 11572 float default_size = GetFrameHeight(); 11573 if (size.x == 0.0f) 11574 size.x = default_size; 11575 if (size.y == 0.0f) 11576 size.y = default_size; 11577 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 11578 ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); 11579 if (!ItemAdd(bb, id)) 11580 return false; 11581 11582 bool hovered, held; 11583 bool pressed = ButtonBehavior(bb, id, &hovered, &held); 11584 11585 if (flags & ImGuiColorEditFlags_NoAlpha) 11586 flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); 11587 11588 ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); 11589 float grid_step = ImMin(size.x, size.y) / 2.99f; 11590 float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); 11591 ImRect bb_inner = bb; 11592 float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. 11593 bb_inner.Expand(off); 11594 if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) 11595 { 11596 float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); 11597 RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight); 11598 window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft); 11599 } 11600 else 11601 { 11602 // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha 11603 ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; 11604 if (col_source.w < 1.0f) 11605 RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); 11606 else 11607 window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); 11608 } 11609 RenderNavHighlight(bb, id); 11610 if (g.Style.FrameBorderSize > 0.0f) 11611 RenderFrameBorder(bb.Min, bb.Max, rounding); 11612 else 11613 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border 11614 11615 // Drag and Drop Source 11616 if (g.ActiveId == id && BeginDragDropSource()) // NB: The ActiveId test is merely an optional micro-optimization 11617 { 11618 if (flags & ImGuiColorEditFlags_NoAlpha) 11619 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); 11620 else 11621 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); 11622 ColorButton(desc_id, col, flags); 11623 SameLine(); 11624 TextUnformatted("Color"); 11625 EndDragDropSource(); 11626 hovered = false; 11627 } 11628 11629 // Tooltip 11630 if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) 11631 ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); 11632 11633 return pressed; 11634 } 11635 11636 bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) 11637 { 11638 return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); 11639 } 11640 11641 void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) 11642 { 11643 bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); 11644 bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); 11645 if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) 11646 return; 11647 ImGuiContext& g = *GImGui; 11648 ImGuiColorEditFlags opts = g.ColorEditOptions; 11649 if (allow_opt_inputs) 11650 { 11651 if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; 11652 if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; 11653 if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; 11654 } 11655 if (allow_opt_datatype) 11656 { 11657 if (allow_opt_inputs) Separator(); 11658 if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; 11659 if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; 11660 } 11661 11662 if (allow_opt_inputs || allow_opt_datatype) 11663 Separator(); 11664 if (Button("Copy as..", ImVec2(-1, 0))) 11665 OpenPopup("Copy"); 11666 if (BeginPopup("Copy")) 11667 { 11668 int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); 11669 char buf[64]; 11670 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); 11671 if (Selectable(buf)) 11672 SetClipboardText(buf); 11673 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); 11674 if (Selectable(buf)) 11675 SetClipboardText(buf); 11676 if (flags & ImGuiColorEditFlags_NoAlpha) 11677 ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); 11678 else 11679 ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); 11680 if (Selectable(buf)) 11681 SetClipboardText(buf); 11682 EndPopup(); 11683 } 11684 11685 g.ColorEditOptions = opts; 11686 EndPopup(); 11687 } 11688 11689 static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col) 11690 { 11691 bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); 11692 bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); 11693 if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) 11694 return; 11695 ImGuiContext& g = *GImGui; 11696 if (allow_opt_picker) 11697 { 11698 ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function 11699 ImGui::PushItemWidth(picker_size.x); 11700 for (int picker_type = 0; picker_type < 2; picker_type++) 11701 { 11702 // Draw small/thumbnail version of each picker type (over an invisible button for selection) 11703 if (picker_type > 0) ImGui::Separator(); 11704 ImGui::PushID(picker_type); 11705 ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha); 11706 if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; 11707 if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; 11708 ImVec2 backup_pos = ImGui::GetCursorScreenPos(); 11709 if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup 11710 g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); 11711 ImGui::SetCursorScreenPos(backup_pos); 11712 ImVec4 dummy_ref_col; 11713 memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); 11714 ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); 11715 ImGui::PopID(); 11716 } 11717 ImGui::PopItemWidth(); 11718 } 11719 if (allow_opt_alpha_bar) 11720 { 11721 if (allow_opt_picker) ImGui::Separator(); 11722 ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); 11723 } 11724 ImGui::EndPopup(); 11725 } 11726 11727 // Edit colors components (each component in 0.0f..1.0f range). 11728 // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. 11729 // With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. 11730 bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) 11731 { 11732 ImGuiWindow* window = GetCurrentWindow(); 11733 if (window->SkipItems) 11734 return false; 11735 11736 ImGuiContext& g = *GImGui; 11737 const ImGuiStyle& style = g.Style; 11738 const float square_sz = GetFrameHeight(); 11739 const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); 11740 const float w_items_all = CalcItemWidth() - w_extra; 11741 const char* label_display_end = FindRenderedTextEnd(label); 11742 11743 const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; 11744 const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; 11745 const int components = alpha ? 4 : 3; 11746 const ImGuiColorEditFlags flags_untouched = flags; 11747 11748 BeginGroup(); 11749 PushID(label); 11750 11751 // If we're not showing any slider there's no point in doing any HSV conversions 11752 if (flags & ImGuiColorEditFlags_NoInputs) 11753 flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; 11754 11755 // Context menu: display and modify options (before defaults are applied) 11756 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11757 ColorEditOptionsPopup(col, flags); 11758 11759 // Read stored options 11760 if (!(flags & ImGuiColorEditFlags__InputsMask)) 11761 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); 11762 if (!(flags & ImGuiColorEditFlags__DataTypeMask)) 11763 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); 11764 if (!(flags & ImGuiColorEditFlags__PickerMask)) 11765 flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); 11766 flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); 11767 11768 // Convert to the formats we need 11769 float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; 11770 if (flags & ImGuiColorEditFlags_HSV) 11771 ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); 11772 int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; 11773 11774 bool value_changed = false; 11775 bool value_changed_as_float = false; 11776 11777 if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) 11778 { 11779 // RGB/HSV 0..255 Sliders 11780 const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); 11781 const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); 11782 11783 const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); 11784 const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; 11785 const char* fmt_table_int[3][4] = 11786 { 11787 { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, // Short display 11788 { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA 11789 { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } // Long display for HSVA 11790 }; 11791 const char* fmt_table_float[3][4] = 11792 { 11793 { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display 11794 { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA 11795 { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA 11796 }; 11797 const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; 11798 11799 PushItemWidth(w_item_one); 11800 for (int n = 0; n < components; n++) 11801 { 11802 if (n > 0) 11803 SameLine(0, style.ItemInnerSpacing.x); 11804 if (n + 1 == components) 11805 PushItemWidth(w_item_last); 11806 if (flags & ImGuiColorEditFlags_Float) 11807 value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); 11808 else 11809 value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); 11810 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11811 OpenPopupOnItemClick("context"); 11812 } 11813 PopItemWidth(); 11814 PopItemWidth(); 11815 } 11816 else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) 11817 { 11818 // RGB Hexadecimal Input 11819 char buf[64]; 11820 if (alpha) 11821 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255)); 11822 else 11823 ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); 11824 PushItemWidth(w_items_all); 11825 if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) 11826 { 11827 value_changed = true; 11828 char* p = buf; 11829 while (*p == '#' || ImCharIsSpace((unsigned int)*p)) 11830 p++; 11831 i[0] = i[1] = i[2] = i[3] = 0; 11832 if (alpha) 11833 sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) 11834 else 11835 sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); 11836 } 11837 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11838 OpenPopupOnItemClick("context"); 11839 PopItemWidth(); 11840 } 11841 11842 ImGuiWindow* picker_active_window = NULL; 11843 if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) 11844 { 11845 if (!(flags & ImGuiColorEditFlags_NoInputs)) 11846 SameLine(0, style.ItemInnerSpacing.x); 11847 11848 const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); 11849 if (ColorButton("##ColorButton", col_v4, flags)) 11850 { 11851 if (!(flags & ImGuiColorEditFlags_NoPicker)) 11852 { 11853 // Store current color and open a picker 11854 g.ColorPickerRef = col_v4; 11855 OpenPopup("picker"); 11856 SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); 11857 } 11858 } 11859 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11860 OpenPopupOnItemClick("context"); 11861 11862 if (BeginPopup("picker")) 11863 { 11864 picker_active_window = g.CurrentWindow; 11865 if (label != label_display_end) 11866 { 11867 TextUnformatted(label, label_display_end); 11868 Separator(); 11869 } 11870 ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; 11871 ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; 11872 PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? 11873 value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); 11874 PopItemWidth(); 11875 EndPopup(); 11876 } 11877 } 11878 11879 if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) 11880 { 11881 SameLine(0, style.ItemInnerSpacing.x); 11882 TextUnformatted(label, label_display_end); 11883 } 11884 11885 // Convert back 11886 if (picker_active_window == NULL) 11887 { 11888 if (!value_changed_as_float) 11889 for (int n = 0; n < 4; n++) 11890 f[n] = i[n] / 255.0f; 11891 if (flags & ImGuiColorEditFlags_HSV) 11892 ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); 11893 if (value_changed) 11894 { 11895 col[0] = f[0]; 11896 col[1] = f[1]; 11897 col[2] = f[2]; 11898 if (alpha) 11899 col[3] = f[3]; 11900 } 11901 } 11902 11903 PopID(); 11904 EndGroup(); 11905 11906 // Drag and Drop Target 11907 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && BeginDragDropTarget()) // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. 11908 { 11909 if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) 11910 { 11911 memcpy((float*)col, payload->Data, sizeof(float) * 3); 11912 value_changed = true; 11913 } 11914 if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) 11915 { 11916 memcpy((float*)col, payload->Data, sizeof(float) * components); 11917 value_changed = true; 11918 } 11919 EndDragDropTarget(); 11920 } 11921 11922 // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). 11923 if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) 11924 window->DC.LastItemId = g.ActiveId; 11925 11926 return value_changed; 11927 } 11928 11929 bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) 11930 { 11931 float col4[4] = { col[0], col[1], col[2], 1.0f }; 11932 if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) 11933 return false; 11934 col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; 11935 return true; 11936 } 11937 11938 // 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. 11939 static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) 11940 { 11941 switch (direction) 11942 { 11943 case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; 11944 case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; 11945 case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; 11946 case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; 11947 case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings 11948 } 11949 } 11950 11951 static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) 11952 { 11953 RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); 11954 RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); 11955 RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); 11956 RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); 11957 } 11958 11959 // ColorPicker 11960 // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. 11961 // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) 11962 bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) 11963 { 11964 ImGuiContext& g = *GImGui; 11965 ImGuiWindow* window = GetCurrentWindow(); 11966 ImDrawList* draw_list = window->DrawList; 11967 11968 ImGuiStyle& style = g.Style; 11969 ImGuiIO& io = g.IO; 11970 11971 PushID(label); 11972 BeginGroup(); 11973 11974 if (!(flags & ImGuiColorEditFlags_NoSidePreview)) 11975 flags |= ImGuiColorEditFlags_NoSmallPreview; 11976 11977 // Context menu: display and store options. 11978 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11979 ColorPickerOptionsPopup(flags, col); 11980 11981 // Read stored options 11982 if (!(flags & ImGuiColorEditFlags__PickerMask)) 11983 flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; 11984 IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected 11985 if (!(flags & ImGuiColorEditFlags_NoOptions)) 11986 flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); 11987 11988 // Setup 11989 int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; 11990 bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); 11991 ImVec2 picker_pos = window->DC.CursorPos; 11992 float square_sz = GetFrameHeight(); 11993 float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars 11994 float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box 11995 float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; 11996 float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; 11997 float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); 11998 11999 float backup_initial_col[4]; 12000 memcpy(backup_initial_col, col, components * sizeof(float)); 12001 12002 float wheel_thickness = sv_picker_size * 0.08f; 12003 float wheel_r_outer = sv_picker_size * 0.50f; 12004 float wheel_r_inner = wheel_r_outer - wheel_thickness; 12005 ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f); 12006 12007 // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. 12008 float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); 12009 ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. 12010 ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. 12011 ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. 12012 12013 float H, S, V; 12014 ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); 12015 12016 bool value_changed = false, value_changed_h = false, value_changed_sv = false; 12017 12018 PushItemFlag(ImGuiItemFlags_NoNav, true); 12019 if (flags & ImGuiColorEditFlags_PickerHueWheel) 12020 { 12021 // Hue wheel + SV triangle logic 12022 InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); 12023 if (IsItemActive()) 12024 { 12025 ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; 12026 ImVec2 current_off = g.IO.MousePos - wheel_center; 12027 float initial_dist2 = ImLengthSqr(initial_off); 12028 if (initial_dist2 >= (wheel_r_inner - 1)*(wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1)*(wheel_r_outer + 1)) 12029 { 12030 // Interactive with Hue wheel 12031 H = atan2f(current_off.y, current_off.x) / IM_PI * 0.5f; 12032 if (H < 0.0f) 12033 H += 1.0f; 12034 value_changed = value_changed_h = true; 12035 } 12036 float cos_hue_angle = cosf(-H * 2.0f * IM_PI); 12037 float sin_hue_angle = sinf(-H * 2.0f * IM_PI); 12038 if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) 12039 { 12040 // Interacting with SV triangle 12041 ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); 12042 if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) 12043 current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); 12044 float uu, vv, ww; 12045 ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); 12046 V = ImClamp(1.0f - vv, 0.0001f, 1.0f); 12047 S = ImClamp(uu / V, 0.0001f, 1.0f); 12048 value_changed = value_changed_sv = true; 12049 } 12050 } 12051 if (!(flags & ImGuiColorEditFlags_NoOptions)) 12052 OpenPopupOnItemClick("context"); 12053 } 12054 else if (flags & ImGuiColorEditFlags_PickerHueBar) 12055 { 12056 // SV rectangle logic 12057 InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); 12058 if (IsItemActive()) 12059 { 12060 S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); 12061 V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); 12062 value_changed = value_changed_sv = true; 12063 } 12064 if (!(flags & ImGuiColorEditFlags_NoOptions)) 12065 OpenPopupOnItemClick("context"); 12066 12067 // Hue bar logic 12068 SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); 12069 InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); 12070 if (IsItemActive()) 12071 { 12072 H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); 12073 value_changed = value_changed_h = true; 12074 } 12075 } 12076 12077 // Alpha bar logic 12078 if (alpha_bar) 12079 { 12080 SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); 12081 InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); 12082 if (IsItemActive()) 12083 { 12084 col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); 12085 value_changed = true; 12086 } 12087 } 12088 PopItemFlag(); // ImGuiItemFlags_NoNav 12089 12090 if (!(flags & ImGuiColorEditFlags_NoSidePreview)) 12091 { 12092 SameLine(0, style.ItemInnerSpacing.x); 12093 BeginGroup(); 12094 } 12095 12096 if (!(flags & ImGuiColorEditFlags_NoLabel)) 12097 { 12098 const char* label_display_end = FindRenderedTextEnd(label); 12099 if (label != label_display_end) 12100 { 12101 if ((flags & ImGuiColorEditFlags_NoSidePreview)) 12102 SameLine(0, style.ItemInnerSpacing.x); 12103 TextUnformatted(label, label_display_end); 12104 } 12105 } 12106 12107 if (!(flags & ImGuiColorEditFlags_NoSidePreview)) 12108 { 12109 PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); 12110 ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); 12111 if ((flags & ImGuiColorEditFlags_NoLabel)) 12112 Text("Current"); 12113 ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); 12114 if (ref_col != NULL) 12115 { 12116 Text("Original"); 12117 ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); 12118 if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) 12119 { 12120 memcpy(col, ref_col, components * sizeof(float)); 12121 value_changed = true; 12122 } 12123 } 12124 PopItemFlag(); 12125 EndGroup(); 12126 } 12127 12128 // Convert back color to RGB 12129 if (value_changed_h || value_changed_sv) 12130 ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); 12131 12132 // R,G,B and H,S,V slider color editor 12133 if ((flags & ImGuiColorEditFlags_NoInputs) == 0) 12134 { 12135 PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); 12136 ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; 12137 ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; 12138 if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) 12139 value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB); 12140 if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) 12141 value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); 12142 if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) 12143 value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); 12144 PopItemWidth(); 12145 } 12146 12147 // Try to cancel hue wrap (after ColorEdit), if any 12148 if (value_changed) 12149 { 12150 float new_H, new_S, new_V; 12151 ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); 12152 if (new_H <= 0 && H > 0) 12153 { 12154 if (new_V <= 0 && V != new_V) 12155 ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); 12156 else if (new_S <= 0) 12157 ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); 12158 } 12159 } 12160 12161 ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); 12162 ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); 12163 ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); 12164 12165 const ImU32 hue_colors[6 + 1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; 12166 ImVec2 sv_cursor_pos; 12167 12168 if (flags & ImGuiColorEditFlags_PickerHueWheel) 12169 { 12170 // Render Hue Wheel 12171 const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). 12172 const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); 12173 for (int n = 0; n < 6; n++) 12174 { 12175 const float a0 = (n) / 6.0f * 2.0f * IM_PI - aeps; 12176 const float a1 = (n + 1.0f) / 6.0f * 2.0f * IM_PI + aeps; 12177 const int vert_start_idx = draw_list->VtxBuffer.Size; 12178 draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); 12179 draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); 12180 const int vert_end_idx = draw_list->VtxBuffer.Size; 12181 12182 // Paint colors over existing vertices 12183 ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner); 12184 ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner); 12185 ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n + 1]); 12186 } 12187 12188 // Render Cursor + preview on Hue Wheel 12189 float cos_hue_angle = cosf(H * 2.0f * IM_PI); 12190 float sin_hue_angle = sinf(H * 2.0f * IM_PI); 12191 ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer)*0.5f); 12192 float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; 12193 int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); 12194 draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); 12195 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, IM_COL32(128, 128, 128, 255), hue_cursor_segments); 12196 draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); 12197 12198 // Render SV triangle (rotated according to hue) 12199 ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); 12200 ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); 12201 ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); 12202 ImVec2 uv_white = GetFontTexUvWhitePixel(); 12203 draw_list->PrimReserve(6, 6); 12204 draw_list->PrimVtx(tra, uv_white, hue_color32); 12205 draw_list->PrimVtx(trb, uv_white, hue_color32); 12206 draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); 12207 draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); 12208 draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); 12209 draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); 12210 draw_list->AddTriangle(tra, trb, trc, IM_COL32(128, 128, 128, 255), 1.5f); 12211 sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); 12212 } 12213 else if (flags & ImGuiColorEditFlags_PickerHueBar) 12214 { 12215 // Render SV Square 12216 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); 12217 draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); 12218 RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f); 12219 sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much 12220 sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); 12221 12222 // Render Hue Bar 12223 for (int i = 0; i < 6; ++i) 12224 draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); 12225 float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); 12226 RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); 12227 RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); 12228 } 12229 12230 // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) 12231 float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; 12232 draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); 12233 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, IM_COL32(128, 128, 128, 255), 12); 12234 draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); 12235 12236 // Render alpha bar 12237 if (alpha_bar) 12238 { 12239 float alpha = ImSaturate(col[3]); 12240 ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); 12241 RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0, 0, 0, 0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); 12242 draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); 12243 float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); 12244 RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); 12245 RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); 12246 } 12247 12248 EndGroup(); 12249 PopID(); 12250 12251 return value_changed && memcmp(backup_initial_col, col, components * sizeof(float)); 12252 } 12253 12254 // Horizontal separating line. 12255 void ImGui::Separator() 12256 { 12257 ImGuiWindow* window = GetCurrentWindow(); 12258 if (window->SkipItems) 12259 return; 12260 ImGuiContext& g = *GImGui; 12261 12262 ImGuiSeparatorFlags flags = 0; 12263 if ((flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)) == 0) 12264 flags |= (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; 12265 IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected 12266 if (flags & ImGuiSeparatorFlags_Vertical) 12267 { 12268 VerticalSeparator(); 12269 return; 12270 } 12271 12272 // Horizontal Separator 12273 if (window->DC.ColumnsSet) 12274 PopClipRect(); 12275 12276 float x1 = window->Pos.x; 12277 float x2 = window->Pos.x + window->Size.x; 12278 if (!window->DC.GroupStack.empty()) 12279 x1 += window->DC.IndentX; 12280 12281 const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + 1.0f)); 12282 ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. 12283 if (!ItemAdd(bb, 0)) 12284 { 12285 if (window->DC.ColumnsSet) 12286 PushColumnClipRect(); 12287 return; 12288 } 12289 12290 window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x, bb.Min.y), GetColorU32(ImGuiCol_Separator)); 12291 12292 if (g.LogEnabled) 12293 LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); 12294 12295 if (window->DC.ColumnsSet) 12296 { 12297 PushColumnClipRect(); 12298 window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; 12299 } 12300 } 12301 12302 void ImGui::VerticalSeparator() 12303 { 12304 ImGuiWindow* window = GetCurrentWindow(); 12305 if (window->SkipItems) 12306 return; 12307 ImGuiContext& g = *GImGui; 12308 12309 float y1 = window->DC.CursorPos.y; 12310 float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight; 12311 const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); 12312 ItemSize(ImVec2(bb.GetWidth(), 0.0f)); 12313 if (!ItemAdd(bb, 0)) 12314 return; 12315 12316 window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); 12317 if (g.LogEnabled) 12318 LogText(" |"); 12319 } 12320 12321 bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend) 12322 { 12323 ImGuiContext& g = *GImGui; 12324 ImGuiWindow* window = g.CurrentWindow; 12325 12326 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; 12327 window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; 12328 bool item_add = ItemAdd(bb, id); 12329 window->DC.ItemFlags = item_flags_backup; 12330 if (!item_add) 12331 return false; 12332 12333 bool hovered, held; 12334 ImRect bb_interact = bb; 12335 bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); 12336 ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); 12337 if (g.ActiveId != id) 12338 SetItemAllowOverlap(); 12339 12340 if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id)) 12341 SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); 12342 12343 ImRect bb_render = bb; 12344 if (held) 12345 { 12346 ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; 12347 float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; 12348 12349 // Minimum pane size 12350 if (mouse_delta < min_size1 - *size1) 12351 mouse_delta = min_size1 - *size1; 12352 if (mouse_delta > *size2 - min_size2) 12353 mouse_delta = *size2 - min_size2; 12354 12355 // Apply resize 12356 *size1 += mouse_delta; 12357 *size2 -= mouse_delta; 12358 bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); 12359 } 12360 12361 // Render 12362 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); 12363 window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); 12364 12365 return held; 12366 } 12367 12368 void ImGui::Spacing() 12369 { 12370 ImGuiWindow* window = GetCurrentWindow(); 12371 if (window->SkipItems) 12372 return; 12373 ItemSize(ImVec2(0, 0)); 12374 } 12375 12376 void ImGui::Dummy(const ImVec2& size) 12377 { 12378 ImGuiWindow* window = GetCurrentWindow(); 12379 if (window->SkipItems) 12380 return; 12381 12382 const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); 12383 ItemSize(bb); 12384 ItemAdd(bb, 0); 12385 } 12386 12387 bool ImGui::IsRectVisible(const ImVec2& size) 12388 { 12389 ImGuiWindow* window = GetCurrentWindowRead(); 12390 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); 12391 } 12392 12393 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) 12394 { 12395 ImGuiWindow* window = GetCurrentWindowRead(); 12396 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); 12397 } 12398 12399 // 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.) 12400 void ImGui::BeginGroup() 12401 { 12402 ImGuiWindow* window = GetCurrentWindow(); 12403 12404 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); 12405 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 12406 group_data.BackupCursorPos = window->DC.CursorPos; 12407 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; 12408 group_data.BackupIndentX = window->DC.IndentX; 12409 group_data.BackupGroupOffsetX = window->DC.GroupOffsetX; 12410 group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; 12411 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; 12412 group_data.BackupLogLinePosY = window->DC.LogLinePosY; 12413 group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive; 12414 group_data.AdvanceCursor = true; 12415 12416 window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX; 12417 window->DC.IndentX = window->DC.GroupOffsetX; 12418 window->DC.CursorMaxPos = window->DC.CursorPos; 12419 window->DC.CurrentLineHeight = 0.0f; 12420 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; 12421 } 12422 12423 void ImGui::EndGroup() 12424 { 12425 ImGuiContext& g = *GImGui; 12426 ImGuiWindow* window = GetCurrentWindow(); 12427 12428 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls 12429 12430 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 12431 12432 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); 12433 group_bb.Max = ImMax(group_bb.Min, group_bb.Max); 12434 12435 window->DC.CursorPos = group_data.BackupCursorPos; 12436 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); 12437 window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; 12438 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; 12439 window->DC.IndentX = group_data.BackupIndentX; 12440 window->DC.GroupOffsetX = group_data.BackupGroupOffsetX; 12441 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; 12442 12443 if (group_data.AdvanceCursor) 12444 { 12445 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. 12446 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); 12447 ItemAdd(group_bb, 0); 12448 } 12449 12450 // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will be functional on the entire group. 12451 // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context. 12452 const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow); 12453 if (active_id_within_group) 12454 window->DC.LastItemId = g.ActiveId; 12455 window->DC.LastItemRect = group_bb; 12456 12457 window->DC.GroupStack.pop_back(); 12458 12459 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] 12460 } 12461 12462 // Gets back to previous line and continue with horizontal layout 12463 // pos_x == 0 : follow right after previous item 12464 // pos_x != 0 : align to specified x position (relative to window/group left) 12465 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 12466 // spacing_w >= 0 : enforce spacing amount 12467 void ImGui::SameLine(float pos_x, float spacing_w) 12468 { 12469 ImGuiWindow* window = GetCurrentWindow(); 12470 if (window->SkipItems) 12471 return; 12472 12473 ImGuiContext& g = *GImGui; 12474 if (pos_x != 0.0f) 12475 { 12476 if (spacing_w < 0.0f) spacing_w = 0.0f; 12477 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX; 12478 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 12479 } 12480 else 12481 { 12482 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; 12483 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; 12484 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 12485 } 12486 window->DC.CurrentLineHeight = window->DC.PrevLineHeight; 12487 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; 12488 } 12489 12490 void ImGui::NewLine() 12491 { 12492 ImGuiWindow* window = GetCurrentWindow(); 12493 if (window->SkipItems) 12494 return; 12495 12496 ImGuiContext& g = *GImGui; 12497 const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; 12498 window->DC.LayoutType = ImGuiLayoutType_Vertical; 12499 if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. 12500 ItemSize(ImVec2(0, 0)); 12501 else 12502 ItemSize(ImVec2(0.0f, g.FontSize)); 12503 window->DC.LayoutType = backup_layout_type; 12504 } 12505 12506 void ImGui::NextColumn() 12507 { 12508 ImGuiWindow* window = GetCurrentWindow(); 12509 if (window->SkipItems || window->DC.ColumnsSet == NULL) 12510 return; 12511 12512 ImGuiContext& g = *GImGui; 12513 PopItemWidth(); 12514 PopClipRect(); 12515 12516 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12517 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); 12518 if (++columns->Current < columns->Count) 12519 { 12520 // Columns 1+ cancel out IndentX 12521 window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x; 12522 window->DrawList->ChannelsSetCurrent(columns->Current); 12523 } 12524 else 12525 { 12526 window->DC.ColumnsOffsetX = 0.0f; 12527 window->DrawList->ChannelsSetCurrent(0); 12528 columns->Current = 0; 12529 columns->LineMinY = columns->LineMaxY; 12530 } 12531 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); 12532 window->DC.CursorPos.y = columns->LineMinY; 12533 window->DC.CurrentLineHeight = 0.0f; 12534 window->DC.CurrentLineTextBaseOffset = 0.0f; 12535 12536 PushColumnClipRect(); 12537 PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup 12538 } 12539 12540 int ImGui::GetColumnIndex() 12541 { 12542 ImGuiWindow* window = GetCurrentWindowRead(); 12543 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0; 12544 } 12545 12546 int ImGui::GetColumnsCount() 12547 { 12548 ImGuiWindow* window = GetCurrentWindowRead(); 12549 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1; 12550 } 12551 12552 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm) 12553 { 12554 return offset_norm * (columns->MaxX - columns->MinX); 12555 } 12556 12557 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset) 12558 { 12559 return offset / (columns->MaxX - columns->MinX); 12560 } 12561 12562 static inline float GetColumnsRectHalfWidth() { return 4.0f; } 12563 12564 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index) 12565 { 12566 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing 12567 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. 12568 ImGuiContext& g = *GImGui; 12569 ImGuiWindow* window = g.CurrentWindow; 12570 IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. 12571 IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); 12572 12573 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x; 12574 x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); 12575 if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths)) 12576 x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); 12577 12578 return x; 12579 } 12580 12581 float ImGui::GetColumnOffset(int column_index) 12582 { 12583 ImGuiWindow* window = GetCurrentWindowRead(); 12584 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12585 IM_ASSERT(columns != NULL); 12586 12587 if (column_index < 0) 12588 column_index = columns->Current; 12589 IM_ASSERT(column_index < columns->Columns.Size); 12590 12591 const float t = columns->Columns[column_index].OffsetNorm; 12592 const float x_offset = ImLerp(columns->MinX, columns->MaxX, t); 12593 return x_offset; 12594 } 12595 12596 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false) 12597 { 12598 if (column_index < 0) 12599 column_index = columns->Current; 12600 12601 float offset_norm; 12602 if (before_resize) 12603 offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize; 12604 else 12605 offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm; 12606 return OffsetNormToPixels(columns, offset_norm); 12607 } 12608 12609 float ImGui::GetColumnWidth(int column_index) 12610 { 12611 ImGuiWindow* window = GetCurrentWindowRead(); 12612 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12613 IM_ASSERT(columns != NULL); 12614 12615 if (column_index < 0) 12616 column_index = columns->Current; 12617 return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm); 12618 } 12619 12620 void ImGui::SetColumnOffset(int column_index, float offset) 12621 { 12622 ImGuiContext& g = *GImGui; 12623 ImGuiWindow* window = g.CurrentWindow; 12624 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12625 IM_ASSERT(columns != NULL); 12626 12627 if (column_index < 0) 12628 column_index = columns->Current; 12629 IM_ASSERT(column_index < columns->Columns.Size); 12630 12631 const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count - 1); 12632 const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; 12633 12634 if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow)) 12635 offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); 12636 columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX); 12637 12638 if (preserve_width) 12639 SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); 12640 } 12641 12642 void ImGui::SetColumnWidth(int column_index, float width) 12643 { 12644 ImGuiWindow* window = GetCurrentWindowRead(); 12645 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12646 IM_ASSERT(columns != NULL); 12647 12648 if (column_index < 0) 12649 column_index = columns->Current; 12650 SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width); 12651 } 12652 12653 void ImGui::PushColumnClipRect(int column_index) 12654 { 12655 ImGuiWindow* window = GetCurrentWindowRead(); 12656 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12657 if (column_index < 0) 12658 column_index = columns->Current; 12659 12660 PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false); 12661 } 12662 12663 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id) 12664 { 12665 for (int n = 0; n < window->ColumnsStorage.Size; n++) 12666 if (window->ColumnsStorage[n].ID == id) 12667 return &window->ColumnsStorage[n]; 12668 12669 window->ColumnsStorage.push_back(ImGuiColumnsSet()); 12670 ImGuiColumnsSet* columns = &window->ColumnsStorage.back(); 12671 columns->ID = id; 12672 return columns; 12673 } 12674 12675 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags) 12676 { 12677 ImGuiContext& g = *GImGui; 12678 ImGuiWindow* window = GetCurrentWindow(); 12679 12680 IM_ASSERT(columns_count > 1); 12681 IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported 12682 12683 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. 12684 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. 12685 PushID(0x11223347 + (str_id ? 0 : columns_count)); 12686 ImGuiID id = window->GetID(str_id ? str_id : "columns"); 12687 PopID(); 12688 12689 // Acquire storage for the columns set 12690 ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id); 12691 IM_ASSERT(columns->ID == id); 12692 columns->Current = 0; 12693 columns->Count = columns_count; 12694 columns->Flags = flags; 12695 window->DC.ColumnsSet = columns; 12696 12697 // Set state for first column 12698 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x); 12699 columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range 12700 columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f); 12701 columns->StartPosY = window->DC.CursorPos.y; 12702 columns->StartMaxPosX = window->DC.CursorMaxPos.x; 12703 columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; 12704 window->DC.ColumnsOffsetX = 0.0f; 12705 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); 12706 12707 // Clear data if columns count changed 12708 if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) 12709 columns->Columns.resize(0); 12710 12711 // Initialize defaults 12712 columns->IsFirstFrame = (columns->Columns.Size == 0); 12713 if (columns->Columns.Size == 0) 12714 { 12715 columns->Columns.reserve(columns_count + 1); 12716 for (int n = 0; n < columns_count + 1; n++) 12717 { 12718 ImGuiColumnData column; 12719 column.OffsetNorm = n / (float)columns_count; 12720 columns->Columns.push_back(column); 12721 } 12722 } 12723 12724 for (int n = 0; n < columns_count; n++) 12725 { 12726 // Compute clipping rectangle 12727 ImGuiColumnData* column = &columns->Columns[n]; 12728 float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f); 12729 float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f); 12730 column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); 12731 column->ClipRect.ClipWith(window->ClipRect); 12732 } 12733 12734 window->DrawList->ChannelsSplit(columns->Count); 12735 PushColumnClipRect(); 12736 PushItemWidth(GetColumnWidth() * 0.65f); 12737 } 12738 12739 void ImGui::EndColumns() 12740 { 12741 ImGuiContext& g = *GImGui; 12742 ImGuiWindow* window = GetCurrentWindow(); 12743 ImGuiColumnsSet* columns = window->DC.ColumnsSet; 12744 IM_ASSERT(columns != NULL); 12745 12746 PopItemWidth(); 12747 PopClipRect(); 12748 window->DrawList->ChannelsMerge(); 12749 12750 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); 12751 window->DC.CursorPos.y = columns->LineMaxY; 12752 if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize)) 12753 window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent 12754 12755 // Draw columns borders and handle resize 12756 bool is_being_resized = false; 12757 if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems) 12758 { 12759 const float y1 = columns->StartPosY; 12760 const float y2 = window->DC.CursorPos.y; 12761 int dragging_column = -1; 12762 for (int n = 1; n < columns->Count; n++) 12763 { 12764 float x = window->Pos.x + GetColumnOffset(n); 12765 const ImGuiID column_id = columns->ID + ImGuiID(n); 12766 const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction 12767 const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2)); 12768 KeepAliveID(column_id); 12769 if (IsClippedEx(column_rect, column_id, false)) 12770 continue; 12771 12772 bool hovered = false, held = false; 12773 if (!(columns->Flags & ImGuiColumnsFlags_NoResize)) 12774 { 12775 ButtonBehavior(column_rect, column_id, &hovered, &held); 12776 if (hovered || held) 12777 g.MouseCursor = ImGuiMouseCursor_ResizeEW; 12778 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize)) 12779 dragging_column = n; 12780 } 12781 12782 // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.) 12783 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); 12784 const float xi = (float)(int)x; 12785 window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col); 12786 } 12787 12788 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. 12789 if (dragging_column != -1) 12790 { 12791 if (!columns->IsBeingResized) 12792 for (int n = 0; n < columns->Count + 1; n++) 12793 columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm; 12794 columns->IsBeingResized = is_being_resized = true; 12795 float x = GetDraggedColumnOffset(columns, dragging_column); 12796 SetColumnOffset(dragging_column, x); 12797 } 12798 } 12799 columns->IsBeingResized = is_being_resized; 12800 12801 window->DC.ColumnsSet = NULL; 12802 window->DC.ColumnsOffsetX = 0.0f; 12803 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); 12804 } 12805 12806 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing] 12807 void ImGui::Columns(int columns_count, const char* id, bool border) 12808 { 12809 ImGuiWindow* window = GetCurrentWindow(); 12810 IM_ASSERT(columns_count >= 1); 12811 12812 ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder); 12813 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior 12814 if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags) 12815 return; 12816 12817 if (window->DC.ColumnsSet != NULL) 12818 EndColumns(); 12819 12820 if (columns_count != 1) 12821 BeginColumns(id, columns_count, flags); 12822 } 12823 12824 void ImGui::Indent(float indent_w) 12825 { 12826 ImGuiContext& g = *GImGui; 12827 ImGuiWindow* window = GetCurrentWindow(); 12828 window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 12829 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; 12830 } 12831 12832 void ImGui::Unindent(float indent_w) 12833 { 12834 ImGuiContext& g = *GImGui; 12835 ImGuiWindow* window = GetCurrentWindow(); 12836 window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 12837 window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; 12838 } 12839 12840 void ImGui::TreePush(const char* str_id) 12841 { 12842 ImGuiWindow* window = GetCurrentWindow(); 12843 Indent(); 12844 window->DC.TreeDepth++; 12845 PushID(str_id ? str_id : "#TreePush"); 12846 } 12847 12848 void ImGui::TreePush(const void* ptr_id) 12849 { 12850 ImGuiWindow* window = GetCurrentWindow(); 12851 Indent(); 12852 window->DC.TreeDepth++; 12853 PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); 12854 } 12855 12856 void ImGui::TreePushRawID(ImGuiID id) 12857 { 12858 ImGuiWindow* window = GetCurrentWindow(); 12859 Indent(); 12860 window->DC.TreeDepth++; 12861 window->IDStack.push_back(id); 12862 } 12863 12864 void ImGui::TreePop() 12865 { 12866 ImGuiContext& g = *GImGui; 12867 ImGuiWindow* window = g.CurrentWindow; 12868 Unindent(); 12869 12870 window->DC.TreeDepth--; 12871 if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) 12872 if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) 12873 { 12874 SetNavID(window->IDStack.back(), g.NavLayer); 12875 NavMoveRequestCancel(); 12876 } 12877 window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; 12878 12879 PopID(); 12880 } 12881 12882 void ImGui::Value(const char* prefix, bool b) 12883 { 12884 Text("%s: %s", prefix, (b ? "true" : "false")); 12885 } 12886 12887 void ImGui::Value(const char* prefix, int v) 12888 { 12889 Text("%s: %d", prefix, v); 12890 } 12891 12892 void ImGui::Value(const char* prefix, unsigned int v) 12893 { 12894 Text("%s: %d", prefix, v); 12895 } 12896 12897 void ImGui::Value(const char* prefix, float v, const char* float_format) 12898 { 12899 if (float_format) 12900 { 12901 char fmt[64]; 12902 ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); 12903 Text(fmt, prefix, v); 12904 } 12905 else 12906 { 12907 Text("%s: %.3f", prefix, v); 12908 } 12909 } 9301 const char* label = window->Name; 9302 if (label == FindRenderedTextEnd(label)) 9303 label = GetFallbackWindowNameForWindowingList(window); 9304 Selectable(label, g.NavWindowingTarget == window); 9305 } 9306 End(); 9307 PopStyleVar(); 9308 } 9309 12910 9310 12911 9311 //----------------------------------------------------------------------------- 12912 // DRAG AND DROP9312 // [SECTION] DRAG AND DROP 12913 9313 //----------------------------------------------------------------------------- 12914 9314 12915 9315 void ImGui::ClearDragDrop() 12916 9316 { 12917 ImGuiContext& g = *GImGui; 12918 g.DragDropActive = false; 12919 g.DragDropPayload.Clear(); 12920 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; 12921 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 12922 g.DragDropAcceptFrameCount = -1; 12923 } 12924 12925 // Call when current ID is active. 9317 ImGuiContext& g = *GImGui; 9318 g.DragDropActive = false; 9319 g.DragDropPayload.Clear(); 9320 g.DragDropAcceptFlags = ImGuiDragDropFlags_None; 9321 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; 9322 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 9323 g.DragDropAcceptFrameCount = -1; 9324 9325 g.DragDropPayloadBufHeap.clear(); 9326 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9327 } 9328 9329 // Call when current ID is active. 12926 9330 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() 12927 9331 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) 12928 9332 { 12929 ImGuiContext& g = *GImGui; 12930 ImGuiWindow* window = g.CurrentWindow; 12931 12932 bool source_drag_active = false; 12933 ImGuiID source_id = 0; 12934 ImGuiID source_parent_id = 0; 12935 int mouse_button = 0; 12936 if (!(flags & ImGuiDragDropFlags_SourceExtern)) 12937 { 12938 source_id = window->DC.LastItemId; 12939 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case 12940 return false; 12941 if (g.IO.MouseDown[mouse_button] == false) 12942 return false; 12943 12944 if (source_id == 0) 12945 { 12946 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: 12947 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. 12948 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) 12949 { 12950 IM_ASSERT(0); 9333 ImGuiContext& g = *GImGui; 9334 ImGuiWindow* window = g.CurrentWindow; 9335 9336 bool source_drag_active = false; 9337 ImGuiID source_id = 0; 9338 ImGuiID source_parent_id = 0; 9339 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; 9340 if (!(flags & ImGuiDragDropFlags_SourceExtern)) 9341 { 9342 source_id = window->DC.LastItemId; 9343 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case 12951 9344 return false; 12952 } 12953 12954 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() 12955 // We build a throwaway ID based on current ID stack + relative AABB of items in window. 12956 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. 12957 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. 12958 bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0; 12959 if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) 9345 if (g.IO.MouseDown[mouse_button] == false) 12960 9346 return false; 12961 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); 12962 if (is_hovered) 12963 SetHoveredID(source_id); 12964 if (is_hovered && g.IO.MouseClicked[mouse_button]) 12965 { 12966 SetActiveID(source_id, window); 12967 FocusWindow(window); 12968 } 12969 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. 12970 g.ActiveIdAllowOverlap = is_hovered; 12971 } 12972 if (g.ActiveId != source_id) 12973 return false; 12974 source_parent_id = window->IDStack.back(); 12975 source_drag_active = IsMouseDragging(mouse_button); 12976 } 12977 else 12978 { 12979 window = NULL; 12980 source_id = ImHash("#SourceExtern", 0); 12981 source_drag_active = true; 12982 } 12983 12984 if (source_drag_active) 12985 { 12986 if (!g.DragDropActive) 12987 { 12988 IM_ASSERT(source_id != 0); 12989 ClearDragDrop(); 12990 ImGuiPayload& payload = g.DragDropPayload; 12991 payload.SourceId = source_id; 12992 payload.SourceParentId = source_parent_id; 12993 g.DragDropActive = true; 12994 g.DragDropSourceFlags = flags; 12995 g.DragDropMouseButton = mouse_button; 12996 } 12997 12998 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 12999 { 13000 // FIXME-DRAG 13001 //SetNextWindowPos(g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding); 13002 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This is better but e.g ColorButton with checkboard has issue with transparent colors :( 13003 SetNextWindowPos(g.IO.MousePos); 13004 PushStyleColor(ImGuiCol_PopupBg, GetStyleColorVec4(ImGuiCol_PopupBg) * ImVec4(1.0f, 1.0f, 1.0f, 0.6f)); 13005 BeginTooltip(); 13006 } 13007 13008 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) 13009 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; 13010 13011 return true; 13012 } 13013 return false; 9347 9348 if (source_id == 0) 9349 { 9350 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: 9351 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. 9352 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) 9353 { 9354 IM_ASSERT(0); 9355 return false; 9356 } 9357 9358 // Early out 9359 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) 9360 return false; 9361 9362 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() 9363 // We build a throwaway ID based on current ID stack + relative AABB of items in window. 9364 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. 9365 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. 9366 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); 9367 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); 9368 if (is_hovered && g.IO.MouseClicked[mouse_button]) 9369 { 9370 SetActiveID(source_id, window); 9371 FocusWindow(window); 9372 } 9373 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. 9374 g.ActiveIdAllowOverlap = is_hovered; 9375 } 9376 else 9377 { 9378 g.ActiveIdAllowOverlap = false; 9379 } 9380 if (g.ActiveId != source_id) 9381 return false; 9382 source_parent_id = window->IDStack.back(); 9383 source_drag_active = IsMouseDragging(mouse_button); 9384 9385 // Disable navigation and key inputs while dragging 9386 g.ActiveIdUsingNavDirMask = ~(ImU32)0; 9387 g.ActiveIdUsingNavInputMask = ~(ImU32)0; 9388 g.ActiveIdUsingKeyInputMask = ~(ImU64)0; 9389 } 9390 else 9391 { 9392 window = NULL; 9393 source_id = ImHashStr("#SourceExtern"); 9394 source_drag_active = true; 9395 } 9396 9397 if (source_drag_active) 9398 { 9399 if (!g.DragDropActive) 9400 { 9401 IM_ASSERT(source_id != 0); 9402 ClearDragDrop(); 9403 ImGuiPayload& payload = g.DragDropPayload; 9404 payload.SourceId = source_id; 9405 payload.SourceParentId = source_parent_id; 9406 g.DragDropActive = true; 9407 g.DragDropSourceFlags = flags; 9408 g.DragDropMouseButton = mouse_button; 9409 } 9410 g.DragDropSourceFrameCount = g.FrameCount; 9411 g.DragDropWithinSource = true; 9412 9413 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9414 { 9415 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) 9416 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. 9417 BeginTooltip(); 9418 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) 9419 { 9420 ImGuiWindow* tooltip_window = g.CurrentWindow; 9421 tooltip_window->SkipItems = true; 9422 tooltip_window->HiddenFramesCanSkipItems = 1; 9423 } 9424 } 9425 9426 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) 9427 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; 9428 9429 return true; 9430 } 9431 return false; 13014 9432 } 13015 9433 13016 9434 void ImGui::EndDragDropSource() 13017 9435 { 13018 ImGuiContext& g = *GImGui; 13019 IM_ASSERT(g.DragDropActive); 13020 13021 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 13022 { 13023 EndTooltip(); 13024 PopStyleColor(); 13025 //PopStyleVar(); 13026 } 13027 13028 // Discard the drag if have not called SetDragDropPayload() 13029 if (g.DragDropPayload.DataFrameCount == -1) 13030 ClearDragDrop(); 9436 ImGuiContext& g = *GImGui; 9437 IM_ASSERT(g.DragDropActive); 9438 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?"); 9439 9440 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9441 EndTooltip(); 9442 9443 // Discard the drag if have not called SetDragDropPayload() 9444 if (g.DragDropPayload.DataFrameCount == -1) 9445 ClearDragDrop(); 9446 g.DragDropWithinSource = false; 13031 9447 } 13032 9448 … … 13034 9450 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) 13035 9451 { 13036 ImGuiContext& g = *GImGui;13037 ImGuiPayload& payload = g.DragDropPayload;13038 if (cond == 0)13039 cond = ImGuiCond_Always;13040 13041 IM_ASSERT(type != NULL);13042 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 12 characters long");13043 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));13044 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);13045 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()13046 13047 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)13048 {13049 // Copy payload13050 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));13051 g.DragDropPayloadBufHeap.resize(0);13052 if (data_size > sizeof(g.DragDropPayloadBufLocal))13053 {13054 // Store in heap13055 g.DragDropPayloadBufHeap.resize((int)data_size);13056 payload.Data = g.DragDropPayloadBufHeap.Data;13057 memcpy(payload.Data, data, data_size);13058 }13059 else if (data_size > 0)13060 {13061 // Store locally13062 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));13063 payload.Data = g.DragDropPayloadBufLocal;13064 memcpy(payload.Data, data, data_size);13065 }13066 else13067 {13068 payload.Data = NULL;13069 }13070 payload.DataSize = (int)data_size;13071 }13072 payload.DataFrameCount = g.FrameCount;13073 13074 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);9452 ImGuiContext& g = *GImGui; 9453 ImGuiPayload& payload = g.DragDropPayload; 9454 if (cond == 0) 9455 cond = ImGuiCond_Always; 9456 9457 IM_ASSERT(type != NULL); 9458 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); 9459 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); 9460 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); 9461 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() 9462 9463 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) 9464 { 9465 // Copy payload 9466 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); 9467 g.DragDropPayloadBufHeap.resize(0); 9468 if (data_size > sizeof(g.DragDropPayloadBufLocal)) 9469 { 9470 // Store in heap 9471 g.DragDropPayloadBufHeap.resize((int)data_size); 9472 payload.Data = g.DragDropPayloadBufHeap.Data; 9473 memcpy(payload.Data, data, data_size); 9474 } 9475 else if (data_size > 0) 9476 { 9477 // Store locally 9478 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9479 payload.Data = g.DragDropPayloadBufLocal; 9480 memcpy(payload.Data, data, data_size); 9481 } 9482 else 9483 { 9484 payload.Data = NULL; 9485 } 9486 payload.DataSize = (int)data_size; 9487 } 9488 payload.DataFrameCount = g.FrameCount; 9489 9490 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); 13075 9491 } 13076 9492 13077 9493 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) 13078 9494 { 13079 ImGuiContext& g = *GImGui; 13080 if (!g.DragDropActive) 13081 return false; 13082 13083 ImGuiWindow* window = g.CurrentWindow; 13084 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) 13085 return false; 13086 IM_ASSERT(id != 0); 13087 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) 13088 return false; 13089 13090 g.DragDropTargetRect = bb; 13091 g.DragDropTargetId = id; 13092 return true; 9495 ImGuiContext& g = *GImGui; 9496 if (!g.DragDropActive) 9497 return false; 9498 9499 ImGuiWindow* window = g.CurrentWindow; 9500 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; 9501 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) 9502 return false; 9503 IM_ASSERT(id != 0); 9504 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) 9505 return false; 9506 if (window->SkipItems) 9507 return false; 9508 9509 IM_ASSERT(g.DragDropWithinTarget == false); 9510 g.DragDropTargetRect = bb; 9511 g.DragDropTargetId = id; 9512 g.DragDropWithinTarget = true; 9513 return true; 13093 9514 } 13094 9515 … … 13099 9520 bool ImGui::BeginDragDropTarget() 13100 9521 { 13101 ImGuiContext& g = *GImGui; 13102 if (!g.DragDropActive) 13103 return false; 13104 13105 ImGuiWindow* window = g.CurrentWindow; 13106 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 13107 return false; 13108 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) 13109 return false; 13110 13111 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; 13112 ImGuiID id = window->DC.LastItemId; 13113 if (id == 0) 13114 id = window->GetIDFromRectangle(display_rect); 13115 if (g.DragDropPayload.SourceId == id) 13116 return false; 13117 13118 g.DragDropTargetRect = display_rect; 13119 g.DragDropTargetId = id; 13120 return true; 9522 ImGuiContext& g = *GImGui; 9523 if (!g.DragDropActive) 9524 return false; 9525 9526 ImGuiWindow* window = g.CurrentWindow; 9527 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 9528 return false; 9529 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; 9530 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) 9531 return false; 9532 9533 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; 9534 ImGuiID id = window->DC.LastItemId; 9535 if (id == 0) 9536 id = window->GetIDFromRectangle(display_rect); 9537 if (g.DragDropPayload.SourceId == id) 9538 return false; 9539 9540 IM_ASSERT(g.DragDropWithinTarget == false); 9541 g.DragDropTargetRect = display_rect; 9542 g.DragDropTargetId = id; 9543 g.DragDropWithinTarget = true; 9544 return true; 13121 9545 } 13122 9546 13123 9547 bool ImGui::IsDragDropPayloadBeingAccepted() 13124 9548 { 13125 ImGuiContext& g = *GImGui;13126 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;9549 ImGuiContext& g = *GImGui; 9550 return g.DragDropActive && g.DragDropAcceptIdPrev != 0; 13127 9551 } 13128 9552 13129 9553 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) 13130 9554 { 13131 ImGuiContext& g = *GImGui; 13132 ImGuiWindow* window = g.CurrentWindow; 13133 ImGuiPayload& payload = g.DragDropPayload; 13134 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? 13135 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? 13136 if (type != NULL && !payload.IsDataType(type)) 13137 return NULL; 13138 13139 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. 13140 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! 13141 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); 13142 ImRect r = g.DragDropTargetRect; 13143 float r_surface = r.GetWidth() * r.GetHeight(); 13144 if (r_surface < g.DragDropAcceptIdCurrRectSurface) 13145 { 13146 g.DragDropAcceptIdCurr = g.DragDropTargetId; 13147 g.DragDropAcceptIdCurrRectSurface = r_surface; 13148 } 13149 13150 // Render default drop visuals 13151 payload.Preview = was_accepted_previously; 13152 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) 13153 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) 13154 { 13155 // FIXME-DRAG: Settle on a proper default visuals for drop target. 13156 r.Expand(3.5f); 13157 bool push_clip_rect = !window->ClipRect.Contains(r); 13158 if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); 13159 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); 13160 if (push_clip_rect) window->DrawList->PopClipRect(); 13161 } 13162 13163 g.DragDropAcceptFrameCount = g.FrameCount; 13164 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() 13165 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) 13166 return NULL; 13167 13168 return &payload; 9555 ImGuiContext& g = *GImGui; 9556 ImGuiWindow* window = g.CurrentWindow; 9557 ImGuiPayload& payload = g.DragDropPayload; 9558 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? 9559 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? 9560 if (type != NULL && !payload.IsDataType(type)) 9561 return NULL; 9562 9563 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. 9564 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! 9565 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); 9566 ImRect r = g.DragDropTargetRect; 9567 float r_surface = r.GetWidth() * r.GetHeight(); 9568 if (r_surface < g.DragDropAcceptIdCurrRectSurface) 9569 { 9570 g.DragDropAcceptFlags = flags; 9571 g.DragDropAcceptIdCurr = g.DragDropTargetId; 9572 g.DragDropAcceptIdCurrRectSurface = r_surface; 9573 } 9574 9575 // Render default drop visuals 9576 payload.Preview = was_accepted_previously; 9577 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) 9578 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) 9579 { 9580 // FIXME-DRAG: Settle on a proper default visuals for drop target. 9581 r.Expand(3.5f); 9582 bool push_clip_rect = !window->ClipRect.Contains(r); 9583 if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1)); 9584 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); 9585 if (push_clip_rect) window->DrawList->PopClipRect(); 9586 } 9587 9588 g.DragDropAcceptFrameCount = g.FrameCount; 9589 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() 9590 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) 9591 return NULL; 9592 9593 return &payload; 9594 } 9595 9596 const ImGuiPayload* ImGui::GetDragDropPayload() 9597 { 9598 ImGuiContext& g = *GImGui; 9599 return g.DragDropActive ? &g.DragDropPayload : NULL; 13169 9600 } 13170 9601 … … 13172 9603 void ImGui::EndDragDropTarget() 13173 9604 { 13174 ImGuiContext& g = *GImGui; (void)g; 13175 IM_ASSERT(g.DragDropActive); 9605 ImGuiContext& g = *GImGui; 9606 IM_ASSERT(g.DragDropActive); 9607 IM_ASSERT(g.DragDropWithinTarget); 9608 g.DragDropWithinTarget = false; 13176 9609 } 13177 9610 13178 9611 //----------------------------------------------------------------------------- 13179 // PLATFORM DEPENDENT HELPERS9612 // [SECTION] LOGGING/CAPTURING 13180 9613 //----------------------------------------------------------------------------- 13181 13182 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) 13183 #undef WIN32_LEAN_AND_MEAN 13184 #define WIN32_LEAN_AND_MEAN 13185 #ifndef __MINGW32__ 13186 #include <Windows.h> 9614 // All text output from the interface can be captured into tty/file/clipboard. 9615 // By default, tree nodes are automatically opened during logging. 9616 //----------------------------------------------------------------------------- 9617 9618 // Pass text data straight to log (without being displayed) 9619 void ImGui::LogText(const char* fmt, ...) 9620 { 9621 ImGuiContext& g = *GImGui; 9622 if (!g.LogEnabled) 9623 return; 9624 9625 va_list args; 9626 va_start(args, fmt); 9627 if (g.LogFile) 9628 { 9629 g.LogBuffer.Buf.resize(0); 9630 g.LogBuffer.appendfv(fmt, args); 9631 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile); 9632 } 9633 else 9634 { 9635 g.LogBuffer.appendfv(fmt, args); 9636 } 9637 va_end(args); 9638 } 9639 9640 // Internal version that takes a position to decide on newline placement and pad items according to their depth. 9641 // We split text into individual lines to add current tree level padding 9642 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) 9643 { 9644 ImGuiContext& g = *GImGui; 9645 ImGuiWindow* window = g.CurrentWindow; 9646 9647 if (!text_end) 9648 text_end = FindRenderedTextEnd(text, text_end); 9649 9650 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); 9651 if (ref_pos) 9652 g.LogLinePosY = ref_pos->y; 9653 if (log_new_line) 9654 g.LogLineFirstItem = true; 9655 9656 const char* text_remaining = text; 9657 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth 9658 g.LogDepthRef = window->DC.TreeDepth; 9659 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); 9660 for (;;) 9661 { 9662 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. 9663 // We don't add a trailing \n to allow a subsequent item on the same line to be captured. 9664 const char* line_start = text_remaining; 9665 const char* line_end = ImStreolRange(line_start, text_end); 9666 const bool is_first_line = (line_start == text); 9667 const bool is_last_line = (line_end == text_end); 9668 if (!is_last_line || (line_start != line_end)) 9669 { 9670 const int char_count = (int)(line_end - line_start); 9671 if (log_new_line || !is_first_line) 9672 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); 9673 else if (g.LogLineFirstItem) 9674 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); 9675 else 9676 LogText(" %.*s", char_count, line_start); 9677 g.LogLineFirstItem = false; 9678 } 9679 else if (log_new_line) 9680 { 9681 // An empty "" string at a different Y position should output a carriage return. 9682 LogText(IM_NEWLINE); 9683 break; 9684 } 9685 9686 if (is_last_line) 9687 break; 9688 text_remaining = line_end + 1; 9689 } 9690 } 9691 9692 // Start logging/capturing text output 9693 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) 9694 { 9695 ImGuiContext& g = *GImGui; 9696 ImGuiWindow* window = g.CurrentWindow; 9697 IM_ASSERT(g.LogEnabled == false); 9698 IM_ASSERT(g.LogFile == NULL); 9699 IM_ASSERT(g.LogBuffer.empty()); 9700 g.LogEnabled = true; 9701 g.LogType = type; 9702 g.LogDepthRef = window->DC.TreeDepth; 9703 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); 9704 g.LogLinePosY = FLT_MAX; 9705 g.LogLineFirstItem = true; 9706 } 9707 9708 void ImGui::LogToTTY(int auto_open_depth) 9709 { 9710 ImGuiContext& g = *GImGui; 9711 if (g.LogEnabled) 9712 return; 9713 IM_UNUSED(auto_open_depth); 9714 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9715 LogBegin(ImGuiLogType_TTY, auto_open_depth); 9716 g.LogFile = stdout; 9717 #endif 9718 } 9719 9720 // Start logging/capturing text output to given file 9721 void ImGui::LogToFile(int auto_open_depth, const char* filename) 9722 { 9723 ImGuiContext& g = *GImGui; 9724 if (g.LogEnabled) 9725 return; 9726 9727 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still 9728 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. 9729 // By opening the file in binary mode "ab" we have consistent output everywhere. 9730 if (!filename) 9731 filename = g.IO.LogFilename; 9732 if (!filename || !filename[0]) 9733 return; 9734 ImFileHandle f = ImFileOpen(filename, "ab"); 9735 if (!f) 9736 { 9737 IM_ASSERT(0); 9738 return; 9739 } 9740 9741 LogBegin(ImGuiLogType_File, auto_open_depth); 9742 g.LogFile = f; 9743 } 9744 9745 // Start logging/capturing text output to clipboard 9746 void ImGui::LogToClipboard(int auto_open_depth) 9747 { 9748 ImGuiContext& g = *GImGui; 9749 if (g.LogEnabled) 9750 return; 9751 LogBegin(ImGuiLogType_Clipboard, auto_open_depth); 9752 } 9753 9754 void ImGui::LogToBuffer(int auto_open_depth) 9755 { 9756 ImGuiContext& g = *GImGui; 9757 if (g.LogEnabled) 9758 return; 9759 LogBegin(ImGuiLogType_Buffer, auto_open_depth); 9760 } 9761 9762 void ImGui::LogFinish() 9763 { 9764 ImGuiContext& g = *GImGui; 9765 if (!g.LogEnabled) 9766 return; 9767 9768 LogText(IM_NEWLINE); 9769 switch (g.LogType) 9770 { 9771 case ImGuiLogType_TTY: 9772 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9773 fflush(g.LogFile); 9774 #endif 9775 break; 9776 case ImGuiLogType_File: 9777 ImFileClose(g.LogFile); 9778 break; 9779 case ImGuiLogType_Buffer: 9780 break; 9781 case ImGuiLogType_Clipboard: 9782 if (!g.LogBuffer.empty()) 9783 SetClipboardText(g.LogBuffer.begin()); 9784 break; 9785 case ImGuiLogType_None: 9786 IM_ASSERT(0); 9787 break; 9788 } 9789 9790 g.LogEnabled = false; 9791 g.LogType = ImGuiLogType_None; 9792 g.LogFile = NULL; 9793 g.LogBuffer.clear(); 9794 } 9795 9796 // Helper to display logging buttons 9797 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) 9798 void ImGui::LogButtons() 9799 { 9800 ImGuiContext& g = *GImGui; 9801 9802 PushID("LogButtons"); 9803 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9804 const bool log_to_tty = Button("Log To TTY"); SameLine(); 13187 9805 #else 13188 #include <windows.h> 9806 const bool log_to_tty = false; 13189 9807 #endif 9808 const bool log_to_file = Button("Log To File"); SameLine(); 9809 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); 9810 PushAllowKeyboardFocus(false); 9811 SetNextItemWidth(80.0f); 9812 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); 9813 PopAllowKeyboardFocus(); 9814 PopID(); 9815 9816 // Start logging at the end of the function so that the buttons don't appear in the log 9817 if (log_to_tty) 9818 LogToTTY(); 9819 if (log_to_file) 9820 LogToFile(); 9821 if (log_to_clipboard) 9822 LogToClipboard(); 9823 } 9824 9825 9826 //----------------------------------------------------------------------------- 9827 // [SECTION] SETTINGS 9828 //----------------------------------------------------------------------------- 9829 // - UpdateSettings() [Internal] 9830 // - MarkIniSettingsDirty() [Internal] 9831 // - CreateNewWindowSettings() [Internal] 9832 // - FindWindowSettings() [Internal] 9833 // - FindOrCreateWindowSettings() [Internal] 9834 // - FindSettingsHandler() [Internal] 9835 // - ClearIniSettings() [Internal] 9836 // - LoadIniSettingsFromDisk() 9837 // - LoadIniSettingsFromMemory() 9838 // - SaveIniSettingsToDisk() 9839 // - SaveIniSettingsToMemory() 9840 // - WindowSettingsHandler_***() [Internal] 9841 //----------------------------------------------------------------------------- 9842 9843 // Called by NewFrame() 9844 void ImGui::UpdateSettings() 9845 { 9846 // Load settings on first frame (if not explicitly loaded manually before) 9847 ImGuiContext& g = *GImGui; 9848 if (!g.SettingsLoaded) 9849 { 9850 IM_ASSERT(g.SettingsWindows.empty()); 9851 if (g.IO.IniFilename) 9852 LoadIniSettingsFromDisk(g.IO.IniFilename); 9853 g.SettingsLoaded = true; 9854 } 9855 9856 // Save settings (with a delay after the last modification, so we don't spam disk too much) 9857 if (g.SettingsDirtyTimer > 0.0f) 9858 { 9859 g.SettingsDirtyTimer -= g.IO.DeltaTime; 9860 if (g.SettingsDirtyTimer <= 0.0f) 9861 { 9862 if (g.IO.IniFilename != NULL) 9863 SaveIniSettingsToDisk(g.IO.IniFilename); 9864 else 9865 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. 9866 g.SettingsDirtyTimer = 0.0f; 9867 } 9868 } 9869 } 9870 9871 void ImGui::MarkIniSettingsDirty() 9872 { 9873 ImGuiContext& g = *GImGui; 9874 if (g.SettingsDirtyTimer <= 0.0f) 9875 g.SettingsDirtyTimer = g.IO.IniSavingRate; 9876 } 9877 9878 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) 9879 { 9880 ImGuiContext& g = *GImGui; 9881 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) 9882 if (g.SettingsDirtyTimer <= 0.0f) 9883 g.SettingsDirtyTimer = g.IO.IniSavingRate; 9884 } 9885 9886 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) 9887 { 9888 ImGuiContext& g = *GImGui; 9889 9890 #if !IMGUI_DEBUG_INI_SETTINGS 9891 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() 9892 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. 9893 if (const char* p = strstr(name, "###")) 9894 name = p; 13190 9895 #endif 13191 13192 // Win32 API clipboard implementation 13193 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) 9896 const size_t name_len = strlen(name); 9897 9898 // Allocate chunk 9899 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1; 9900 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size); 9901 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings(); 9902 settings->ID = ImHashStr(name, name_len); 9903 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator 9904 9905 return settings; 9906 } 9907 9908 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) 9909 { 9910 ImGuiContext& g = *GImGui; 9911 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 9912 if (settings->ID == id) 9913 return settings; 9914 return NULL; 9915 } 9916 9917 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) 9918 { 9919 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name))) 9920 return settings; 9921 return CreateNewWindowSettings(name); 9922 } 9923 9924 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) 9925 { 9926 ImGuiContext& g = *GImGui; 9927 const ImGuiID type_hash = ImHashStr(type_name); 9928 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 9929 if (g.SettingsHandlers[handler_n].TypeHash == type_hash) 9930 return &g.SettingsHandlers[handler_n]; 9931 return NULL; 9932 } 9933 9934 void ImGui::ClearIniSettings() 9935 { 9936 ImGuiContext& g = *GImGui; 9937 g.SettingsIniData.clear(); 9938 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 9939 if (g.SettingsHandlers[handler_n].ClearAllFn) 9940 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]); 9941 } 9942 9943 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) 9944 { 9945 size_t file_data_size = 0; 9946 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); 9947 if (!file_data) 9948 return; 9949 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); 9950 IM_FREE(file_data); 9951 } 9952 9953 // Zero-tolerance, no error reporting, cheap .ini parsing 9954 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) 9955 { 9956 ImGuiContext& g = *GImGui; 9957 IM_ASSERT(g.Initialized); 9958 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()"); 9959 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); 9960 9961 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). 9962 // 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.. 9963 if (ini_size == 0) 9964 ini_size = strlen(ini_data); 9965 g.SettingsIniData.Buf.resize((int)ini_size + 1); 9966 char* const buf = g.SettingsIniData.Buf.Data; 9967 char* const buf_end = buf + ini_size; 9968 memcpy(buf, ini_data, ini_size); 9969 buf_end[0] = 0; 9970 9971 // Call pre-read handlers 9972 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) 9973 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 9974 if (g.SettingsHandlers[handler_n].ReadInitFn) 9975 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]); 9976 9977 void* entry_data = NULL; 9978 ImGuiSettingsHandler* entry_handler = NULL; 9979 9980 char* line_end = NULL; 9981 for (char* line = buf; line < buf_end; line = line_end + 1) 9982 { 9983 // Skip new lines markers, then find end of the line 9984 while (*line == '\n' || *line == '\r') 9985 line++; 9986 line_end = line; 9987 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') 9988 line_end++; 9989 line_end[0] = 0; 9990 if (line[0] == ';') 9991 continue; 9992 if (line[0] == '[' && line_end > line && line_end[-1] == ']') 9993 { 9994 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. 9995 line_end[-1] = 0; 9996 const char* name_end = line_end - 1; 9997 const char* type_start = line + 1; 9998 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']'); 9999 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; 10000 if (!type_end || !name_start) 10001 continue; 10002 *type_end = 0; // Overwrite first ']' 10003 name_start++; // Skip second '[' 10004 entry_handler = FindSettingsHandler(type_start); 10005 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; 10006 } 10007 else if (entry_handler != NULL && entry_data != NULL) 10008 { 10009 // Let type handler parse the line 10010 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); 10011 } 10012 } 10013 g.SettingsLoaded = true; 10014 10015 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary) 10016 memcpy(buf, ini_data, ini_size); 10017 10018 // Call post-read handlers 10019 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10020 if (g.SettingsHandlers[handler_n].ApplyAllFn) 10021 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]); 10022 } 10023 10024 void ImGui::SaveIniSettingsToDisk(const char* ini_filename) 10025 { 10026 ImGuiContext& g = *GImGui; 10027 g.SettingsDirtyTimer = 0.0f; 10028 if (!ini_filename) 10029 return; 10030 10031 size_t ini_data_size = 0; 10032 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); 10033 ImFileHandle f = ImFileOpen(ini_filename, "wt"); 10034 if (!f) 10035 return; 10036 ImFileWrite(ini_data, sizeof(char), ini_data_size, f); 10037 ImFileClose(f); 10038 } 10039 10040 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer 10041 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) 10042 { 10043 ImGuiContext& g = *GImGui; 10044 g.SettingsDirtyTimer = 0.0f; 10045 g.SettingsIniData.Buf.resize(0); 10046 g.SettingsIniData.Buf.push_back(0); 10047 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10048 { 10049 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; 10050 handler->WriteAllFn(&g, handler, &g.SettingsIniData); 10051 } 10052 if (out_size) 10053 *out_size = (size_t)g.SettingsIniData.size(); 10054 return g.SettingsIniData.c_str(); 10055 } 10056 10057 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) 10058 { 10059 ImGuiContext& g = *ctx; 10060 for (int i = 0; i != g.Windows.Size; i++) 10061 g.Windows[i]->SettingsOffset = -1; 10062 g.SettingsWindows.clear(); 10063 } 10064 10065 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) 10066 { 10067 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name); 10068 ImGuiID id = settings->ID; 10069 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry 10070 settings->ID = id; 10071 settings->WantApply = true; 10072 return (void*)settings; 10073 } 10074 10075 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) 10076 { 10077 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; 10078 int x, y; 10079 int i; 10080 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } 10081 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } 10082 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } 10083 } 10084 10085 // Apply to existing windows (if any) 10086 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) 10087 { 10088 ImGuiContext& g = *ctx; 10089 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10090 if (settings->WantApply) 10091 { 10092 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID)) 10093 ApplyWindowSettings(window, settings); 10094 settings->WantApply = false; 10095 } 10096 } 10097 10098 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) 10099 { 10100 // Gather data from windows that were active during this session 10101 // (if a window wasn't opened in this session we preserve its settings) 10102 ImGuiContext& g = *ctx; 10103 for (int i = 0; i != g.Windows.Size; i++) 10104 { 10105 ImGuiWindow* window = g.Windows[i]; 10106 if (window->Flags & ImGuiWindowFlags_NoSavedSettings) 10107 continue; 10108 10109 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID); 10110 if (!settings) 10111 { 10112 settings = ImGui::CreateNewWindowSettings(window->Name); 10113 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 10114 } 10115 IM_ASSERT(settings->ID == window->ID); 10116 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); 10117 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); 10118 settings->Collapsed = window->Collapsed; 10119 } 10120 10121 // Write to text buffer 10122 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve 10123 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10124 { 10125 const char* settings_name = settings->GetName(); 10126 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); 10127 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); 10128 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); 10129 buf->appendf("Collapsed=%d\n", settings->Collapsed); 10130 buf->append("\n"); 10131 } 10132 } 10133 10134 10135 //----------------------------------------------------------------------------- 10136 // [SECTION] VIEWPORTS, PLATFORM WINDOWS 10137 //----------------------------------------------------------------------------- 10138 10139 // (this section is filled in the 'docking' branch) 10140 10141 10142 //----------------------------------------------------------------------------- 10143 // [SECTION] DOCKING 10144 //----------------------------------------------------------------------------- 10145 10146 // (this section is filled in the 'docking' branch) 10147 10148 10149 //----------------------------------------------------------------------------- 10150 // [SECTION] PLATFORM DEPENDENT HELPERS 10151 //----------------------------------------------------------------------------- 10152 10153 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) 13194 10154 13195 10155 #ifdef _MSC_VER 13196 10156 #pragma comment(lib, "user32") 10157 #pragma comment(lib, "kernel32") 13197 10158 #endif 13198 10159 10160 // Win32 clipboard implementation 10161 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() 13199 10162 static const char* GetClipboardTextFn_DefaultImpl(void*) 13200 10163 { 13201 static ImVector<char> buf_local;13202 buf_local.clear();13203 if (!OpenClipboard(NULL))13204 return NULL;13205 HANDLE wbuf_handle =GetClipboardData(CF_UNICODETEXT);13206 if (wbuf_handle == NULL)13207 {13208 CloseClipboard();13209 return NULL;13210 }13211 if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle))13212 {13213 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;13214 buf_local.resize(buf_len);13215 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);13216 }13217 GlobalUnlock(wbuf_handle);13218 CloseClipboard();13219 return buf_local.Data;10164 ImGuiContext& g = *GImGui; 10165 g.ClipboardHandlerData.clear(); 10166 if (!::OpenClipboard(NULL)) 10167 return NULL; 10168 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); 10169 if (wbuf_handle == NULL) 10170 { 10171 ::CloseClipboard(); 10172 return NULL; 10173 } 10174 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) 10175 { 10176 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); 10177 g.ClipboardHandlerData.resize(buf_len); 10178 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); 10179 } 10180 ::GlobalUnlock(wbuf_handle); 10181 ::CloseClipboard(); 10182 return g.ClipboardHandlerData.Data; 13220 10183 } 13221 10184 13222 10185 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 13223 10186 { 13224 if (!OpenClipboard(NULL)) 13225 return; 13226 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; 13227 HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); 13228 if (wbuf_handle == NULL) 13229 { 13230 CloseClipboard(); 13231 return; 13232 } 13233 ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); 13234 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); 13235 GlobalUnlock(wbuf_handle); 13236 EmptyClipboard(); 13237 SetClipboardData(CF_UNICODETEXT, wbuf_handle); 13238 CloseClipboard(); 10187 if (!::OpenClipboard(NULL)) 10188 return; 10189 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); 10190 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); 10191 if (wbuf_handle == NULL) 10192 { 10193 ::CloseClipboard(); 10194 return; 10195 } 10196 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); 10197 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); 10198 ::GlobalUnlock(wbuf_handle); 10199 ::EmptyClipboard(); 10200 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) 10201 ::GlobalFree(wbuf_handle); 10202 ::CloseClipboard(); 10203 } 10204 10205 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) 10206 10207 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file 10208 static PasteboardRef main_clipboard = 0; 10209 10210 // OSX clipboard implementation 10211 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! 10212 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 10213 { 10214 if (!main_clipboard) 10215 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 10216 PasteboardClear(main_clipboard); 10217 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); 10218 if (cf_data) 10219 { 10220 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); 10221 CFRelease(cf_data); 10222 } 10223 } 10224 10225 static const char* GetClipboardTextFn_DefaultImpl(void*) 10226 { 10227 if (!main_clipboard) 10228 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 10229 PasteboardSynchronize(main_clipboard); 10230 10231 ItemCount item_count = 0; 10232 PasteboardGetItemCount(main_clipboard, &item_count); 10233 for (ItemCount i = 0; i < item_count; i++) 10234 { 10235 PasteboardItemID item_id = 0; 10236 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); 10237 CFArrayRef flavor_type_array = 0; 10238 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); 10239 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) 10240 { 10241 CFDataRef cf_data; 10242 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) 10243 { 10244 ImGuiContext& g = *GImGui; 10245 g.ClipboardHandlerData.clear(); 10246 int length = (int)CFDataGetLength(cf_data); 10247 g.ClipboardHandlerData.resize(length + 1); 10248 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); 10249 g.ClipboardHandlerData[length] = 0; 10250 CFRelease(cf_data); 10251 return g.ClipboardHandlerData.Data; 10252 } 10253 } 10254 } 10255 return NULL; 13239 10256 } 13240 10257 13241 10258 #else 13242 10259 13243 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers10260 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. 13244 10261 static const char* GetClipboardTextFn_DefaultImpl(void*) 13245 10262 { 13246 ImGuiContext& g = *GImGui; 13247 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin(); 13248 } 13249 13250 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers 10263 ImGuiContext& g = *GImGui; 10264 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); 10265 } 10266 13251 10267 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 13252 10268 { 13253 ImGuiContext& g = *GImGui;13254 g.PrivateClipboard.clear();13255 const char* text_end = text + strlen(text);13256 g.PrivateClipboard.resize((int)(text_end - text) + 1);13257 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));13258 g.PrivateClipboard[(int)(text_end - text)] = 0;10269 ImGuiContext& g = *GImGui; 10270 g.ClipboardHandlerData.clear(); 10271 const char* text_end = text + strlen(text); 10272 g.ClipboardHandlerData.resize((int)(text_end - text) + 1); 10273 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); 10274 g.ClipboardHandlerData[(int)(text_end - text)] = 0; 13259 10275 } 13260 10276 … … 13262 10278 13263 10279 // Win32 API IME support (for Asian languages, etc.) 13264 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_ DEFAULT_IME_FUNCTIONS)10280 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) 13265 10281 13266 10282 #include <imm.h> … … 13271 10287 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) 13272 10288 { 13273 // Notify OS Input Method Editor of text input position 13274 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) 13275 if (HIMC himc = ImmGetContext(hwnd)) 13276 { 13277 COMPOSITIONFORM cf; 13278 cf.ptCurrentPos.x = x; 13279 cf.ptCurrentPos.y = y; 13280 cf.dwStyle = CFS_FORCE_POSITION; 13281 ImmSetCompositionWindow(himc, &cf); 13282 } 10289 // Notify OS Input Method Editor of text input position 10290 ImGuiIO& io = ImGui::GetIO(); 10291 if (HWND hwnd = (HWND)io.ImeWindowHandle) 10292 if (HIMC himc = ::ImmGetContext(hwnd)) 10293 { 10294 COMPOSITIONFORM cf; 10295 cf.ptCurrentPos.x = x; 10296 cf.ptCurrentPos.y = y; 10297 cf.dwStyle = CFS_FORCE_POSITION; 10298 ::ImmSetCompositionWindow(himc, &cf); 10299 ::ImmReleaseContext(hwnd, himc); 10300 } 13283 10301 } 13284 10302 … … 13290 10308 13291 10309 //----------------------------------------------------------------------------- 13292 // HELP, METRICS10310 // [SECTION] METRICS/DEBUG WINDOW 13293 10311 //----------------------------------------------------------------------------- 13294 10312 10313 #ifndef IMGUI_DISABLE_METRICS_WINDOW 10314 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. 10315 static void MetricsHelpMarker(const char* desc) 10316 { 10317 ImGui::TextDisabled("(?)"); 10318 if (ImGui::IsItemHovered()) 10319 { 10320 ImGui::BeginTooltip(); 10321 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 10322 ImGui::TextUnformatted(desc); 10323 ImGui::PopTextWrapPos(); 10324 ImGui::EndTooltip(); 10325 } 10326 } 10327 13295 10328 void ImGui::ShowMetricsWindow(bool* p_open) 13296 10329 { 13297 if (ImGui::Begin("ImGui Metrics", p_open)) 13298 { 13299 ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); 13300 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); 13301 ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); 13302 ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount); 13303 static bool show_clip_rects = true; 13304 ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects); 13305 ImGui::Separator(); 13306 13307 struct Funcs 13308 { 13309 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) 13310 { 10330 if (!ImGui::Begin("Dear ImGui Metrics", p_open)) 10331 { 10332 ImGui::End(); 10333 return; 10334 } 10335 10336 // Debugging enums 10337 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type 10338 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; 10339 enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type 10340 const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" }; 10341 10342 // State 10343 static bool show_windows_rects = false; 10344 static int show_windows_rect_type = WRT_WorkRect; 10345 static bool show_windows_begin_order = false; 10346 static bool show_tables_rects = false; 10347 static int show_tables_rect_type = TRT_WorkRect; 10348 static bool show_drawcmd_mesh = true; 10349 static bool show_drawcmd_aabb = true; 10350 10351 // Basic info 10352 ImGuiContext& g = *GImGui; 10353 ImGuiIO& io = ImGui::GetIO(); 10354 ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); 10355 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); 10356 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); 10357 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); 10358 ImGui::Text("%d active allocations", io.MetricsActiveAllocations); 10359 ImGui::Separator(); 10360 10361 // Helper functions to display common structures: 10362 // - NodeDrawList() 10363 // - NodeColumns() 10364 // - NodeWindow() 10365 // - NodeWindows() 10366 // - NodeTabBar() 10367 // - NodeStorage() 10368 struct Funcs 10369 { 10370 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) 10371 { 10372 if (rect_type == WRT_OuterRect) { return window->Rect(); } 10373 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } 10374 else if (rect_type == WRT_InnerRect) { return window->InnerRect; } 10375 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } 10376 else if (rect_type == WRT_WorkRect) { return window->WorkRect; } 10377 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } 10378 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } 10379 IM_ASSERT(0); 10380 return ImRect(); 10381 } 10382 10383 static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) 10384 { 10385 IM_ASSERT(show_mesh || show_aabb); 10386 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list 10387 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 10388 10389 // Draw wire-frame version of all triangles 10390 ImRect clip_rect = draw_cmd->ClipRect; 10391 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); 10392 ImDrawListFlags backup_flags = fg_draw_list->Flags; 10393 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 10394 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + draw_cmd->ElemCount); base_idx += 3) 10395 { 10396 ImVec2 triangle[3]; 10397 for (int n = 0; n < 3; n++) 10398 { 10399 ImVec2 p = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos; 10400 triangle[n] = p; 10401 vtxs_rect.Add(p); 10402 } 10403 if (show_mesh) 10404 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles 10405 } 10406 // Draw bounding boxes 10407 if (show_aabb) 10408 { 10409 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU 10410 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles 10411 } 10412 fg_draw_list->Flags = backup_flags; 10413 } 10414 10415 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) 10416 { 13311 10417 bool node_open = ImGui::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, draw_list->CmdBuffer.Size); 13312 10418 if (draw_list == ImGui::GetWindowDrawList()) 13313 10419 { 13314 ImGui::SameLine();13315 ImGui::TextColored(ImColor(255, 100, 100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)13316 if (node_open) ImGui::TreePop();13317 return;10420 ImGui::SameLine(); 10421 ImGui::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) 10422 if (node_open) ImGui::TreePop(); 10423 return; 13318 10424 } 13319 10425 13320 ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list10426 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list 13321 10427 if (window && IsItemHovered()) 13322 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));10428 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 13323 10429 if (!node_open) 13324 return; 13325 13326 int elem_offset = 0; 10430 return; 10431 10432 if (window && !window->WasActive) 10433 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!"); 10434 10435 unsigned int elem_offset = 0; 13327 10436 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) 13328 10437 { 13329 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) 13330 continue; 13331 if (pcmd->UserCallback) 13332 { 13333 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); 13334 continue; 13335 } 13336 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 13337 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); 13338 if (show_clip_rects && ImGui::IsItemHovered()) 13339 { 13340 ImRect clip_rect = pcmd->ClipRect; 13341 ImRect vtxs_rect; 13342 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) 13343 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); 13344 clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255, 255, 0, 255)); 13345 vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255, 0, 255, 255)); 13346 } 13347 if (!pcmd_node_open) 13348 continue; 13349 13350 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. 13351 ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. 13352 while (clipper.Step()) 13353 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) 13354 { 13355 char buf[300]; 13356 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); 13357 ImVec2 triangles_pos[3]; 13358 for (int n = 0; n < 3; n++, vtx_i++) 13359 { 13360 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; 13361 triangles_pos[n] = v.pos; 13362 buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); 13363 } 13364 ImGui::Selectable(buf, false); 13365 if (ImGui::IsItemHovered()) 13366 { 13367 ImDrawListFlags backup_flags = overlay_draw_list->Flags; 13368 overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. 13369 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); 13370 overlay_draw_list->Flags = backup_flags; 13371 } 13372 } 13373 ImGui::TreePop(); 10438 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) 10439 continue; 10440 if (pcmd->UserCallback) 10441 { 10442 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); 10443 continue; 10444 } 10445 10446 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 10447 char buf[300]; 10448 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", 10449 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId, 10450 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); 10451 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); 10452 if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list) 10453 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); 10454 if (!pcmd_node_open) 10455 continue; 10456 10457 // Calculate approximate coverage area (touched pixel count) 10458 // This will be in pixels squared as long there's no post-scaling happening to the renderer output. 10459 float total_area = 0.0f; 10460 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3) 10461 { 10462 ImVec2 triangle[3]; 10463 for (int n = 0; n < 3; n++) 10464 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos; 10465 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]); 10466 } 10467 10468 // Display vertex information summary. Hover to get all triangles drawn in wire-frame 10469 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); 10470 ImGui::Selectable(buf); 10471 if (ImGui::IsItemHovered() && fg_draw_list) 10472 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false); 10473 10474 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. 10475 ImGuiListClipper clipper; 10476 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. 10477 while (clipper.Step()) 10478 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) 10479 { 10480 char* buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); 10481 ImVec2 triangle[3]; 10482 for (int n = 0; n < 3; n++, idx_i++) 10483 { 10484 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i]; 10485 triangle[n] = v.pos; 10486 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", 10487 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); 10488 } 10489 10490 ImGui::Selectable(buf, false); 10491 if (fg_draw_list && ImGui::IsItemHovered()) 10492 { 10493 ImDrawListFlags backup_flags = fg_draw_list->Flags; 10494 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 10495 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f); 10496 fg_draw_list->Flags = backup_flags; 10497 } 10498 } 10499 ImGui::TreePop(); 13374 10500 } 13375 10501 ImGui::TreePop(); 13376 } 13377 13378 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label) 13379 { 10502 } 10503 10504 static void NodeColumns(const ImGuiColumns* columns) 10505 { 10506 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) 10507 return; 10508 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); 10509 for (int column_n = 0; column_n < columns->Columns.Size; column_n++) 10510 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm)); 10511 ImGui::TreePop(); 10512 } 10513 10514 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label) 10515 { 13380 10516 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) 13381 return; 13382 for (int i = 0; i < windows.Size; i++) 13383 Funcs::NodeWindow(windows[i], "Window"); 10517 return; 10518 ImGui::Text("(In front-to-back order:)"); 10519 for (int i = windows.Size - 1; i >= 0; i--) // Iterate front to back 10520 { 10521 ImGui::PushID(windows[i]); 10522 Funcs::NodeWindow(windows[i], "Window"); 10523 ImGui::PopID(); 10524 } 13384 10525 ImGui::TreePop(); 13385 } 13386 13387 static void NodeWindow(ImGuiWindow* window, const char* label) 13388 { 13389 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) 13390 return; 10526 } 10527 10528 static void NodeWindow(ImGuiWindow* window, const char* label) 10529 { 10530 if (window == NULL) 10531 { 10532 ImGui::BulletText("%s: NULL", label); 10533 return; 10534 } 10535 10536 ImGuiContext& g = *GImGui; 10537 const bool is_active = window->WasActive; 10538 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; 10539 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } 10540 const bool open = ImGui::TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*"); 10541 if (!is_active) { PopStyleColor(); } 10542 if (ImGui::IsItemHovered() && is_active) 10543 ImGui::GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 10544 if (!open) 10545 return; 10546 10547 if (window->MemoryCompacted) 10548 ImGui::TextDisabled("Note: some memory buffers have been compacted/freed."); 10549 13391 10550 ImGuiWindowFlags flags = window->Flags; 13392 10551 NodeDrawList(window, window->DrawList, "DrawList"); 13393 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); 13394 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, 13395 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", 13396 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); 13397 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); 13398 ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); 10552 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y); 10553 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, 10554 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", 10555 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", 10556 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); 10557 ImGui::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" : ""); 10558 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); 10559 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); 13399 10560 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); 13400 10561 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); 13401 10562 if (!window->NavRectRel[0].IsInverted()) 13402 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);10563 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); 13403 10564 else 13404 ImGui::BulletText("NavRectRel[0]: <None>");10565 ImGui::BulletText("NavRectRel[0]: <None>"); 13405 10566 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); 13406 10567 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); … … 13408 10569 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) 13409 10570 { 13410 for (int n = 0; n < window->ColumnsStorage.Size; n++) 13411 { 13412 const ImGuiColumnsSet* columns = &window->ColumnsStorage[n]; 13413 if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) 13414 { 13415 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX); 13416 for (int column_n = 0; column_n < columns->Columns.Size; column_n++) 13417 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); 13418 ImGui::TreePop(); 13419 } 13420 } 13421 ImGui::TreePop(); 10571 for (int n = 0; n < window->ColumnsStorage.Size; n++) 10572 NodeColumns(&window->ColumnsStorage[n]); 10573 ImGui::TreePop(); 13422 10574 } 13423 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));10575 NodeStorage(&window->StateStorage, "Storage"); 13424 10576 ImGui::TreePop(); 13425 } 13426 }; 13427 13428 // Access private state, we are going to display the draw lists from last frame 13429 ImGuiContext& g = *GImGui; 13430 Funcs::NodeWindows(g.Windows, "Windows"); 13431 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) 13432 { 13433 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) 10577 } 10578 10579 static void NodeWindowSettings(ImGuiWindowSettings* settings) 10580 { 10581 ImGui::Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", 10582 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); 10583 } 10584 10585 static void NodeTabBar(ImGuiTabBar* tab_bar) 10586 { 10587 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. 10588 char buf[256]; 10589 char* p = buf; 10590 const char* buf_end = buf + IM_ARRAYSIZE(buf); 10591 const bool is_active = (tab_bar->PrevFrameVisible >= ImGui::GetFrameCount() - 2); 10592 p += ImFormatString(p, buf_end - p, "Tab Bar 0x%08X (%d tabs)%s", tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); 10593 IM_UNUSED(p); 10594 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } 10595 bool open = ImGui::TreeNode(tab_bar, "%s", buf); 10596 if (!is_active) { PopStyleColor(); } 10597 if (is_active && ImGui::IsItemHovered()) 10598 { 10599 ImDrawList* draw_list = ImGui::GetForegroundDrawList(); 10600 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); 10601 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)); 10602 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)); 10603 } 10604 if (open) 10605 { 10606 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) 10607 { 10608 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; 10609 ImGui::PushID(tab); 10610 if (ImGui::SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); 10611 if (ImGui::SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } ImGui::SameLine(); 10612 ImGui::Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); 10613 ImGui::PopID(); 10614 } 10615 ImGui::TreePop(); 10616 } 10617 } 10618 10619 static void NodeStorage(ImGuiStorage* storage, const char* label) 10620 { 10621 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) 10622 return; 10623 for (int n = 0; n < storage->Data.Size; n++) 10624 { 10625 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n]; 10626 ImGui::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. 10627 } 10628 ImGui::TreePop(); 10629 } 10630 }; 10631 10632 // Tools 10633 if (ImGui::TreeNode("Tools")) 10634 { 10635 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. 10636 if (ImGui::Button("Item Picker..")) 10637 ImGui::DebugStartItemPicker(); 10638 ImGui::SameLine(); 10639 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."); 10640 10641 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order); 10642 ImGui::Checkbox("Show windows rectangles", &show_windows_rects); 10643 ImGui::SameLine(); 10644 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12); 10645 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count); 10646 if (show_windows_rects && g.NavWindow) 10647 { 10648 ImGui::BulletText("'%s':", g.NavWindow->Name); 10649 ImGui::Indent(); 10650 for (int rect_n = 0; rect_n < WRT_Count; rect_n++) 10651 { 10652 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); 10653 ImGui::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]); 10654 } 10655 ImGui::Unindent(); 10656 } 10657 ImGui::Checkbox("Show mesh when hovering ImDrawCmd", &show_drawcmd_mesh); 10658 ImGui::Checkbox("Show bounding boxes when hovering ImDrawCmd", &show_drawcmd_aabb); 10659 ImGui::TreePop(); 10660 } 10661 10662 // Contents 10663 Funcs::NodeWindows(g.Windows, "Windows"); 10664 //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder"); 10665 if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) 10666 { 10667 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) 13434 10668 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); 13435 ImGui::TreePop(); 13436 } 13437 if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) 13438 { 13439 for (int i = 0; i < g.OpenPopupStack.Size; i++) 13440 { 10669 ImGui::TreePop(); 10670 } 10671 10672 // Details for Popups 10673 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) 10674 { 10675 for (int i = 0; i < g.OpenPopupStack.Size; i++) 10676 { 13441 10677 ImGuiWindow* window = g.OpenPopupStack[i].Window; 13442 10678 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); 13443 } 13444 ImGui::TreePop(); 13445 } 13446 if (ImGui::TreeNode("Internal state")) 13447 { 13448 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); 13449 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); 13450 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); 13451 ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not 13452 ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]); 13453 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); 13454 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); 13455 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); 13456 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); 13457 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); 13458 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); 13459 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); 13460 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); 13461 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); 13462 ImGui::TreePop(); 13463 } 13464 } 13465 ImGui::End(); 13466 } 10679 } 10680 ImGui::TreePop(); 10681 } 10682 10683 // Details for TabBars 10684 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) 10685 { 10686 for (int n = 0; n < g.TabBars.GetSize(); n++) 10687 Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); 10688 ImGui::TreePop(); 10689 } 10690 10691 // Details for Tables 10692 IM_UNUSED(trt_rects_names); 10693 IM_UNUSED(show_tables_rects); 10694 IM_UNUSED(show_tables_rect_type); 10695 #ifdef IMGUI_HAS_TABLE 10696 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) 10697 { 10698 for (int n = 0; n < g.Tables.GetSize(); n++) 10699 Funcs::NodeTable(g.Tables.GetByIndex(n)); 10700 ImGui::TreePop(); 10701 } 10702 #endif // #ifdef IMGUI_HAS_TABLE 10703 10704 // Details for Docking 10705 #ifdef IMGUI_HAS_DOCK 10706 if (ImGui::TreeNode("Dock nodes")) 10707 { 10708 ImGui::TreePop(); 10709 } 10710 #endif // #ifdef IMGUI_HAS_DOCK 10711 10712 // Settings 10713 if (ImGui::TreeNode("Settings")) 10714 { 10715 if (ImGui::SmallButton("Clear")) 10716 ImGui::ClearIniSettings(); 10717 ImGui::SameLine(); 10718 if (ImGui::SmallButton("Save to memory")) 10719 ImGui::SaveIniSettingsToMemory(); 10720 ImGui::SameLine(); 10721 if (ImGui::SmallButton("Save to disk")) 10722 ImGui::SaveIniSettingsToDisk(g.IO.IniFilename); 10723 ImGui::SameLine(); 10724 if (g.IO.IniFilename) 10725 ImGui::Text("\"%s\"", g.IO.IniFilename); 10726 else 10727 ImGui::TextUnformatted("<NULL>"); 10728 ImGui::Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); 10729 if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) 10730 { 10731 for (int n = 0; n < g.SettingsHandlers.Size; n++) 10732 ImGui::BulletText("%s", g.SettingsHandlers[n].TypeName); 10733 ImGui::TreePop(); 10734 } 10735 if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) 10736 { 10737 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10738 Funcs::NodeWindowSettings(settings); 10739 ImGui::TreePop(); 10740 } 10741 10742 #ifdef IMGUI_HAS_TABLE 10743 if (ImGui::TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) 10744 { 10745 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) 10746 Funcs::NodeTableSettings(settings); 10747 ImGui::TreePop(); 10748 } 10749 #endif // #ifdef IMGUI_HAS_TABLE 10750 10751 #ifdef IMGUI_HAS_DOCK 10752 #endif // #ifdef IMGUI_HAS_DOCK 10753 10754 if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) 10755 { 10756 ImGui::InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, 0.0f), ImGuiInputTextFlags_ReadOnly); 10757 ImGui::TreePop(); 10758 } 10759 ImGui::TreePop(); 10760 } 10761 10762 // Misc Details 10763 if (ImGui::TreeNode("Internal state")) 10764 { 10765 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); 10766 10767 ImGui::Text("WINDOWING"); 10768 ImGui::Indent(); 10769 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); 10770 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); 10771 ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); 10772 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); 10773 ImGui::Unindent(); 10774 10775 ImGui::Text("ITEMS"); 10776 ImGui::Indent(); 10777 ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); 10778 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); 10779 ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not 10780 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); 10781 ImGui::Unindent(); 10782 10783 ImGui::Text("NAV,FOCUS"); 10784 ImGui::Indent(); 10785 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); 10786 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); 10787 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); 10788 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); 10789 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); 10790 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); 10791 ImGui::Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); 10792 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); 10793 ImGui::Unindent(); 10794 10795 ImGui::TreePop(); 10796 } 10797 10798 // Overlay: Display windows Rectangles and Begin Order 10799 if (show_windows_rects || show_windows_begin_order) 10800 { 10801 for (int n = 0; n < g.Windows.Size; n++) 10802 { 10803 ImGuiWindow* window = g.Windows[n]; 10804 if (!window->WasActive) 10805 continue; 10806 ImDrawList* draw_list = GetForegroundDrawList(window); 10807 if (show_windows_rects) 10808 { 10809 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type); 10810 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); 10811 } 10812 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow)) 10813 { 10814 char buf[32]; 10815 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); 10816 float font_size = ImGui::GetFontSize(); 10817 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); 10818 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); 10819 } 10820 } 10821 } 10822 10823 #ifdef IMGUI_HAS_TABLE 10824 // Overlay: Display Tables Rectangles 10825 if (show_tables_rects) 10826 { 10827 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) 10828 { 10829 ImGuiTable* table = g.Tables.GetByIndex(table_n); 10830 } 10831 } 10832 #endif // #ifdef IMGUI_HAS_TABLE 10833 10834 #ifdef IMGUI_HAS_DOCK 10835 // Overlay: Display Docking info 10836 if (show_docking_nodes && g.IO.KeyCtrl) 10837 { 10838 } 10839 #endif // #ifdef IMGUI_HAS_DOCK 10840 10841 ImGui::End(); 10842 } 10843 10844 #else 10845 10846 void ImGui::ShowMetricsWindow(bool*) { } 10847 10848 #endif 13467 10849 13468 10850 //----------------------------------------------------------------------------- … … 13475 10857 13476 10858 //----------------------------------------------------------------------------- 10859 10860 #endif // #ifndef IMGUI_DISABLE
Note:
See TracChangeset
for help on using the changeset viewer.